From d534a7f9fc31d649af5ad955230b77800a0f02fd Mon Sep 17 00:00:00 2001 From: hobbes1069 Date: Wed, 19 Jul 2017 13:08:47 +0000 Subject: [PATCH] Nuke bad freedv 1.2 branch and 1.2.2 tag and start over. git-svn-id: https://svn.code.sf.net/p/freetel/code@3313 01035d8c-6547-0410-b346-abe4f91aad63 --- freedv/branches/1.2/.clang/.gitignore | 0 freedv/branches/1.2/CMakeLists.txt | 463 -- freedv/branches/1.2/COPYING | 502 -- freedv/branches/1.2/README.osx | 107 - freedv/branches/1.2/README.txt | 233 - freedv/branches/1.2/RELEASE_NOTES.txt | 7 - freedv/branches/1.2/USER_MANUAL.txt | 100 - freedv/branches/1.2/cmake/BuildCodec2.cmake | 25 - freedv/branches/1.2/cmake/BuildHamlib.cmake | 20 - .../branches/1.2/cmake/BuildPortaudio.cmake | 52 - .../branches/1.2/cmake/BuildSamplerate.cmake | 27 - freedv/branches/1.2/cmake/BuildSndfile.cmake | 26 - freedv/branches/1.2/cmake/BuildSpeex.cmake | 26 - .../branches/1.2/cmake/BuildWxWidgets.cmake | 43 - freedv/branches/1.2/cmake/FindPortaudio.cmake | 107 - .../1.2/cmake/GetDependencies.cmake.in | 37 - freedv/branches/1.2/cmake/MinGW.cmake | 8 - .../1.2/cmake/Toolchain-Ubuntu-mingw32.cmake | 25 - freedv/branches/1.2/cmake/config.h.in | 19 - freedv/branches/1.2/cmake/soxconfig.h.in | 16 - freedv/branches/1.2/cmake/version.h.in | 11 - freedv/branches/1.2/contrib/CMakeLists.txt | 22 - freedv/branches/1.2/contrib/LICENSE | 393 -- freedv/branches/1.2/contrib/freedv.desktop | 8 - freedv/branches/1.2/contrib/freedv.ico | Bin 364646 -> 0 bytes freedv/branches/1.2/contrib/freedv.rc | 1 - freedv/branches/1.2/contrib/freedv128x128.png | Bin 22063 -> 0 bytes freedv/branches/1.2/contrib/freedv256x256.png | Bin 72148 -> 0 bytes freedv/branches/1.2/contrib/freedv48x48.png | Bin 3787 -> 0 bytes freedv/branches/1.2/contrib/freedv64x64.png | Bin 6289 -> 0 bytes .../1.2/contrib/freedv_screenshot.png | Bin 81690 -> 0 bytes freedv/branches/1.2/credits.txt | 13 - freedv/branches/1.2/db/current | 1 - freedv/branches/1.2/db/format | 2 - freedv/branches/1.2/db/fs-type | 1 - freedv/branches/1.2/db/fsfs.conf | 38 - freedv/branches/1.2/db/min-unpacked-rev | 1 - freedv/branches/1.2/db/rep-cache.db | Bin 4096 -> 0 bytes freedv/branches/1.2/db/revprops/0/0 | 5 - freedv/branches/1.2/db/revprops/0/1 | 13 - freedv/branches/1.2/db/revs/0/0 | 11 - freedv/branches/1.2/db/revs/0/1 | 49 - .../branches/1.2/db/transactions/.gitignore | 0 freedv/branches/1.2/db/txn-current | 1 - freedv/branches/1.2/db/txn-current-lock | 0 .../branches/1.2/db/txn-protorevs/.gitignore | 0 freedv/branches/1.2/db/uuid | 1 - freedv/branches/1.2/db/write-lock | 0 freedv/branches/1.2/debian/changelog | 5 - freedv/branches/1.2/debian/compat | 1 - freedv/branches/1.2/debian/control | 19 - freedv/branches/1.2/debian/copyright | 38 - freedv/branches/1.2/debian/docs | 3 - freedv/branches/1.2/debian/format | 1 - freedv/branches/1.2/debian/rules | 30 - .../branches/1.2/freedv-dev/.clang/.gitignore | 0 freedv/branches/1.2/freedv-dev/CMakeLists.txt | 463 -- freedv/branches/1.2/freedv-dev/COPYING | 502 -- freedv/branches/1.2/freedv-dev/README.osx | 107 - freedv/branches/1.2/freedv-dev/README.txt | 233 - .../branches/1.2/freedv-dev/RELEASE_NOTES.txt | 7 - .../branches/1.2/freedv-dev/USER_MANUAL.txt | 100 - .../1.2/freedv-dev/cmake/BuildCodec2.cmake | 26 - .../1.2/freedv-dev/cmake/BuildHamlib.cmake | 20 - .../1.2/freedv-dev/cmake/BuildPortaudio.cmake | 52 - .../freedv-dev/cmake/BuildSamplerate.cmake | 27 - .../1.2/freedv-dev/cmake/BuildSndfile.cmake | 26 - .../1.2/freedv-dev/cmake/BuildSpeex.cmake | 30 - .../1.2/freedv-dev/cmake/BuildWxWidgets.cmake | 43 - .../1.2/freedv-dev/cmake/FindPortaudio.cmake | 107 - .../freedv-dev/cmake/GetDependencies.cmake.in | 37 - .../branches/1.2/freedv-dev/cmake/MinGW.cmake | 8 - .../cmake/Toolchain-Ubuntu-mingw32.cmake | 25 - .../branches/1.2/freedv-dev/cmake/config.h.in | 19 - .../1.2/freedv-dev/cmake/soxconfig.h.in | 16 - .../1.2/freedv-dev/cmake/version.h.in | 11 - .../1.2/freedv-dev/contrib/CMakeLists.txt | 22 - .../branches/1.2/freedv-dev/contrib/LICENSE | 393 -- .../1.2/freedv-dev/contrib/freedv.desktop | 8 - .../1.2/freedv-dev/contrib/freedv.ico | Bin 364646 -> 0 bytes .../branches/1.2/freedv-dev/contrib/freedv.rc | 1 - .../1.2/freedv-dev/contrib/freedv128x128.png | Bin 22063 -> 0 bytes .../1.2/freedv-dev/contrib/freedv256x256.png | Bin 72148 -> 0 bytes .../1.2/freedv-dev/contrib/freedv48x48.png | Bin 3787 -> 0 bytes .../1.2/freedv-dev/contrib/freedv64x64.png | Bin 6289 -> 0 bytes .../freedv-dev/contrib/freedv_screenshot.png | Bin 81690 -> 0 bytes freedv/branches/1.2/freedv-dev/credits.txt | 13 - freedv/branches/1.2/freedv-dev/db/current | 1 - freedv/branches/1.2/freedv-dev/db/format | 2 - freedv/branches/1.2/freedv-dev/db/fs-type | 1 - freedv/branches/1.2/freedv-dev/db/fsfs.conf | 38 - .../1.2/freedv-dev/db/min-unpacked-rev | 1 - .../branches/1.2/freedv-dev/db/rep-cache.db | Bin 4096 -> 0 bytes .../branches/1.2/freedv-dev/db/revprops/0/0 | 5 - .../branches/1.2/freedv-dev/db/revprops/0/1 | 13 - freedv/branches/1.2/freedv-dev/db/revs/0/0 | 11 - freedv/branches/1.2/freedv-dev/db/revs/0/1 | 49 - .../1.2/freedv-dev/db/transactions/.gitignore | 0 freedv/branches/1.2/freedv-dev/db/txn-current | 1 - .../1.2/freedv-dev/db/txn-current-lock | 0 .../freedv-dev/db/txn-protorevs/.gitignore | 0 freedv/branches/1.2/freedv-dev/db/uuid | 1 - freedv/branches/1.2/freedv-dev/db/write-lock | 0 .../branches/1.2/freedv-dev/debian/changelog | 5 - freedv/branches/1.2/freedv-dev/debian/compat | 1 - freedv/branches/1.2/freedv-dev/debian/control | 19 - .../branches/1.2/freedv-dev/debian/copyright | 38 - freedv/branches/1.2/freedv-dev/debian/docs | 3 - freedv/branches/1.2/freedv-dev/debian/format | 1 - freedv/branches/1.2/freedv-dev/debian/rules | 30 - freedv/branches/1.2/freedv-dev/script/spot.sh | 29 - .../1.2/freedv-dev/src/CMakeLists.txt | 79 - .../1.2/freedv-dev/src/Makefile.win32 | 52 - .../1.2/freedv-dev/src/afreedvplugin.c | 117 - freedv/branches/1.2/freedv-dev/src/comp.h | 39 - .../1.2/freedv-dev/src/dlg_audiooptions.cpp | 1264 ------ .../1.2/freedv-dev/src/dlg_audiooptions.h | 176 - .../1.2/freedv-dev/src/dlg_filter.cpp | 785 ---- .../branches/1.2/freedv-dev/src/dlg_filter.h | 166 - .../1.2/freedv-dev/src/dlg_options.cpp | 616 --- .../branches/1.2/freedv-dev/src/dlg_options.h | 137 - .../1.2/freedv-dev/src/dlg_plugin.cpp | 148 - .../branches/1.2/freedv-dev/src/dlg_plugin.h | 65 - .../branches/1.2/freedv-dev/src/dlg_ptt.cpp | 570 --- freedv/branches/1.2/freedv-dev/src/dlg_ptt.h | 95 - .../1.2/freedv-dev/src/fdmdv2_defines.h | 106 - .../1.2/freedv-dev/src/fdmdv2_main.cpp | 4042 ----------------- .../branches/1.2/freedv-dev/src/fdmdv2_main.h | 681 --- .../1.2/freedv-dev/src/fdmdv2_pa_wrapper.cpp | 324 -- .../1.2/freedv-dev/src/fdmdv2_pa_wrapper.h | 115 - .../1.2/freedv-dev/src/fdmdv2_plot.cpp | 283 -- .../branches/1.2/freedv-dev/src/fdmdv2_plot.h | 150 - .../1.2/freedv-dev/src/fdmdv2_plot_scalar.cpp | 344 -- .../1.2/freedv-dev/src/fdmdv2_plot_scalar.h | 78 - .../freedv-dev/src/fdmdv2_plot_scatter.cpp | 289 -- .../1.2/freedv-dev/src/fdmdv2_plot_scatter.h | 65 - .../freedv-dev/src/fdmdv2_plot_spectrum.cpp | 267 -- .../1.2/freedv-dev/src/fdmdv2_plot_spectrum.h | 58 - .../freedv-dev/src/fdmdv2_plot_waterfall.cpp | 483 -- .../freedv-dev/src/fdmdv2_plot_waterfall.h | 73 - .../branches/1.2/freedv-dev/src/freedv.icns | Bin 92136 -> 0 bytes freedv/branches/1.2/freedv-dev/src/hamlib.cpp | 160 - freedv/branches/1.2/freedv-dev/src/hamlib.h | 31 - freedv/branches/1.2/freedv-dev/src/info.plist | 104 - .../1.2/freedv-dev/src/serialport.cpp | 234 - .../branches/1.2/freedv-dev/src/serialport.h | 42 - freedv/branches/1.2/freedv-dev/src/sox/band.h | 47 - .../branches/1.2/freedv-dev/src/sox/biquad.c | 178 - .../branches/1.2/freedv-dev/src/sox/biquad.h | 78 - .../branches/1.2/freedv-dev/src/sox/biquads.c | 400 -- .../branches/1.2/freedv-dev/src/sox/effects.c | 544 --- .../branches/1.2/freedv-dev/src/sox/effects.h | 22 - .../1.2/freedv-dev/src/sox/effects_i.c | 379 -- .../1.2/freedv-dev/src/sox/formats_i.c | 487 -- .../branches/1.2/freedv-dev/src/sox/libsox.c | 225 - freedv/branches/1.2/freedv-dev/src/sox/sox.h | 2608 ----------- .../branches/1.2/freedv-dev/src/sox/sox_i.h | 417 -- .../branches/1.2/freedv-dev/src/sox/soxomp.h | 38 - freedv/branches/1.2/freedv-dev/src/sox/util.h | 231 - .../branches/1.2/freedv-dev/src/sox/xmalloc.c | 43 - .../branches/1.2/freedv-dev/src/sox/xmalloc.h | 34 - .../branches/1.2/freedv-dev/src/sox_biquad.c | 134 - .../branches/1.2/freedv-dev/src/sox_biquad.h | 40 - .../branches/1.2/freedv-dev/src/topFrame.cpp | 595 --- freedv/branches/1.2/freedv-dev/src/topFrame.h | 194 - freedv/branches/1.2/script/spot.sh | 29 - freedv/branches/1.2/src/CMakeLists.txt | 79 - freedv/branches/1.2/src/Makefile.win32 | 52 - freedv/branches/1.2/src/afreedvplugin.c | 117 - freedv/branches/1.2/src/comp.h | 39 - freedv/branches/1.2/src/dlg_audiooptions.cpp | 1264 ------ freedv/branches/1.2/src/dlg_audiooptions.h | 176 - freedv/branches/1.2/src/dlg_filter.cpp | 785 ---- freedv/branches/1.2/src/dlg_filter.h | 166 - freedv/branches/1.2/src/dlg_options.cpp | 616 --- freedv/branches/1.2/src/dlg_options.h | 137 - freedv/branches/1.2/src/dlg_plugin.cpp | 148 - freedv/branches/1.2/src/dlg_plugin.h | 65 - freedv/branches/1.2/src/dlg_ptt.cpp | 570 --- freedv/branches/1.2/src/dlg_ptt.h | 93 - freedv/branches/1.2/src/fdmdv2_defines.h | 106 - freedv/branches/1.2/src/fdmdv2_main.cpp | 3943 ---------------- freedv/branches/1.2/src/fdmdv2_main.h | 681 --- freedv/branches/1.2/src/fdmdv2_pa_wrapper.cpp | 324 -- freedv/branches/1.2/src/fdmdv2_pa_wrapper.h | 115 - freedv/branches/1.2/src/fdmdv2_plot.cpp | 283 -- freedv/branches/1.2/src/fdmdv2_plot.h | 150 - .../branches/1.2/src/fdmdv2_plot_scalar.cpp | 281 -- freedv/branches/1.2/src/fdmdv2_plot_scalar.h | 73 - .../branches/1.2/src/fdmdv2_plot_scatter.cpp | 289 -- freedv/branches/1.2/src/fdmdv2_plot_scatter.h | 65 - .../branches/1.2/src/fdmdv2_plot_spectrum.cpp | 267 -- .../branches/1.2/src/fdmdv2_plot_spectrum.h | 58 - .../1.2/src/fdmdv2_plot_waterfall.cpp | 483 -- .../branches/1.2/src/fdmdv2_plot_waterfall.h | 73 - freedv/branches/1.2/src/freedv.icns | Bin 92136 -> 0 bytes freedv/branches/1.2/src/hamlib.cpp | 160 - freedv/branches/1.2/src/hamlib.h | 31 - freedv/branches/1.2/src/info.plist | 104 - freedv/branches/1.2/src/serialport.cpp | 234 - freedv/branches/1.2/src/serialport.h | 42 - freedv/branches/1.2/src/sox/band.h | 47 - freedv/branches/1.2/src/sox/biquad.c | 178 - freedv/branches/1.2/src/sox/biquad.h | 78 - freedv/branches/1.2/src/sox/biquads.c | 400 -- freedv/branches/1.2/src/sox/effects.c | 544 --- freedv/branches/1.2/src/sox/effects.h | 22 - freedv/branches/1.2/src/sox/effects_i.c | 379 -- freedv/branches/1.2/src/sox/formats_i.c | 487 -- freedv/branches/1.2/src/sox/libsox.c | 225 - freedv/branches/1.2/src/sox/sox.h | 2608 ----------- freedv/branches/1.2/src/sox/sox_i.h | 417 -- freedv/branches/1.2/src/sox/soxomp.h | 38 - freedv/branches/1.2/src/sox/util.h | 231 - freedv/branches/1.2/src/sox/xmalloc.c | 43 - freedv/branches/1.2/src/sox/xmalloc.h | 34 - freedv/branches/1.2/src/sox_biquad.c | 134 - freedv/branches/1.2/src/sox_biquad.h | 40 - freedv/branches/1.2/src/topFrame.cpp | 592 --- freedv/branches/1.2/src/topFrame.h | 193 - freedv/tags/1.2.2/.clang/.gitignore | 0 freedv/tags/1.2.2/CMakeLists.txt | 463 -- freedv/tags/1.2.2/COPYING | 502 -- freedv/tags/1.2.2/README.osx | 107 - freedv/tags/1.2.2/README.txt | 233 - freedv/tags/1.2.2/RELEASE_NOTES.txt | 7 - freedv/tags/1.2.2/USER_MANUAL.txt | 100 - freedv/tags/1.2.2/cmake/BuildCodec2.cmake | 25 - freedv/tags/1.2.2/cmake/BuildHamlib.cmake | 20 - freedv/tags/1.2.2/cmake/BuildPortaudio.cmake | 52 - freedv/tags/1.2.2/cmake/BuildSamplerate.cmake | 27 - freedv/tags/1.2.2/cmake/BuildSndfile.cmake | 26 - freedv/tags/1.2.2/cmake/BuildSpeex.cmake | 26 - freedv/tags/1.2.2/cmake/BuildWxWidgets.cmake | 43 - freedv/tags/1.2.2/cmake/FindPortaudio.cmake | 107 - .../tags/1.2.2/cmake/GetDependencies.cmake.in | 37 - freedv/tags/1.2.2/cmake/MinGW.cmake | 8 - .../cmake/Toolchain-Ubuntu-mingw32.cmake | 25 - freedv/tags/1.2.2/cmake/config.h.in | 19 - freedv/tags/1.2.2/cmake/soxconfig.h.in | 16 - freedv/tags/1.2.2/cmake/version.h.in | 11 - freedv/tags/1.2.2/contrib/CMakeLists.txt | 22 - freedv/tags/1.2.2/contrib/LICENSE | 393 -- freedv/tags/1.2.2/contrib/freedv.desktop | 8 - freedv/tags/1.2.2/contrib/freedv.ico | Bin 364646 -> 0 bytes freedv/tags/1.2.2/contrib/freedv.rc | 1 - freedv/tags/1.2.2/contrib/freedv128x128.png | Bin 22063 -> 0 bytes freedv/tags/1.2.2/contrib/freedv256x256.png | Bin 72148 -> 0 bytes freedv/tags/1.2.2/contrib/freedv48x48.png | Bin 3787 -> 0 bytes freedv/tags/1.2.2/contrib/freedv64x64.png | Bin 6289 -> 0 bytes .../tags/1.2.2/contrib/freedv_screenshot.png | Bin 81690 -> 0 bytes freedv/tags/1.2.2/credits.txt | 13 - freedv/tags/1.2.2/db/current | 1 - freedv/tags/1.2.2/db/format | 2 - freedv/tags/1.2.2/db/fs-type | 1 - freedv/tags/1.2.2/db/fsfs.conf | 38 - freedv/tags/1.2.2/db/min-unpacked-rev | 1 - freedv/tags/1.2.2/db/rep-cache.db | Bin 4096 -> 0 bytes freedv/tags/1.2.2/db/revprops/0/0 | 5 - freedv/tags/1.2.2/db/revprops/0/1 | 13 - freedv/tags/1.2.2/db/revs/0/0 | 11 - freedv/tags/1.2.2/db/revs/0/1 | 49 - freedv/tags/1.2.2/db/transactions/.gitignore | 0 freedv/tags/1.2.2/db/txn-current | 1 - freedv/tags/1.2.2/db/txn-current-lock | 0 freedv/tags/1.2.2/db/txn-protorevs/.gitignore | 0 freedv/tags/1.2.2/db/uuid | 1 - freedv/tags/1.2.2/db/write-lock | 0 freedv/tags/1.2.2/debian/changelog | 5 - freedv/tags/1.2.2/debian/compat | 1 - freedv/tags/1.2.2/debian/control | 19 - freedv/tags/1.2.2/debian/copyright | 38 - freedv/tags/1.2.2/debian/docs | 3 - freedv/tags/1.2.2/debian/format | 1 - freedv/tags/1.2.2/debian/rules | 30 - .../tags/1.2.2/freedv-dev/.clang/.gitignore | 0 freedv/tags/1.2.2/freedv-dev/CMakeLists.txt | 463 -- freedv/tags/1.2.2/freedv-dev/COPYING | 502 -- freedv/tags/1.2.2/freedv-dev/README.osx | 107 - freedv/tags/1.2.2/freedv-dev/README.txt | 233 - .../tags/1.2.2/freedv-dev/RELEASE_NOTES.txt | 7 - freedv/tags/1.2.2/freedv-dev/USER_MANUAL.txt | 100 - .../1.2.2/freedv-dev/cmake/BuildCodec2.cmake | 26 - .../1.2.2/freedv-dev/cmake/BuildHamlib.cmake | 20 - .../freedv-dev/cmake/BuildPortaudio.cmake | 52 - .../freedv-dev/cmake/BuildSamplerate.cmake | 27 - .../1.2.2/freedv-dev/cmake/BuildSndfile.cmake | 26 - .../1.2.2/freedv-dev/cmake/BuildSpeex.cmake | 30 - .../freedv-dev/cmake/BuildWxWidgets.cmake | 43 - .../freedv-dev/cmake/FindPortaudio.cmake | 107 - .../freedv-dev/cmake/GetDependencies.cmake.in | 37 - .../tags/1.2.2/freedv-dev/cmake/MinGW.cmake | 8 - .../cmake/Toolchain-Ubuntu-mingw32.cmake | 25 - .../tags/1.2.2/freedv-dev/cmake/config.h.in | 19 - .../1.2.2/freedv-dev/cmake/soxconfig.h.in | 16 - .../tags/1.2.2/freedv-dev/cmake/version.h.in | 11 - .../1.2.2/freedv-dev/contrib/CMakeLists.txt | 22 - freedv/tags/1.2.2/freedv-dev/contrib/LICENSE | 393 -- .../1.2.2/freedv-dev/contrib/freedv.desktop | 8 - .../tags/1.2.2/freedv-dev/contrib/freedv.ico | Bin 364646 -> 0 bytes .../tags/1.2.2/freedv-dev/contrib/freedv.rc | 1 - .../freedv-dev/contrib/freedv128x128.png | Bin 22063 -> 0 bytes .../freedv-dev/contrib/freedv256x256.png | Bin 72148 -> 0 bytes .../1.2.2/freedv-dev/contrib/freedv48x48.png | Bin 3787 -> 0 bytes .../1.2.2/freedv-dev/contrib/freedv64x64.png | Bin 6289 -> 0 bytes .../freedv-dev/contrib/freedv_screenshot.png | Bin 81690 -> 0 bytes freedv/tags/1.2.2/freedv-dev/credits.txt | 13 - freedv/tags/1.2.2/freedv-dev/db/current | 1 - freedv/tags/1.2.2/freedv-dev/db/format | 2 - freedv/tags/1.2.2/freedv-dev/db/fs-type | 1 - freedv/tags/1.2.2/freedv-dev/db/fsfs.conf | 38 - .../tags/1.2.2/freedv-dev/db/min-unpacked-rev | 1 - freedv/tags/1.2.2/freedv-dev/db/rep-cache.db | Bin 4096 -> 0 bytes freedv/tags/1.2.2/freedv-dev/db/revprops/0/0 | 5 - freedv/tags/1.2.2/freedv-dev/db/revprops/0/1 | 13 - freedv/tags/1.2.2/freedv-dev/db/revs/0/0 | 11 - freedv/tags/1.2.2/freedv-dev/db/revs/0/1 | 49 - .../freedv-dev/db/transactions/.gitignore | 0 freedv/tags/1.2.2/freedv-dev/db/txn-current | 1 - .../tags/1.2.2/freedv-dev/db/txn-current-lock | 0 .../freedv-dev/db/txn-protorevs/.gitignore | 0 freedv/tags/1.2.2/freedv-dev/db/uuid | 1 - freedv/tags/1.2.2/freedv-dev/db/write-lock | 0 freedv/tags/1.2.2/freedv-dev/debian/changelog | 5 - freedv/tags/1.2.2/freedv-dev/debian/compat | 1 - freedv/tags/1.2.2/freedv-dev/debian/control | 19 - freedv/tags/1.2.2/freedv-dev/debian/copyright | 38 - freedv/tags/1.2.2/freedv-dev/debian/docs | 3 - freedv/tags/1.2.2/freedv-dev/debian/format | 1 - freedv/tags/1.2.2/freedv-dev/debian/rules | 30 - freedv/tags/1.2.2/freedv-dev/script/spot.sh | 29 - .../tags/1.2.2/freedv-dev/src/CMakeLists.txt | 79 - .../tags/1.2.2/freedv-dev/src/Makefile.win32 | 52 - .../tags/1.2.2/freedv-dev/src/afreedvplugin.c | 117 - freedv/tags/1.2.2/freedv-dev/src/comp.h | 39 - .../1.2.2/freedv-dev/src/dlg_audiooptions.cpp | 1264 ------ .../1.2.2/freedv-dev/src/dlg_audiooptions.h | 176 - .../tags/1.2.2/freedv-dev/src/dlg_filter.cpp | 785 ---- freedv/tags/1.2.2/freedv-dev/src/dlg_filter.h | 166 - .../tags/1.2.2/freedv-dev/src/dlg_options.cpp | 616 --- .../tags/1.2.2/freedv-dev/src/dlg_options.h | 137 - .../tags/1.2.2/freedv-dev/src/dlg_plugin.cpp | 148 - freedv/tags/1.2.2/freedv-dev/src/dlg_plugin.h | 65 - freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.cpp | 570 --- freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.h | 95 - .../1.2.2/freedv-dev/src/fdmdv2_defines.h | 106 - .../tags/1.2.2/freedv-dev/src/fdmdv2_main.cpp | 4042 ----------------- .../tags/1.2.2/freedv-dev/src/fdmdv2_main.h | 681 --- .../freedv-dev/src/fdmdv2_pa_wrapper.cpp | 324 -- .../1.2.2/freedv-dev/src/fdmdv2_pa_wrapper.h | 115 - .../tags/1.2.2/freedv-dev/src/fdmdv2_plot.cpp | 283 -- .../tags/1.2.2/freedv-dev/src/fdmdv2_plot.h | 150 - .../freedv-dev/src/fdmdv2_plot_scalar.cpp | 344 -- .../1.2.2/freedv-dev/src/fdmdv2_plot_scalar.h | 78 - .../freedv-dev/src/fdmdv2_plot_scatter.cpp | 289 -- .../freedv-dev/src/fdmdv2_plot_scatter.h | 65 - .../freedv-dev/src/fdmdv2_plot_spectrum.cpp | 267 -- .../freedv-dev/src/fdmdv2_plot_spectrum.h | 58 - .../freedv-dev/src/fdmdv2_plot_waterfall.cpp | 483 -- .../freedv-dev/src/fdmdv2_plot_waterfall.h | 73 - freedv/tags/1.2.2/freedv-dev/src/freedv.icns | Bin 92136 -> 0 bytes freedv/tags/1.2.2/freedv-dev/src/hamlib.cpp | 160 - freedv/tags/1.2.2/freedv-dev/src/hamlib.h | 31 - freedv/tags/1.2.2/freedv-dev/src/info.plist | 104 - .../tags/1.2.2/freedv-dev/src/serialport.cpp | 234 - freedv/tags/1.2.2/freedv-dev/src/serialport.h | 42 - freedv/tags/1.2.2/freedv-dev/src/sox/band.h | 47 - freedv/tags/1.2.2/freedv-dev/src/sox/biquad.c | 178 - freedv/tags/1.2.2/freedv-dev/src/sox/biquad.h | 78 - .../tags/1.2.2/freedv-dev/src/sox/biquads.c | 400 -- .../tags/1.2.2/freedv-dev/src/sox/effects.c | 544 --- .../tags/1.2.2/freedv-dev/src/sox/effects.h | 22 - .../tags/1.2.2/freedv-dev/src/sox/effects_i.c | 379 -- .../tags/1.2.2/freedv-dev/src/sox/formats_i.c | 487 -- freedv/tags/1.2.2/freedv-dev/src/sox/libsox.c | 225 - freedv/tags/1.2.2/freedv-dev/src/sox/sox.h | 2608 ----------- freedv/tags/1.2.2/freedv-dev/src/sox/sox_i.h | 417 -- freedv/tags/1.2.2/freedv-dev/src/sox/soxomp.h | 38 - freedv/tags/1.2.2/freedv-dev/src/sox/util.h | 231 - .../tags/1.2.2/freedv-dev/src/sox/xmalloc.c | 43 - .../tags/1.2.2/freedv-dev/src/sox/xmalloc.h | 34 - freedv/tags/1.2.2/freedv-dev/src/sox_biquad.c | 134 - freedv/tags/1.2.2/freedv-dev/src/sox_biquad.h | 40 - freedv/tags/1.2.2/freedv-dev/src/topFrame.cpp | 595 --- freedv/tags/1.2.2/freedv-dev/src/topFrame.h | 194 - freedv/tags/1.2.2/script/spot.sh | 29 - freedv/tags/1.2.2/src/CMakeLists.txt | 79 - freedv/tags/1.2.2/src/Makefile.win32 | 52 - freedv/tags/1.2.2/src/afreedvplugin.c | 117 - freedv/tags/1.2.2/src/comp.h | 39 - freedv/tags/1.2.2/src/dlg_audiooptions.cpp | 1264 ------ freedv/tags/1.2.2/src/dlg_audiooptions.h | 176 - freedv/tags/1.2.2/src/dlg_filter.cpp | 785 ---- freedv/tags/1.2.2/src/dlg_filter.h | 166 - freedv/tags/1.2.2/src/dlg_options.cpp | 616 --- freedv/tags/1.2.2/src/dlg_options.h | 137 - freedv/tags/1.2.2/src/dlg_plugin.cpp | 148 - freedv/tags/1.2.2/src/dlg_plugin.h | 65 - freedv/tags/1.2.2/src/dlg_ptt.cpp | 570 --- freedv/tags/1.2.2/src/dlg_ptt.h | 93 - freedv/tags/1.2.2/src/fdmdv2_defines.h | 106 - freedv/tags/1.2.2/src/fdmdv2_main.cpp | 3943 ---------------- freedv/tags/1.2.2/src/fdmdv2_main.h | 681 --- freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.cpp | 324 -- freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.h | 115 - freedv/tags/1.2.2/src/fdmdv2_plot.cpp | 283 -- freedv/tags/1.2.2/src/fdmdv2_plot.h | 150 - freedv/tags/1.2.2/src/fdmdv2_plot_scalar.cpp | 281 -- freedv/tags/1.2.2/src/fdmdv2_plot_scalar.h | 73 - freedv/tags/1.2.2/src/fdmdv2_plot_scatter.cpp | 289 -- freedv/tags/1.2.2/src/fdmdv2_plot_scatter.h | 65 - .../tags/1.2.2/src/fdmdv2_plot_spectrum.cpp | 267 -- freedv/tags/1.2.2/src/fdmdv2_plot_spectrum.h | 58 - .../tags/1.2.2/src/fdmdv2_plot_waterfall.cpp | 483 -- freedv/tags/1.2.2/src/fdmdv2_plot_waterfall.h | 73 - freedv/tags/1.2.2/src/freedv.icns | Bin 92136 -> 0 bytes freedv/tags/1.2.2/src/hamlib.cpp | 160 - freedv/tags/1.2.2/src/hamlib.h | 31 - freedv/tags/1.2.2/src/info.plist | 104 - freedv/tags/1.2.2/src/serialport.cpp | 234 - freedv/tags/1.2.2/src/serialport.h | 42 - freedv/tags/1.2.2/src/sox/band.h | 47 - freedv/tags/1.2.2/src/sox/biquad.c | 178 - freedv/tags/1.2.2/src/sox/biquad.h | 78 - freedv/tags/1.2.2/src/sox/biquads.c | 400 -- freedv/tags/1.2.2/src/sox/effects.c | 544 --- freedv/tags/1.2.2/src/sox/effects.h | 22 - freedv/tags/1.2.2/src/sox/effects_i.c | 379 -- freedv/tags/1.2.2/src/sox/formats_i.c | 487 -- freedv/tags/1.2.2/src/sox/libsox.c | 225 - freedv/tags/1.2.2/src/sox/sox.h | 2608 ----------- freedv/tags/1.2.2/src/sox/sox_i.h | 417 -- freedv/tags/1.2.2/src/sox/soxomp.h | 38 - freedv/tags/1.2.2/src/sox/util.h | 231 - freedv/tags/1.2.2/src/sox/xmalloc.c | 43 - freedv/tags/1.2.2/src/sox/xmalloc.h | 34 - freedv/tags/1.2.2/src/sox_biquad.c | 134 - freedv/tags/1.2.2/src/sox_biquad.h | 40 - freedv/tags/1.2.2/src/topFrame.cpp | 592 --- freedv/tags/1.2.2/src/topFrame.h | 193 - 440 files changed, 85552 deletions(-) delete mode 100644 freedv/branches/1.2/.clang/.gitignore delete mode 100644 freedv/branches/1.2/CMakeLists.txt delete mode 100644 freedv/branches/1.2/COPYING delete mode 100644 freedv/branches/1.2/README.osx delete mode 100644 freedv/branches/1.2/README.txt delete mode 100644 freedv/branches/1.2/RELEASE_NOTES.txt delete mode 100644 freedv/branches/1.2/USER_MANUAL.txt delete mode 100644 freedv/branches/1.2/cmake/BuildCodec2.cmake delete mode 100644 freedv/branches/1.2/cmake/BuildHamlib.cmake delete mode 100644 freedv/branches/1.2/cmake/BuildPortaudio.cmake delete mode 100644 freedv/branches/1.2/cmake/BuildSamplerate.cmake delete mode 100644 freedv/branches/1.2/cmake/BuildSndfile.cmake delete mode 100644 freedv/branches/1.2/cmake/BuildSpeex.cmake delete mode 100644 freedv/branches/1.2/cmake/BuildWxWidgets.cmake delete mode 100644 freedv/branches/1.2/cmake/FindPortaudio.cmake delete mode 100644 freedv/branches/1.2/cmake/GetDependencies.cmake.in delete mode 100644 freedv/branches/1.2/cmake/MinGW.cmake delete mode 100644 freedv/branches/1.2/cmake/Toolchain-Ubuntu-mingw32.cmake delete mode 100644 freedv/branches/1.2/cmake/config.h.in delete mode 100644 freedv/branches/1.2/cmake/soxconfig.h.in delete mode 100644 freedv/branches/1.2/cmake/version.h.in delete mode 100644 freedv/branches/1.2/contrib/CMakeLists.txt delete mode 100644 freedv/branches/1.2/contrib/LICENSE delete mode 100644 freedv/branches/1.2/contrib/freedv.desktop delete mode 100644 freedv/branches/1.2/contrib/freedv.ico delete mode 100644 freedv/branches/1.2/contrib/freedv.rc delete mode 100644 freedv/branches/1.2/contrib/freedv128x128.png delete mode 100644 freedv/branches/1.2/contrib/freedv256x256.png delete mode 100644 freedv/branches/1.2/contrib/freedv48x48.png delete mode 100644 freedv/branches/1.2/contrib/freedv64x64.png delete mode 100644 freedv/branches/1.2/contrib/freedv_screenshot.png delete mode 100644 freedv/branches/1.2/credits.txt delete mode 100644 freedv/branches/1.2/db/current delete mode 100644 freedv/branches/1.2/db/format delete mode 100644 freedv/branches/1.2/db/fs-type delete mode 100644 freedv/branches/1.2/db/fsfs.conf delete mode 100644 freedv/branches/1.2/db/min-unpacked-rev delete mode 100644 freedv/branches/1.2/db/rep-cache.db delete mode 100644 freedv/branches/1.2/db/revprops/0/0 delete mode 100644 freedv/branches/1.2/db/revprops/0/1 delete mode 100644 freedv/branches/1.2/db/revs/0/0 delete mode 100644 freedv/branches/1.2/db/revs/0/1 delete mode 100644 freedv/branches/1.2/db/transactions/.gitignore delete mode 100644 freedv/branches/1.2/db/txn-current delete mode 100644 freedv/branches/1.2/db/txn-current-lock delete mode 100644 freedv/branches/1.2/db/txn-protorevs/.gitignore delete mode 100644 freedv/branches/1.2/db/uuid delete mode 100644 freedv/branches/1.2/db/write-lock delete mode 100644 freedv/branches/1.2/debian/changelog delete mode 100644 freedv/branches/1.2/debian/compat delete mode 100644 freedv/branches/1.2/debian/control delete mode 100644 freedv/branches/1.2/debian/copyright delete mode 100644 freedv/branches/1.2/debian/docs delete mode 100644 freedv/branches/1.2/debian/format delete mode 100755 freedv/branches/1.2/debian/rules delete mode 100644 freedv/branches/1.2/freedv-dev/.clang/.gitignore delete mode 100644 freedv/branches/1.2/freedv-dev/CMakeLists.txt delete mode 100644 freedv/branches/1.2/freedv-dev/COPYING delete mode 100644 freedv/branches/1.2/freedv-dev/README.osx delete mode 100644 freedv/branches/1.2/freedv-dev/README.txt delete mode 100644 freedv/branches/1.2/freedv-dev/RELEASE_NOTES.txt delete mode 100644 freedv/branches/1.2/freedv-dev/USER_MANUAL.txt delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/BuildCodec2.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/BuildHamlib.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/BuildPortaudio.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/BuildSamplerate.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/BuildSndfile.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/BuildSpeex.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/BuildWxWidgets.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/FindPortaudio.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/GetDependencies.cmake.in delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/MinGW.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/config.h.in delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/soxconfig.h.in delete mode 100644 freedv/branches/1.2/freedv-dev/cmake/version.h.in delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/CMakeLists.txt delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/LICENSE delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/freedv.desktop delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/freedv.ico delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/freedv.rc delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/freedv128x128.png delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/freedv256x256.png delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/freedv48x48.png delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/freedv64x64.png delete mode 100644 freedv/branches/1.2/freedv-dev/contrib/freedv_screenshot.png delete mode 100644 freedv/branches/1.2/freedv-dev/credits.txt delete mode 100644 freedv/branches/1.2/freedv-dev/db/current delete mode 100644 freedv/branches/1.2/freedv-dev/db/format delete mode 100644 freedv/branches/1.2/freedv-dev/db/fs-type delete mode 100644 freedv/branches/1.2/freedv-dev/db/fsfs.conf delete mode 100644 freedv/branches/1.2/freedv-dev/db/min-unpacked-rev delete mode 100644 freedv/branches/1.2/freedv-dev/db/rep-cache.db delete mode 100644 freedv/branches/1.2/freedv-dev/db/revprops/0/0 delete mode 100644 freedv/branches/1.2/freedv-dev/db/revprops/0/1 delete mode 100644 freedv/branches/1.2/freedv-dev/db/revs/0/0 delete mode 100644 freedv/branches/1.2/freedv-dev/db/revs/0/1 delete mode 100644 freedv/branches/1.2/freedv-dev/db/transactions/.gitignore delete mode 100644 freedv/branches/1.2/freedv-dev/db/txn-current delete mode 100644 freedv/branches/1.2/freedv-dev/db/txn-current-lock delete mode 100644 freedv/branches/1.2/freedv-dev/db/txn-protorevs/.gitignore delete mode 100644 freedv/branches/1.2/freedv-dev/db/uuid delete mode 100644 freedv/branches/1.2/freedv-dev/db/write-lock delete mode 100644 freedv/branches/1.2/freedv-dev/debian/changelog delete mode 100644 freedv/branches/1.2/freedv-dev/debian/compat delete mode 100644 freedv/branches/1.2/freedv-dev/debian/control delete mode 100644 freedv/branches/1.2/freedv-dev/debian/copyright delete mode 100644 freedv/branches/1.2/freedv-dev/debian/docs delete mode 100644 freedv/branches/1.2/freedv-dev/debian/format delete mode 100755 freedv/branches/1.2/freedv-dev/debian/rules delete mode 100644 freedv/branches/1.2/freedv-dev/script/spot.sh delete mode 100644 freedv/branches/1.2/freedv-dev/src/CMakeLists.txt delete mode 100644 freedv/branches/1.2/freedv-dev/src/Makefile.win32 delete mode 100644 freedv/branches/1.2/freedv-dev/src/afreedvplugin.c delete mode 100644 freedv/branches/1.2/freedv-dev/src/comp.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_audiooptions.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_audiooptions.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_filter.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_filter.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_options.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_options.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_plugin.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_plugin.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_ptt.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/dlg_ptt.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_defines.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_main.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_main.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_pa_wrapper.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_pa_wrapper.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scalar.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scalar.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scatter.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scatter.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_spectrum.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_spectrum.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_waterfall.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_waterfall.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/freedv.icns delete mode 100644 freedv/branches/1.2/freedv-dev/src/hamlib.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/hamlib.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/info.plist delete mode 100644 freedv/branches/1.2/freedv-dev/src/serialport.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/serialport.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/band.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/biquad.c delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/biquad.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/biquads.c delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/effects.c delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/effects.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/effects_i.c delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/formats_i.c delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/libsox.c delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/sox.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/sox_i.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/soxomp.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/util.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/xmalloc.c delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox/xmalloc.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox_biquad.c delete mode 100644 freedv/branches/1.2/freedv-dev/src/sox_biquad.h delete mode 100644 freedv/branches/1.2/freedv-dev/src/topFrame.cpp delete mode 100644 freedv/branches/1.2/freedv-dev/src/topFrame.h delete mode 100644 freedv/branches/1.2/script/spot.sh delete mode 100644 freedv/branches/1.2/src/CMakeLists.txt delete mode 100644 freedv/branches/1.2/src/Makefile.win32 delete mode 100644 freedv/branches/1.2/src/afreedvplugin.c delete mode 100644 freedv/branches/1.2/src/comp.h delete mode 100644 freedv/branches/1.2/src/dlg_audiooptions.cpp delete mode 100644 freedv/branches/1.2/src/dlg_audiooptions.h delete mode 100644 freedv/branches/1.2/src/dlg_filter.cpp delete mode 100644 freedv/branches/1.2/src/dlg_filter.h delete mode 100644 freedv/branches/1.2/src/dlg_options.cpp delete mode 100644 freedv/branches/1.2/src/dlg_options.h delete mode 100644 freedv/branches/1.2/src/dlg_plugin.cpp delete mode 100644 freedv/branches/1.2/src/dlg_plugin.h delete mode 100644 freedv/branches/1.2/src/dlg_ptt.cpp delete mode 100644 freedv/branches/1.2/src/dlg_ptt.h delete mode 100644 freedv/branches/1.2/src/fdmdv2_defines.h delete mode 100644 freedv/branches/1.2/src/fdmdv2_main.cpp delete mode 100644 freedv/branches/1.2/src/fdmdv2_main.h delete mode 100644 freedv/branches/1.2/src/fdmdv2_pa_wrapper.cpp delete mode 100644 freedv/branches/1.2/src/fdmdv2_pa_wrapper.h delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot.cpp delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot.h delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot_scalar.cpp delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot_scalar.h delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot_scatter.cpp delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot_scatter.h delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot_spectrum.cpp delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot_spectrum.h delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot_waterfall.cpp delete mode 100644 freedv/branches/1.2/src/fdmdv2_plot_waterfall.h delete mode 100644 freedv/branches/1.2/src/freedv.icns delete mode 100644 freedv/branches/1.2/src/hamlib.cpp delete mode 100644 freedv/branches/1.2/src/hamlib.h delete mode 100644 freedv/branches/1.2/src/info.plist delete mode 100644 freedv/branches/1.2/src/serialport.cpp delete mode 100644 freedv/branches/1.2/src/serialport.h delete mode 100644 freedv/branches/1.2/src/sox/band.h delete mode 100644 freedv/branches/1.2/src/sox/biquad.c delete mode 100644 freedv/branches/1.2/src/sox/biquad.h delete mode 100644 freedv/branches/1.2/src/sox/biquads.c delete mode 100644 freedv/branches/1.2/src/sox/effects.c delete mode 100644 freedv/branches/1.2/src/sox/effects.h delete mode 100644 freedv/branches/1.2/src/sox/effects_i.c delete mode 100644 freedv/branches/1.2/src/sox/formats_i.c delete mode 100644 freedv/branches/1.2/src/sox/libsox.c delete mode 100644 freedv/branches/1.2/src/sox/sox.h delete mode 100644 freedv/branches/1.2/src/sox/sox_i.h delete mode 100644 freedv/branches/1.2/src/sox/soxomp.h delete mode 100644 freedv/branches/1.2/src/sox/util.h delete mode 100644 freedv/branches/1.2/src/sox/xmalloc.c delete mode 100644 freedv/branches/1.2/src/sox/xmalloc.h delete mode 100644 freedv/branches/1.2/src/sox_biquad.c delete mode 100644 freedv/branches/1.2/src/sox_biquad.h delete mode 100644 freedv/branches/1.2/src/topFrame.cpp delete mode 100644 freedv/branches/1.2/src/topFrame.h delete mode 100644 freedv/tags/1.2.2/.clang/.gitignore delete mode 100644 freedv/tags/1.2.2/CMakeLists.txt delete mode 100644 freedv/tags/1.2.2/COPYING delete mode 100644 freedv/tags/1.2.2/README.osx delete mode 100644 freedv/tags/1.2.2/README.txt delete mode 100644 freedv/tags/1.2.2/RELEASE_NOTES.txt delete mode 100644 freedv/tags/1.2.2/USER_MANUAL.txt delete mode 100644 freedv/tags/1.2.2/cmake/BuildCodec2.cmake delete mode 100644 freedv/tags/1.2.2/cmake/BuildHamlib.cmake delete mode 100644 freedv/tags/1.2.2/cmake/BuildPortaudio.cmake delete mode 100644 freedv/tags/1.2.2/cmake/BuildSamplerate.cmake delete mode 100644 freedv/tags/1.2.2/cmake/BuildSndfile.cmake delete mode 100644 freedv/tags/1.2.2/cmake/BuildSpeex.cmake delete mode 100644 freedv/tags/1.2.2/cmake/BuildWxWidgets.cmake delete mode 100644 freedv/tags/1.2.2/cmake/FindPortaudio.cmake delete mode 100644 freedv/tags/1.2.2/cmake/GetDependencies.cmake.in delete mode 100644 freedv/tags/1.2.2/cmake/MinGW.cmake delete mode 100644 freedv/tags/1.2.2/cmake/Toolchain-Ubuntu-mingw32.cmake delete mode 100644 freedv/tags/1.2.2/cmake/config.h.in delete mode 100644 freedv/tags/1.2.2/cmake/soxconfig.h.in delete mode 100644 freedv/tags/1.2.2/cmake/version.h.in delete mode 100644 freedv/tags/1.2.2/contrib/CMakeLists.txt delete mode 100644 freedv/tags/1.2.2/contrib/LICENSE delete mode 100644 freedv/tags/1.2.2/contrib/freedv.desktop delete mode 100644 freedv/tags/1.2.2/contrib/freedv.ico delete mode 100644 freedv/tags/1.2.2/contrib/freedv.rc delete mode 100644 freedv/tags/1.2.2/contrib/freedv128x128.png delete mode 100644 freedv/tags/1.2.2/contrib/freedv256x256.png delete mode 100644 freedv/tags/1.2.2/contrib/freedv48x48.png delete mode 100644 freedv/tags/1.2.2/contrib/freedv64x64.png delete mode 100644 freedv/tags/1.2.2/contrib/freedv_screenshot.png delete mode 100644 freedv/tags/1.2.2/credits.txt delete mode 100644 freedv/tags/1.2.2/db/current delete mode 100644 freedv/tags/1.2.2/db/format delete mode 100644 freedv/tags/1.2.2/db/fs-type delete mode 100644 freedv/tags/1.2.2/db/fsfs.conf delete mode 100644 freedv/tags/1.2.2/db/min-unpacked-rev delete mode 100644 freedv/tags/1.2.2/db/rep-cache.db delete mode 100644 freedv/tags/1.2.2/db/revprops/0/0 delete mode 100644 freedv/tags/1.2.2/db/revprops/0/1 delete mode 100644 freedv/tags/1.2.2/db/revs/0/0 delete mode 100644 freedv/tags/1.2.2/db/revs/0/1 delete mode 100644 freedv/tags/1.2.2/db/transactions/.gitignore delete mode 100644 freedv/tags/1.2.2/db/txn-current delete mode 100644 freedv/tags/1.2.2/db/txn-current-lock delete mode 100644 freedv/tags/1.2.2/db/txn-protorevs/.gitignore delete mode 100644 freedv/tags/1.2.2/db/uuid delete mode 100644 freedv/tags/1.2.2/db/write-lock delete mode 100644 freedv/tags/1.2.2/debian/changelog delete mode 100644 freedv/tags/1.2.2/debian/compat delete mode 100644 freedv/tags/1.2.2/debian/control delete mode 100644 freedv/tags/1.2.2/debian/copyright delete mode 100644 freedv/tags/1.2.2/debian/docs delete mode 100644 freedv/tags/1.2.2/debian/format delete mode 100755 freedv/tags/1.2.2/debian/rules delete mode 100644 freedv/tags/1.2.2/freedv-dev/.clang/.gitignore delete mode 100644 freedv/tags/1.2.2/freedv-dev/CMakeLists.txt delete mode 100644 freedv/tags/1.2.2/freedv-dev/COPYING delete mode 100644 freedv/tags/1.2.2/freedv-dev/README.osx delete mode 100644 freedv/tags/1.2.2/freedv-dev/README.txt delete mode 100644 freedv/tags/1.2.2/freedv-dev/RELEASE_NOTES.txt delete mode 100644 freedv/tags/1.2.2/freedv-dev/USER_MANUAL.txt delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/BuildCodec2.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/BuildHamlib.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/BuildPortaudio.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/BuildSamplerate.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/BuildSndfile.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/BuildSpeex.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/BuildWxWidgets.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/FindPortaudio.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/GetDependencies.cmake.in delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/MinGW.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/config.h.in delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/soxconfig.h.in delete mode 100644 freedv/tags/1.2.2/freedv-dev/cmake/version.h.in delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/CMakeLists.txt delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/LICENSE delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/freedv.desktop delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/freedv.ico delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/freedv.rc delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/freedv128x128.png delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/freedv256x256.png delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/freedv48x48.png delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/freedv64x64.png delete mode 100644 freedv/tags/1.2.2/freedv-dev/contrib/freedv_screenshot.png delete mode 100644 freedv/tags/1.2.2/freedv-dev/credits.txt delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/current delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/format delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/fs-type delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/fsfs.conf delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/min-unpacked-rev delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/rep-cache.db delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/revprops/0/0 delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/revprops/0/1 delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/revs/0/0 delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/revs/0/1 delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/transactions/.gitignore delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/txn-current delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/txn-current-lock delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/txn-protorevs/.gitignore delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/uuid delete mode 100644 freedv/tags/1.2.2/freedv-dev/db/write-lock delete mode 100644 freedv/tags/1.2.2/freedv-dev/debian/changelog delete mode 100644 freedv/tags/1.2.2/freedv-dev/debian/compat delete mode 100644 freedv/tags/1.2.2/freedv-dev/debian/control delete mode 100644 freedv/tags/1.2.2/freedv-dev/debian/copyright delete mode 100644 freedv/tags/1.2.2/freedv-dev/debian/docs delete mode 100644 freedv/tags/1.2.2/freedv-dev/debian/format delete mode 100755 freedv/tags/1.2.2/freedv-dev/debian/rules delete mode 100644 freedv/tags/1.2.2/freedv-dev/script/spot.sh delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/CMakeLists.txt delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/Makefile.win32 delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/afreedvplugin.c delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/comp.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_audiooptions.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_audiooptions.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_filter.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_filter.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_options.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_options.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_plugin.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_plugin.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_defines.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_main.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_main.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_pa_wrapper.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_pa_wrapper.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scalar.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scalar.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scatter.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scatter.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_spectrum.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_spectrum.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_waterfall.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_waterfall.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/freedv.icns delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/hamlib.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/hamlib.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/info.plist delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/serialport.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/serialport.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/band.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/biquad.c delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/biquad.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/biquads.c delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/effects.c delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/effects.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/effects_i.c delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/formats_i.c delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/libsox.c delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/sox.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/sox_i.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/soxomp.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/util.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/xmalloc.c delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox/xmalloc.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox_biquad.c delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/sox_biquad.h delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/topFrame.cpp delete mode 100644 freedv/tags/1.2.2/freedv-dev/src/topFrame.h delete mode 100644 freedv/tags/1.2.2/script/spot.sh delete mode 100644 freedv/tags/1.2.2/src/CMakeLists.txt delete mode 100644 freedv/tags/1.2.2/src/Makefile.win32 delete mode 100644 freedv/tags/1.2.2/src/afreedvplugin.c delete mode 100644 freedv/tags/1.2.2/src/comp.h delete mode 100644 freedv/tags/1.2.2/src/dlg_audiooptions.cpp delete mode 100644 freedv/tags/1.2.2/src/dlg_audiooptions.h delete mode 100644 freedv/tags/1.2.2/src/dlg_filter.cpp delete mode 100644 freedv/tags/1.2.2/src/dlg_filter.h delete mode 100644 freedv/tags/1.2.2/src/dlg_options.cpp delete mode 100644 freedv/tags/1.2.2/src/dlg_options.h delete mode 100644 freedv/tags/1.2.2/src/dlg_plugin.cpp delete mode 100644 freedv/tags/1.2.2/src/dlg_plugin.h delete mode 100644 freedv/tags/1.2.2/src/dlg_ptt.cpp delete mode 100644 freedv/tags/1.2.2/src/dlg_ptt.h delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_defines.h delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_main.cpp delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_main.h delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.cpp delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.h delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot.cpp delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot.h delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot_scalar.cpp delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot_scalar.h delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot_scatter.cpp delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot_scatter.h delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot_spectrum.cpp delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot_spectrum.h delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot_waterfall.cpp delete mode 100644 freedv/tags/1.2.2/src/fdmdv2_plot_waterfall.h delete mode 100644 freedv/tags/1.2.2/src/freedv.icns delete mode 100644 freedv/tags/1.2.2/src/hamlib.cpp delete mode 100644 freedv/tags/1.2.2/src/hamlib.h delete mode 100644 freedv/tags/1.2.2/src/info.plist delete mode 100644 freedv/tags/1.2.2/src/serialport.cpp delete mode 100644 freedv/tags/1.2.2/src/serialport.h delete mode 100644 freedv/tags/1.2.2/src/sox/band.h delete mode 100644 freedv/tags/1.2.2/src/sox/biquad.c delete mode 100644 freedv/tags/1.2.2/src/sox/biquad.h delete mode 100644 freedv/tags/1.2.2/src/sox/biquads.c delete mode 100644 freedv/tags/1.2.2/src/sox/effects.c delete mode 100644 freedv/tags/1.2.2/src/sox/effects.h delete mode 100644 freedv/tags/1.2.2/src/sox/effects_i.c delete mode 100644 freedv/tags/1.2.2/src/sox/formats_i.c delete mode 100644 freedv/tags/1.2.2/src/sox/libsox.c delete mode 100644 freedv/tags/1.2.2/src/sox/sox.h delete mode 100644 freedv/tags/1.2.2/src/sox/sox_i.h delete mode 100644 freedv/tags/1.2.2/src/sox/soxomp.h delete mode 100644 freedv/tags/1.2.2/src/sox/util.h delete mode 100644 freedv/tags/1.2.2/src/sox/xmalloc.c delete mode 100644 freedv/tags/1.2.2/src/sox/xmalloc.h delete mode 100644 freedv/tags/1.2.2/src/sox_biquad.c delete mode 100644 freedv/tags/1.2.2/src/sox_biquad.h delete mode 100644 freedv/tags/1.2.2/src/topFrame.cpp delete mode 100644 freedv/tags/1.2.2/src/topFrame.h diff --git a/freedv/branches/1.2/.clang/.gitignore b/freedv/branches/1.2/.clang/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/CMakeLists.txt b/freedv/branches/1.2/CMakeLists.txt deleted file mode 100644 index a9f70496..00000000 --- a/freedv/branches/1.2/CMakeLists.txt +++ /dev/null @@ -1,463 +0,0 @@ -# -# FreeDV - HF Digital Voice for Radio Amateurs -# -# CMake configuration contributed by Richard Shaw (KF5OIM) -# Please report questions, comments, problems, or patches to the freetel -# mailing list: https://lists.sourceforge.net/lists/listinfo/freetel-codec2 -# - -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7" CACHE STRING "Minimum OS X deployment version") - -cmake_minimum_required(VERSION 2.8) - -# Prevent in-source builds to protect automake/autoconf config. -# If an in-source build is attempted, you will still need to clean up a few -# files manually. -set(CMAKE_DISABLE_SOURCE_CHANGES ON) -set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) -if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") - message(FATAL_ERROR "In-source builds in ${CMAKE_BINARY_DIR} are not " - "allowed, please remove ./CMakeCache.txt and ./CMakeFiles/, create a " - "separate build directory and run cmake from there.") -endif("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") - -# Set local module path. -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") - -project(FreeDV) - -# -# Set FreeDV version and generate src/version.h -# -set(FREEDV_VERSION_MAJOR 1) -set(FREEDV_VERSION_MINOR 2) -set(FREEDV_VERSION_PATCH 2) -set(FREEDV_VERSION ${FREEDV_VERSION_MAJOR}.${FREEDV_VERSION_MINOR}) -if(FREEDV_VERSION_PATCH) - set(FREEDV_VERSION ${FREEDV_VERSION}.${FREEDV_VERSION_PATCH}) -endif() -set(FREEDV_VERSION_SUFFIX FALSE) -if(FREEDV_VERSION_SUFFIX) - set(FREEDV_VERSION_STRING "${FREEDV_VERSION} ${FREEDV_VERSION_SUFFIX}") -else() - set(FREEDV_VERSION_STRING "${FREEDV_VERSION}") -endif() -message(STATUS "FreeDV version: ${FREEDV_VERSION_STRING}") -configure_file(cmake/version.h.in src/version.h @ONLY) - -# Set default build type -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug") - message(STATUS "Build type not specified, defaulting to ${CMAKE_BUILD_TYPE}") -endif(NOT CMAKE_BUILD_TYPE) - -# Work around for not using a svn working copy. -add_definitions(-D_NO_AUTOTOOLS_) -find_program(SVN_PATH svn) -if(SVN_PATH) - execute_process(COMMAND ${SVN_PATH} info --show-item revision - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE SVN_REVISION_RESULT - OUTPUT_VARIABLE SVN_CURRENT_REVISION - ERROR_QUIET - ) -else() - set(SVN_REVISION_RESULT 1) -endif() - -if(SVN_REVISION_RESULT EQUAL 0) - string(STRIP ${SVN_CURRENT_REVISION} SVN_REVISION) - add_definitions(-DSVN_REVISION="${SVN_REVISION}") -else() - add_definitions(-DSVN_REVISION="None") -endif() - - -# Set default build flags. -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") -if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11") -endif(APPLE) - -# -# Setup cmake options -# -set(CMAKE_VERBOSE_MAKEFILE TRUE CACHE BOOL "Verbose makefile.") -set(USE_STATIC_DEPS FALSE CACHE BOOL - "Download and build static libraries instead of system libraries.") -set(USE_STATIC_PORTAUDIO FALSE CACHE BOOL - "Download and build static portaudio instead of the system library.") -set(USE_STATIC_SNDFILE FALSE CACHE BOOL - "Download and build static sndfile instead of the system library.") -set(USE_STATIC_SAMPLERATE FALSE CACHE BOOL - "Download and build static samplerate instead of the system library.") -set(USE_STATIC_CODEC2 TRUE CACHE BOOL - "Download and build static codec2 instead of the system library.") -set(USE_STATIC_SPEEXDSP TRUE CACHE BOOL - "Download and build static speex instead of the system library.") -set(BOOTSTRAP_WXWIDGETS FALSE CACHE BOOL - "Download and build static wxWidgets instead of the system library.") - -if(USE_STATIC_DEPS) - set(USE_STATIC_PORTAUDIO TRUE FORCE) - set(USE_STATIC_SNDFILE TRUE FORCE) - set(USE_STATIC_SAMPLERATE TRUE FORCE) - set(USE_STATIC_CODEC2 TRUE FORCE) -endif(USE_STATIC_DEPS) - -# -# Pull in external wxWidgets target if performing static build. -# -if(BOOTSTRAP_WXWIDGETS) - message(STATUS "Adding wxWidgets build target...") - include(cmake/BuildWxWidgets.cmake) -endif(BOOTSTRAP_WXWIDGETS) - -# -# Perform bootstrap build of wxWidgets -# -if(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) - message(STATUS "Will perform bootstrap build of wxWidgets. - After make step completes, re-run cmake and make again to perform FreeDV build.") -# -# Continue normal build if not bootstrapping wxWidgets or is already built. -# -else(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) - - -# -# Various hacks and work arounds for building under MinGW. -# -if(MINGW) - message(STATUS "System is MinGW.") - # Setup HOST variable. - include(cmake/MinGW.cmake) - # This sets up the exe icon for windows under mingw. - set(RES_FILES "") - set(RES_FILES "${CMAKE_SOURCE_DIR}/contrib/freedv.rc") - set(CMAKE_RC_COMPILER_INIT windres) - enable_language(RC) - set(CMAKE_RC_COMPILE_OBJECT - " -O coff -i -o ") - include(InstallRequiredSystemLibraries) -endif(MINGW) - -# Math library is automatic on MinGW -if(UNIX) - set(CMAKE_REQUIRED_INCLUDES math.h) - set(CMAKE_REQUIRED_LIBRARIES m) -endif(UNIX) - -# Find some standard headers and functions. -include(CheckIncludeFiles) -check_include_files("byteswap.h" HAVE_BYTESWAP_H) -check_include_files("limits.h" HAVE_LIMITS_H) -check_include_files("stddef.h" HAVE_STDDEF_H) -check_include_files("stdlib.h" HAVE_STDLIB_H) -check_include_files("string.h" HAVE_STRING_H) -check_include_files("strings.h" HAVE_STRINGS_H) -check_include_files("ltdl.h" HAVE_LTDL_H) -check_include_files("inttypes.h" HAVE_INTTYPES_H) -check_include_files("sys/stat.h" HAVE_SYS_STAT_H) -check_include_files("sys/types.h" HAVE_SYS_TYPES_H) - -include(CheckTypeSize) -check_type_size("int" SIZEOF_INT) - -include(CheckFunctionExists) -check_function_exists(floor HAVE_FLOOR) -check_function_exists(memset HAVE_MEMSET) -check_function_exists(pow HAVE_POW) -check_function_exists(sqrt HAVE_SQRT) -check_function_exists(fseeko HAVE_FSEEKO) -check_function_exists(fmemopen HAVE_FMEMOPEN) -check_function_exists(strcasecmp HAVE_STRCASECMP) -check_function_exists(vsnprintf HAVE_VSNPRINTF) - -include(CheckSymbolExists) -check_symbol_exists("_fseeki64" "stdio.h" HAVE__FSEEKI64) - -# fdmdv2_main.h requires patching to find config.h as it current looks in the -# source directory and the generated file goes in the binary directory. -configure_file ("${PROJECT_SOURCE_DIR}/cmake/config.h.in" - "${PROJECT_BINARY_DIR}/config.h" ) -include_directories(${PROJECT_BINARY_DIR}) -add_definitions(-DHAVE_CONFIG_H) - -# Config file for bundled sox sources -configure_file("${PROJECT_SOURCE_DIR}/cmake/soxconfig.h.in" - "${PROJECT_BINARY_DIR}/soxconfig.h") - -# Pthread Library -find_package(Threads REQUIRED) -message(STATUS "Threads library flags: ${CMAKE_THREAD_LIBS_INIT}") - -# -# Find codec2 -# -if(NOT USE_STATIC_CODEC2) - message(STATUS "Looking for codec2...") - # 'CONFIG' removed due to incompatibility with cmake version - # in Ubuntu 12.04 (Precise) -- Stuart Longland - find_package(codec2 QUIET) - if(codec2_FOUND) - get_target_property(CODEC2_LIBRARY codec2 LOCATION) - message(STATUS " codec2 library: ${CODEC2_LIBRARY}") - message(STATUS " codec2 headers: ${codec2_INCLUDE_DIRS}") - else() - # Try to find manually - find_path(CODEC2_INCLUDE_DIRS codec2.h - PATH_SUFFIXES codec2) - find_library(CODEC2_LIBRARY NAMES codec2) - if(CODEC2_LIBRARY AND CODEC2_INCLUDE_DIRS) - message(STATUS " codec2 library: ${CODEC2_LIBRARY}") - message(STATUS " codec2 headers: ${CODEC2_INCLUDE_DIRS}") - list(APPEND FREEDV_LINK_LIBS ${CODEC2_LIBRARY}) - include_directories(${CODEC2_INCLUDE_DIRS}) - else() - message(FATAL_ERROR "codec2 library not found. -Linux: -Codec2 may not be in your distribution so build yourself or use the cmake option to build statically into FreeDV. -Windws: -It's easiest to use the cmake option: USE_STATIC_CODEC2" - ) - endif() - endif() -else(NOT USE_STATIC_CODEC2) - message(STATUS "Will attempt static build of codec2.") - include(cmake/BuildCodec2.cmake) -endif(NOT USE_STATIC_CODEC2) - -# -# Find or build portaudio Library -# -if(NOT USE_STATIC_PORTAUDIO) - message(STATUS "Looking for portaudio...") - find_package(Portaudio REQUIRED) - if(PORTAUDIO_FOUND) - message(STATUS " portaudio library: ${PORTAUDIO_LIBRARIES}") - message(STATUS " portaudio headers: ${PORTAUDIO_INCLUDE_DIRS}") - list(APPEND FREEDV_LINK_LIBS ${PORTAUDIO_LIBRARIES}) - include_directories(${PORTAUDIO_INCLUDE_DIRS}) - else() - message(FATAL_ERROR "portaudio library not found. -On Linux systems try installing: - portaudio-devel (RPM based systems) - libportaudio-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_PORTAUDIO" - ) - endif() - if(NOT ${PORTAUDIO_VERSION} EQUAL 19) - message(WARNING "Portaudio versions other than 19 are known to have issues. You have been warned!") - endif() -else(NOT USE_STATIC_PORTAUDIO) - message(STATUS "Will attempt static build of portaudio.") - include(cmake/BuildPortaudio.cmake) -endif(NOT USE_STATIC_PORTAUDIO) - -# -# Hamlib library -# -message(STATUS "Looking for hamlib...") -find_path(HAMLIB_INCLUDE_DIR hamlib/rig.h) -find_library(HAMLIB_LIBRARY hamlib PATH_SUFFIXES hamlib) -message(STATUS "Hamlib library: ${HAMLIB_LIBRARY}") -message(STATUS "Hamlib headers: ${HAMLIB_INCLUDE_DIR}") -if(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - message(STATUS "Hamlib library found.") - include_directories(${HAMLIB_INCLUDE_DIR}) - list(APPEND FREEDV_LINK_LIBS ${HAMLIB_LIBRARY}) -else(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - message(FATAL_ERROR "hamlib not found. -On Linux systems try installing: - hamlib-devel (RPM based systems) - libhamlib-dev (DEB based systems)" - ) -endif(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - - -# -# Samplerate Library -# -if(NOT USE_STATIC_SAMPLERATE) - message(STATUS "Looking for samplerate...") - find_library(LIBSAMPLERATE samplerate) - find_path(LIBSAMPLERATE_INCLUDE_DIR samplerate.h) - message(STATUS " samplerate library: ${LIBSAMPLERATE}") - message(STATUS " samplerate headers: ${LIBSAMPLERATE_INCLUDE_DIR}") - if(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) - list(APPEND FREEDV_LINK_LIBS ${LIBSAMPLERATE}) - include_directories(${LIBSAMPLERATE_INCLUDE_DIR}) - else(LIBSTAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) - message(FATAL_ERROR "samplerate library not found. -On Linux systems try installing: - samplerate-devel (RPM based systems) - libsamplerate-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_SAMPLERATE" - ) - endif(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) -else(NOT USE_STATIC_SAMPLERATE) - message(STATUS "Will attempt static build of samplerate.") - include(cmake/BuildSamplerate.cmake) -endif(NOT USE_STATIC_SAMPLERATE) - -# -# sndfile Library -# -if(NOT USE_STATIC_SNDFILE) - message(STATUS "Looking for sndfile...") - find_library(LIBSNDFILE sndfile) - find_path(LIBSNDFILE_INCLUDE_DIR sndfile.h) - message(STATUS " sndfile library: ${LIBSNDFILE}") - message(STATUS " sndfile headers: ${LIBSNDFILE_INCLUDE_DIR}") - if(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) - list(APPEND FREEDV_LINK_LIBS ${LIBSNDFILE}) - else(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) - message(FATAL_ERROR "sndfile library not found. -On Linux systems try installing: - libsndfile-devel (RPM based systems) - libsndfile-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_SNDFILE" - ) - endif(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) -else(NOT USE_STATIC_SNDFILE) - message(STATUS "Will attempt static build of sndfile.") - include(cmake/BuildSndfile.cmake) -endif(NOT USE_STATIC_SNDFILE) - -# -# Find wxWidgets -# -if(NOT BOOTSTRAP_WXWIDGETS) - set(WXCONFIG "" CACHE FILEPATH "Location of wx-config binary.") - set(WXRC "" CACHE FILEPATH "Location of wxrc binary.") -endif(NOT BOOTSTRAP_WXWIDGETS) -#if(BOOTSTRAP_WXWIDGETS) -# set(WXCONFIG "${CMAKE_BINARY_DIR}/external/dist/bin/wx-config") -# set(WXRC "${CMAKE_BINARY_DIR}/external/dist/bin/wxrc") -# list(APPEND FREEDV_STATIC_DEPS wxWidgets) -#endif(BOOTSTRAP_WXWIDGETS) -message(STATUS "Looking for wxWidgets...") -if(WXCONFIG) - message(STATUS "wx-config: ${WXCONFIG}") - set(wxWidgets_CONFIG_EXECUTABLE ${WXCONFIG}) -endif(WXCONFIG) -if(WXRC) - message(STATUS "wxrc: ${WXRC}") - set(wxWidgets_wxrc_EXECUTABLE ${WXRC}) -endif(WXRC) -set(WX_VERSION_MIN 3.0.0) -find_package(wxWidgets REQUIRED core base aui html net adv) -execute_process(COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" --version - OUTPUT_VARIABLE WX_VERSION) -string(STRIP ${WX_VERSION} WX_VERSION) -if(WX_VERSION VERSION_EQUAL ${WX_VERSION_MIN} - OR WX_VERSION VERSION_GREATER ${WX_VERSION_MIN}) - message(STATUS "wxWidgets version: ${WX_VERSION}") -else() - message(FATAL_ERROR "wxWidgets must be installed on your system. -Please check that wx-config is in path, the directory -where wxWidgets libraries are installed (returned by -'wx-config --libs' or 'wx-config --static --libs' command) -is in LD_LIBRARY_PATH or equivalent variable and -wxWidgets version is ${WX_VERSION_MIN} or above.") -endif() -if(wxWidgets_FOUND) - include("${wxWidgets_USE_FILE}") - list(APPEND FREEDV_LINK_LIBS ${wxWidgets_LIBRARIES}) -endif(wxWidgets_FOUND) - -# -# Find speex library -# -if(NOT USE_STATIC_SPEEXDSP) - message(STATUS "Looking for Speex DSP library.") - find_path(SPEEXDSP_INCLUDE_DIR NAMES speex/speex.h speex/speexdsp_types.h) - find_library(SPEEXDSP_LIBRARY speexdsp) - message(STATUS " Speex DSP headers: ${SPEEXDSP_INCLUDE_DIR}") - message(STATUS " Speex DSP library: ${SPEEXDSP_LIBRARY}") - if(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) - include_directories(${SPEEXDSP_INCLUDE_DIR}) - list(APPEND FREEDV_LINK_LIBS ${SPEEXDSP_LIBRARY}) - else(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) - message(FATAL_ERROR "Speex DSP library not found!") - endif(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) -else() - message(STATUS "Will attempt static build of speex.") - include(cmake/BuildSpeex.cmake) -endif() - -# -# Find libdl for dlopen/dlclose -# -if(UNIX) - message(STATUS "Looking for dl library.") - find_library(DL_LIBRARY dl) - if(DL_LIBRARY) - message(STATUS " dl library: ${DL_LIBRARY}") - list(APPEND FREEDV_LINK_LIBS ${DL_LIBRARY}) - else() - message(FATAL_ERROR "dl library not found. -On Linux systems try installing: - glibc-devel (RPM based systems) - glibc-dev (DEB based systems)" - ) - endif() -endif(UNIX) - - -#Freedv -add_subdirectory(src) - -# Icons and desktop file -add_subdirectory(contrib) - -message(STATUS "Build type will be: ${CMAKE_BUILD_TYPE}") - -# -# Cpack NSIS configuration for Windows. -# -if(WIN32) - # Detect if we're doing a 32-bit or 64-bit windows build. - if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(CMAKE_CL_64 TRUE) - set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") - endif() - if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - set(CPACK_STRIP_FILES TRUE) - endif() - configure_file(cmake/GetDependencies.cmake.in cmake/GetDependencies.cmake - @ONLY - ) - install(SCRIPT ${CMAKE_BINARY_DIR}/cmake/GetDependencies.cmake) - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "HF Digital Voice for Radio Amateurs") - set(CPACK_PACKAGE_VENDOR "CMake") - #set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README") - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") - set(CPACK_PACKAGE_VERSION_MAJOR ${FREEDV_VERSION_MAJOR}) - set(CPACK_PACKAGE_VERSION_MINOR ${FREEDV_VERSION_MINOR}) - # CPack expects a patch level version so set it here and override if we - # are actually setting one. - set(CPACK_PACKAGE_VERSION_PATCH 0) - if(FREEDV_VERSION_PATCH) - set(CPACK_PACKAGE_VERSION_PATCH ${FREEDV_VERSION_PATCH}) - endif() - if(FREEDV_VERSION_SUFFIX) - set(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${FREEDV_VERSION_SUFFIX}") - endif() - # There is a bug in NSI that does not handle full unix paths properly. Make - # sure there is at least one set of four (4) backlasshes. - #set(CPACK_PACKAGE_ICON "${CMake_SOURCE_DIR}/Utilities/Release\\\\InstallIcon.bmp") - set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\freedv.exe") - set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY}") - set(CPACK_NSIS_PACKAGE_NAME "FreeDV") - set(CPACK_PACKAGE_EXECUTABLES freedv;FreeDV) - set(CPACK_NSIS_URL_INFO_ABOUT "http://freedv.org") - set(CPACK_NSIS_MODIFY_PATH OFF) - set(CPACK_NSIS_MENU_LINKS - "http://freedv.org" "FreeDV Homepage") - include(CPack) -endif(WIN32) - -endif(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) diff --git a/freedv/branches/1.2/COPYING b/freedv/branches/1.2/COPYING deleted file mode 100644 index cfd4e991..00000000 --- a/freedv/branches/1.2/COPYING +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see - . - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/freedv/branches/1.2/README.osx b/freedv/branches/1.2/README.osx deleted file mode 100644 index c71544ff..00000000 --- a/freedv/branches/1.2/README.osx +++ /dev/null @@ -1,107 +0,0 @@ -Building under OSX is similar to building under linux, but there are some additional steps that need to be performed to produce a working app-bundle. - -For the following instructions, I'm assuming you will be placing everything in: -/Users//Dev/ - -1/ DEPENDENCIES -Using Macports, most of the appropriate dependencies can be installed by: - -$ sudo port install subversion git libtool libsamplerate sox portaudio dylibbundler cmake - -It should be fairly similar using HomeBrew, but you will need to replace all the /opt/ paths in the following instructions. - -1.1/ HAMLIB -First, we will need to build hamlib from source, as we need hamlib to be statically compiled (Macports won't do this..) - -$ git clone git://git.code.sf.net/p/hamlib/code hamlib-code -$ cd hamlib-code - -You will now need to edit line 12 of autogen.sh, to change "libtoolize" to "glibtoolize" - -$ ./autogen.sh -$ ./configure --disable-shared --prefix /Users//Dev/hamlib -$ make -$ make install - -You should now have an installation of hamlib in ~/Dev/hamlib - -Just in case you have hamlib installed via Macports, it may be a good idea to run -$ sudo port deactivate hamlib - -1.2/ WXWIDGETS -To be able to produce an appbundle, we need wxWidgets to be build statically. Again, Macports won't do this out of the box. - -Edit the wxWidgets-3.0 port file using: -$ sudo port edit wxWidgets-3.0 - -and add the following to the bottom of the file: - -variant static description { build a static version of the libraries with some other options... } { - configure.args-append --enable-std_iostreams - configure.args-append --disable-shared - configure.args-delete --with-sdl - configure.args-delete --with-opengl - set installtype release-static -} - -Now you can build and install a static variant of wxWidgets with: -$ sudo port install wxWidgets-3.0 +static - -Note: This will probably break anything else which is using wxWidgets. Once you have finished building FreeDV, you may -want to go back to the dynamically compiled version using: -$ sudo port install wxWidgets-3.0 - -HomeBrew Users: Anyone know how to do the above? - -1.3/ CODEC2 LIBRARIES -The FreeDV CMake procedure will automatically checkout and compile Codec2. -If you want to build and install your own copy (i.e. for access to the command-line tools), you can do so: - -$ wget http://files.freedv.org/codec2/codec2-0.4.tar.gz -or -$ svn checkout https://svn.code.sf.net/p/freetel/code/codec2-dev/ - -$ cd codec2-0.4 -or -cd codec2-dev -$ mkdir build_osx && cd build_osx -$ cmake ../ && make -$ sudo make install - -3/ BUILDING FREEDV -Get the FreeDV source by either: - -Getting the current 'stable' release (1.0): -$ wget http://files.freedv.org/freedv/freedv-1.0.tar.gz -$ tar -xzf freedv-1.0.tar.gz - -or - -Checking the latest revision out from SVN: -$ svn checkout https://svn.code.sf.net/p/freetel/code/freedv-dev/ - -$ cd freedv-1.0 -or -$ cd freedv-dev - -$ mkdir build_osx && cd build_osx - -Assuming you are intending on building Codec2 as part of the build process, run: - -$ cmake -DWXCONFIG=/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.0/lib/wx/config/osx_cocoa-unicode-static-3.0 -DCMAKE_EXE_LINKER_FLAGS="-L/opt/local/lib" -DHAMLIB_INCLUDE_DIR=../../hamlib/include -DHAMLIB_LIBRARY=../../hamlib/lib/libhamlib.a ../ - -Then, build FreeDV: -$ make - -The build process will create an appbundle (FreeDV.app) and a compressed disk image (FreeDV.dmg) in ./build_osx/src -Move these to wherever you want, and run! - -Happy DVing! - -Acknowledgements: -A big thank you to Mooneer Salem, K6AQ, for walking me through this process, and figuring out how to solve the wxWidgets and Hamlib issues. - -Please e-mail any corrections to either the digitalvoice google group list, or myself, at: -vk5qi(at)rfhead.net -Mark Jessop VK5QI - diff --git a/freedv/branches/1.2/README.txt b/freedv/branches/1.2/README.txt deleted file mode 100644 index 26e731d1..00000000 --- a/freedv/branches/1.2/README.txt +++ /dev/null @@ -1,233 +0,0 @@ -================================== - FreeDV GUI README.txt -================================== - -This document describes how to build the FreeDV GUI program for -various operating systems. See also: - - http://freedv.org - introduction, documentation, downloads - RELEASE_NOTES.txt - changes made to each version - USER_MANUAL.txt - FreeDV GUI Manual - -================================== - Building and installing on Linux -================================== - -First the basics: - - $ sudo apt-get install build-essential cmake subversion - -To install the required development libraries instead of building them -statically: - -Debian/Ubuntu: - - $ sudo apt-get install libwxgtk3.0-dev portaudio19-dev \ - libhamlib-dev libsamplerate-dev libasound2-dev libao-dev libgsm1-dev \ - libsndfile-dev - -Fedora: - - $ sudo dnf install wxGTK3-devel portaudio-devel libsamplerate-devel \ - libsndfile-devel speexdsp-devel hamlib-devel alsa-lib-devel libao-devel \ - gsm-devel - - -RHEL/CentOS and derivitves (requires Fedora EPEL repository) - - $ sudo yum install wxGTK3-devel portaudio-devel libsamplerate-devel \ - libsndfile-devel speexdsp-devel hamlib-devel alsa-lib-devel libao-devel \ - gsm-devel - - -Quickstart 1 ------------- - -1/ Using a modern Linux, and the development library packages - installed above: - - $ cd /path/to/freedv - $ mkdir build_linux - $ cd build_linux - $ cmake ../ - $ make - $ ./src/freedv - -Quickstart 2 ------------- - -Builds static versions of wxWidgets, portaudio, codec2-dev, which are commonly -missing on older Linux systems. - -1/ Assumes static build of wxWidgets and the freedv-dev source is checked out into ~/freedv-dev: - - $ cd ~/freedv-dev - $ mkdir build_linux - $ cd build_linux - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE ../ - $ make - -2/ Then you can configure FreeDV using your local codec-dev, something like: - - $ cmake -DCMAKE_BUILD_TYPE=Debug -DBOOTSTRAP_WXWIDGETS=TRUE -DCODEC2_INCLUDE_DIRS=/path/to/codec2-dev/src -DCODEC2_LIBRARY=/path/to/codec2-dev/build_linux/src/libcodec2.so -DUSE_STATIC_CODEC2=FALSE -DUSE_STATIC_PORTAUDIO=TRUE ../ - -3/ OR build a local copy of codec2-dev: - - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE -DUSE_STATIC_CODEC2=TRUE -DUSE_STATIC_PORTAUDIO=TRUE ../ - -4/ Build and run FreeDV: - - $ make - $ ./src/freedv - -======================================================= - Building for Windows on Ubuntu Linux (Cross compiling) -======================================================= - -1/ Install the cross compiling toolchain: - - $ sudo apt-get install mingw-w64 - -2/ Patch cmake using: http://www.cmake.org/gitweb?p=stage/cmake.git;a=patch;h=33286235048495ceafb636d549d9a4e8891967ae - -3/ Checkout a fresh copy of codec2-dev and build for Windows, pointing to the generate_codebook built by a linux build of generate_codebook, using this cmake line - - $ cmake .. -DCMAKE_TOOLCHAIN_FILE=/home/david/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake -DUNITTEST=FALSE -DGENERATE_CODEBOOK=/home/david/codec2-dev/build_linux/src/generate_codebook - -4/ Build WxWidgets - - $ cd /path/to/freedv-dev - $ mkdir build_windows - $ cd build_windows - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE .. -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-Ubuntu-mingw32.cmake -DCMAKE_BUILD_TYPE=Debug - $ make - -5/ Download and install the Windows version of Hamlib: - - $ wget http://internode.dl.sourceforge.net/project/hamlib/hamlib/1.2.15.3/hamlib-win32-1.2.15.3.zip - $ unzip hamlib-win32-1.2.15.3.zip - -6/ Build All the libraries and FreeDV: - - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-Ubuntu-mingw32.cmake -DUSE_STATIC_PORTAUDIO=TRUE -DUSE_STATIC_SNDFILE=TRUE -DUSE_STATIC_SAMPLERATE=TRUE -DUSE_STATIC_CODEC2=FALSE -DCODEC2_INCLUDE_DIRS=/home/david/tmp/codec2-dev/src -DCODEC2_LIBRARY=/home/david/tmp/codec2-dev/build_windows/src/libcodec2.dll.a -DHAMLIB_INCLUDE_DIR=hamlib-win32-1.2.15.3/include -DHAMLIB_LIBRARY=hamlib-win32-1.2.15.3/lib/gcc/libhamlib.dll.a -DCMAKE_BUILD_TYPE=Debug .. - $ make - -7/ Test on Linux with "wine", this will tell you if any DLLs are missing: - - $ wine src/freedv.exe - -8/ When moving to an actual Windows machine, I needed: - - /usr/lib/gcc/i686-w64-mingw32/4.8/libstdc++-6.dll - /usr/lib/gcc/i686-w64-mingw32/4.8/libgcc_s_sjlj-1.dll - /usr/i686-w64-mingw32/lib/libwinpthread-1.dll - - Wine seems to find these automagically, so I found them on my system by - looking at ~/.wine/system.reg for PATH: - - [System\\CurrentControlSet\\Control\\Session Manager\\Environment] 1423800803 - "PATH"=str(2):"C:\\windows\\system32;C:\\windows;C:\\windows\\system32\\wbem;Z:\\usr\\i686-w64-mingw32\\lib;Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\4.8" - - -==================================== - Building and installing on Windows -==================================== - -The windows build is similar to linux and follows the same basic workflow, -however, while codec2 and FreeDV (freedv) build well on windows, some of the -dependencies do not. For that reson current windows releases are cross-compiled -from linux. - -Only MinGW is supported. While it is likely possible to perform a pure MinGW -build, installing MSYS2 will make your life easier. - -CMake may not automatically detect that you're in the MSYS environment. If this -occurs you need to pass cmake the proper generator: - -cmake -G"MSYS Makefiles" [other options] - -=============================== - Bootstrapping wxWidgets build -=============================== - -If wxWidgets (>= 3.0) is not available then one option is to have CMake boot- -strap the build for FreeDV. - -This is required because the tool wx-config is used to get the correct compiler -and linker flags of the wxWidgets components needed by FreeDV. Since this is -normally done at configure time, not during "make", it is not possible for CMake -or have this information prior to building wxWidgets. - -In order to work around this issue you can "bootstrap" the wxWidgets build using -the CMake option, "BOOTSTRAP_WXWIDGETS". wxWidgets will be built using static -libraries. - -NOTE: This forces "USE_STATIC_WXWIDGETS" to be true internally regarless of the -value set manually. - -(from any directory, but empty directory outside of the source is prefered.) -$ cmake -DBOOTSTRAP_WXWIDGETS=TRUE /path/to/freedv -$ make -(wxWidgets is downloaded and built) -$ cmake . -(wxWidgets build should be detected) -$ make -(if all goes well, as root) -$ make install - -==================================== - Building and installing on OSX -==================================== - -Pls see README.osx - -==================================== - Building and installing on FreeBSD -==================================== - -As per "Quickstart 2" above but change build_linux to build_freebsd - -======= -Editing -======= - -Please make sure your text editor does not insert tabs, and -used indents of 4 spaces. The following .emacs code was used to -configure emacs: - -(setq-default indent-tabs-mode nil) - -(add-hook 'c-mode-common-hook - (function (lambda () - (setq c-basic-offset 4) - ))) - -FreeDV GUI TODO List --------------------- - -[ ] Ubuntu packaging -[ ] default sound card in/out setting for rx out of the box -[ ] When application close on windows while "Start" down sometimes crashes - + Also on Linux it reports an unterminated thread when exiting -[ ] Tool-Audio Config Dialog sound device names truncated on Windows -[ ] Serialport::closeport() on Linux takes about 1 second - + delays 'Stop' on main window test on Tools-PTT Test -[ ] Voice keyer file name at bottom on main screen truncated - + need a bigger field -[ ] Start/Stop file rec/playback, work out a better UI, - maybe buttons on front page -[ ] feature for evaluating yr own sound quality - + trap bad mic response/levels - + zero in on different sound quality from different users -[ ] feeding audio over UDP say from from gqrx - + could also be used to netcat stored files -[ ] refactoring - [ ] fdmdv2_main.cpp is way too long - [ ] rename fdmdv2*.cpp -> freedv*.cpp - [ ] dlg_ptt uses ComPortsDlg name internally, rename PttDlg or similar -[ ] Add RSID - + use case, when would it be used? -[ ] clean up dialogs - + were based on auto generation code - + must be an easier/clearer way to write them - diff --git a/freedv/branches/1.2/RELEASE_NOTES.txt b/freedv/branches/1.2/RELEASE_NOTES.txt deleted file mode 100644 index 79eaeca0..00000000 --- a/freedv/branches/1.2/RELEASE_NOTES.txt +++ /dev/null @@ -1,7 +0,0 @@ -V1.2.2 July 2017 ----------------- - -1/ Improvements to Hamlib support, error message reporting, serial rate box. - -2/ Disabled unused UDP comms/egexp processing to clean up Options dialog. - diff --git a/freedv/branches/1.2/USER_MANUAL.txt b/freedv/branches/1.2/USER_MANUAL.txt deleted file mode 100644 index 2cc44945..00000000 --- a/freedv/branches/1.2/USER_MANUAL.txt +++ /dev/null @@ -1,100 +0,0 @@ -====================== -FREEDV GUI USER MANUAL -====================== - -Introduction ------------- - -This document describes additional features in the latest FreeDV -releases that haven't been documented in other sources. See also -freedv.org - -PTT Configuration ------------------ - -Tools-PTT Dialog - -Hamlib comes with a default serial rate for each radio. If your radio -has a different serial rate change the Serial Rate drop down box to -match your radio. - -When "Test" is pressed, the "Serial Params" field is populated and -displayed. This will help track down any mis-matches between Hamlib -and your radio. - -Voice Keyer ------------ - -Voice Keyer Button on Front Page -Options-PTT Dialog - -Puts FreeDV and your radio into transmit, reads a wave file of your -voice to call CQ, then switches to receive to see if anyone is -replying. If you press space bar the voice keyer stops. If a signal -with a valid sync is received for a few seconds the voice keyer stops. - -Options-PTT dialog can be used to select the wave file, set the Rx -delay, and number of times the tx/rx cycle repeats. - -The wave file for the voice keyer should be in 8kHz mono 16 bit sample -form. Use a free application such as Audacity to convert a file you -have recorded to this format. - -Test Frame Histogram --------------------- - -Test Frame Histogram tab on Front Page - -Displays BER of each carrier when in "test frame" mode. As each QPSK -carrier has 2 bits there are 2*Nc histogram points. - -Ideally all carriers will have about the same BER (+/- 20% after 5000 -total bit errors). However problems can occur with filtering in the -tx path. If one carrier has less power, then it will have a higher -BER. The errors in this carrier will tend to dominate overall -BER. For example if one carrier is attenuated due to SSB filter ripple -in the tx path then the BER on that carrier will be higher. This is -bad news for DV. - -Suggested usage: - -i) Transmit FreeDV in test frame mode. Use a 2nd rx (or -get a friend) to monitor your rx signal with FreeDV in test frame -mode. - -ii) Adjust your rx SNR to get a BER of a few % (e.g. reduce tx -power, use a short antenna for the rx, point your beam away, adjust rx -RF gain). - -iii) Monitor the error histogram for a few minutes, until you -have say 5000 total bit errors. You have a problem if the BER of any -carrier is more than 20% different from the rest. - -A typical issue will be one carrier at 1.0, the others at 0.5, -indicating the poorer carrier BER is twice the larger. - -Full Duplex Testing with loopback ---------------------------------- - -Options - Half Duplex check box - -FreeDV GUI can operate in full duplex mode which is useful for -development of listening to your own FreeDV signal as only one PC is -required. Normal operation is half duplex. - -Tx and Rx signals can be looped back via an analog connection between -the sound cards. - -On Linux, using the Alsa loopback module: - - $ sudo modprobe snd-aloop - $ ./freedv - - In Tools - Audio Config - Receive Tab - From Radio select -> Loopback: Loopback PCM (hw:1,0) - - Transmit Tab - To Radio select -> Loopback: Loopback PCM (hw:1,1) - -TODO ----- - -[ ] Merge this information into existing start up guides - diff --git a/freedv/branches/1.2/cmake/BuildCodec2.cmake b/freedv/branches/1.2/cmake/BuildCodec2.cmake deleted file mode 100644 index 588b3161..00000000 --- a/freedv/branches/1.2/cmake/BuildCodec2.cmake +++ /dev/null @@ -1,25 +0,0 @@ -set(SPEEXDSP_CMAKE_ARGS -DBUILD_SHARED_LIBS=FALSE -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external/dist) - -if(USE_STATIC_SPEEXDSP) - list(APPEND SPEEXDSP_CMAKE_ARGS - -DSPEEXDSP_LIBRARIES=${CMAKE_BINARY_DIR}/external/dist/lib/libspeexdsp.a - -DSPEEXDSP_INCLUDE_DIR=${CMAKE_BINARY_DIR}/external/dist/include) -endif() - -set(CODEC2_CMAKE_ARGS -DUNITTEST=FALSE) - -if(CMAKE_CROSSCOMPILING) - set(CODEC2_CMAKE_ARGS ${CODEC2_CMAKE_ARGS} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) -endif() - -include(ExternalProject) -ExternalProject_Add(codec2 - SVN_REPOSITORY https://svn.code.sf.net/p/freetel/code/codec2-dev - CMAKE_ARGS ${CODEC2_CMAKE_ARGS} ${SPEEXDSP_CMAKE_ARGS} - INSTALL_COMMAND "" -) -set(CODEC2_LIBRARIES - ${CMAKE_BINARY_DIR}/codec2-prefix/src/codec2-build/src/libcodec2.a) -include_directories(${CMAKE_BINARY_DIR}/codec2-prefix/src/codec2/src) -list(APPEND FREEDV_LINK_LIBS ${CODEC2_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS codec2) diff --git a/freedv/branches/1.2/cmake/BuildHamlib.cmake b/freedv/branches/1.2/cmake/BuildHamlib.cmake deleted file mode 100644 index 4166f5ae..00000000 --- a/freedv/branches/1.2/cmake/BuildHamlib.cmake +++ /dev/null @@ -1,20 +0,0 @@ -set(HAMLIB_TARBALL "hamlib-1.2.15.3") - -include(ExternalProject) -ExternalProject_Add(hamlib - URL http://downloads.sourceforge.net/hamlib/${HAMLIB_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(HAMLIB_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/portaudio.lib) -else(WIN32) - set(HAMLIB_LIBRARIES - ) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${HAMLIB_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS hamlib) diff --git a/freedv/branches/1.2/cmake/BuildPortaudio.cmake b/freedv/branches/1.2/cmake/BuildPortaudio.cmake deleted file mode 100644 index cc33d061..00000000 --- a/freedv/branches/1.2/cmake/BuildPortaudio.cmake +++ /dev/null @@ -1,52 +0,0 @@ -set(PORTAUDIO_TARBALL "pa_stable_v19_20111121") - -# required linking libraries on linux. Not sure about windows. -find_library(ALSA_LIBRARIES asound) - -if(UNIX AND NOT ALSA_LIBRARIES) - message(ERROR "Could not find alsa library which is required for portaudio. -On Linux systems try installing: - alsa-lib-devel (RPM based systems) - libasound2-dev (DEB based systems)" - ) -endif() - -# Make sure that configure knows what system we're using when cross-compiling. -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --enable-cxx --without-jack --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) -else() - set(CONFIGURE_COMMAND ./configure --enable-cxx --without-jack --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) -endif() - -include(ExternalProject) -ExternalProject_Add(portaudio - URL http://www.portaudio.com/archives/${PORTAUDIO_TARBALL}.tgz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(PORTAUDIO_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudio.a - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudiocpp.a -) -else(WIN32) - find_library(RT rt) - find_library(ASOUND asound) - set(PORTAUDIO_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudio.a - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudiocpp.a - ${RT} - ${ASOUND} - ) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) - -# Add the portaudio library to the list of libraries that must be linked. -list(APPEND FREEDV_LINK_LIBS ${PORTAUDIO_LIBRARIES}) - -# Setup a dependency so that this gets built before linking to freedv. -list(APPEND FREEDV_STATIC_DEPS portaudio) diff --git a/freedv/branches/1.2/cmake/BuildSamplerate.cmake b/freedv/branches/1.2/cmake/BuildSamplerate.cmake deleted file mode 100644 index 8b6b7a36..00000000 --- a/freedv/branches/1.2/cmake/BuildSamplerate.cmake +++ /dev/null @@ -1,27 +0,0 @@ -set(SAMPLERATE_TARBALL "libsamplerate-0.1.8") - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-sndfile --disable-fftw) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist) -endif() - -include(ExternalProject) -ExternalProject_Add(samplerate - URL http://www.mega-nerd.com/SRC/${SAMPLERATE_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(SAMPLERATE_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libsamplerate.a) -else(WIN32) - set(SAMPLERATE_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libsamplerate.a) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SAMPLERATE_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS samplerate) diff --git a/freedv/branches/1.2/cmake/BuildSndfile.cmake b/freedv/branches/1.2/cmake/BuildSndfile.cmake deleted file mode 100644 index c49b6388..00000000 --- a/freedv/branches/1.2/cmake/BuildSndfile.cmake +++ /dev/null @@ -1,26 +0,0 @@ -set(SNDFILE_TARBALL "libsndfile-1.0.25") - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-external-libs --disable-shared --disable-sqlite) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-external-libs --disable-shared --disable-external-libs) -endif() - -include(ExternalProject) -ExternalProject_Add(sndfile - URL http://www.mega-nerd.com/libsndfile/files/${SNDFILE_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) V=1 - INSTALL_COMMAND $(MAKE) install -) -if(MINGW) - set(SNDFILE_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libsndfile.a) -else() - set(SNDFILE_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libsndfile.a) -endif() - -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SNDFILE_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS sndfile) diff --git a/freedv/branches/1.2/cmake/BuildSpeex.cmake b/freedv/branches/1.2/cmake/BuildSpeex.cmake deleted file mode 100644 index 262d558f..00000000 --- a/freedv/branches/1.2/cmake/BuildSpeex.cmake +++ /dev/null @@ -1,26 +0,0 @@ -set(SPEEXDSP_TARBALL "speexdsp-1.2rc3.tar.gz") - -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-examples) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-examples) -endif() - -include(ExternalProject) -ExternalProject_Add(speex - URL http://downloads.xiph.org/releases/speex/${SPEEXDSP_TARBALL} - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) - -set(SPEEXDSP_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libspeexdsp.a) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SPEEXDSP_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS speex) -if(USE_STATIC_CODEC2) - add_dependencies(codec2 speex) -endif() diff --git a/freedv/branches/1.2/cmake/BuildWxWidgets.cmake b/freedv/branches/1.2/cmake/BuildWxWidgets.cmake deleted file mode 100644 index 901d8062..00000000 --- a/freedv/branches/1.2/cmake/BuildWxWidgets.cmake +++ /dev/null @@ -1,43 +0,0 @@ -set(WXWIDGETS_TARBALL "wxWidgets-3.0.2") - -# If we're cross-compiling then we need to set the target host manually. -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) -endif() - -# If not cross-compiling then use the built-in makefile, otherwise use standard configure. -if(MINGW AND NOT CMAKE_CROSSCOMPILING) -# set(CONFIGURE_COMMAND "true") -# set(MAKE_COMMAND $(MAKE) -C build/msw -f makefile.gcc SHARED=0 UNICODE=1 BUILD=release PREFIX=${CMAKE_BINARY_DIR}/external/dist) - set(CONFIGURE_COMMAND ./configure --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -if(NOT MINGW) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --target=${HOST} --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -include(ExternalProject) -ExternalProject_Add(wxWidgets - URL http://downloads.sourceforge.net/wxwindows/${WXWIDGETS_TARBALL}.tar.bz2 - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND ${MAKE_COMMAND} - INSTALL_COMMAND $(MAKE) install -) - -ExternalProject_Get_Property(wxWidgets install_dir) -message(STATUS "wxWidgets install dir: ${install_dir}") -if(NOT WXCONFIG) - set(WXCONFIG "${install_dir}/bin/wx-config") -endif() -if(EXISTS ${WXCONFIG}) - set(BS_WX_DONE TRUE) -endif() diff --git a/freedv/branches/1.2/cmake/FindPortaudio.cmake b/freedv/branches/1.2/cmake/FindPortaudio.cmake deleted file mode 100644 index 158e20ee..00000000 --- a/freedv/branches/1.2/cmake/FindPortaudio.cmake +++ /dev/null @@ -1,107 +0,0 @@ -# - Try to find Portaudio -# Once done this will define -# -# PORTAUDIO_FOUND - system has Portaudio -# PORTAUDIO_INCLUDE_DIRS - the Portaudio include directory -# PORTAUDIO_LIBRARIES - Link these to use Portaudio -# PORTAUDIO_DEFINITIONS - Compiler switches required for using Portaudio -# PORTAUDIO_VERSION - Portaudio version -# -# Copyright (c) 2006 Andreas Schneider -# -# Redistribution and use is allowed according to the terms of the New BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - - -if (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - # in cache already - set(PORTAUDIO_FOUND TRUE) -else (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - if (NOT WIN32) - include(FindPkgConfig) - pkg_check_modules(PORTAUDIO2 portaudio-2.0) - endif (NOT WIN32) - - if (PORTAUDIO2_FOUND) - set(PORTAUDIO_INCLUDE_DIRS - ${PORTAUDIO2_INCLUDE_DIRS} - ) - if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_LIBRARIES "${PORTAUDIO2_LIBRARY_DIRS}/lib${PORTAUDIO2_LIBRARIES}.dylib") - else (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_LIBRARIES - ${PORTAUDIO2_LIBRARIES} - ) - endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_VERSION - 19 - ) - set(PORTAUDIO_FOUND TRUE) - else (PORTAUDIO2_FOUND) - find_path(PORTAUDIO_INCLUDE_DIR - NAMES - portaudio.h - PATHS - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ) - - find_library(PORTAUDIO_LIBRARY - NAMES - portaudio - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - find_path(PORTAUDIO_LIBRARY_DIR - NAMES - portaudio - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - set(PORTAUDIO_INCLUDE_DIRS - ${PORTAUDIO_INCLUDE_DIR} - ) - set(PORTAUDIO_LIBRARIES - ${PORTAUDIO_LIBRARY} - ) - - set(PORTAUDIO_LIBRARY_DIRS - ${PORTAUDIO_LIBRARY_DIR} - ) - - set(PORTAUDIO_VERSION - 18 - ) - - if (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) - set(PORTAUDIO_FOUND TRUE) - endif (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) - - if (PORTAUDIO_FOUND) - if (NOT Portaudio_FIND_QUIETLY) - message(STATUS "Found Portaudio: ${PORTAUDIO_LIBRARIES}") - endif (NOT Portaudio_FIND_QUIETLY) - else (PORTAUDIO_FOUND) - if (Portaudio_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Portaudio") - endif (Portaudio_FIND_REQUIRED) - endif (PORTAUDIO_FOUND) - endif (PORTAUDIO2_FOUND) - - - # show the PORTAUDIO_INCLUDE_DIRS and PORTAUDIO_LIBRARIES variables only in the advanced view - mark_as_advanced(PORTAUDIO_INCLUDE_DIRS PORTAUDIO_LIBRARIES) - -endif (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - diff --git a/freedv/branches/1.2/cmake/GetDependencies.cmake.in b/freedv/branches/1.2/cmake/GetDependencies.cmake.in deleted file mode 100644 index 7470aa6d..00000000 --- a/freedv/branches/1.2/cmake/GetDependencies.cmake.in +++ /dev/null @@ -1,37 +0,0 @@ -# As this script is run in a new cmake instance, it does not have access to -# the existing cache variables. Pass them in via the configure_file command. -set(CMAKE_BINARY_DIR @CMAKE_BINARY_DIR@) -set(CMAKE_SOURCE_DIR @CMAKE_SOURCE_DIR@) -set(UNIX @UNIX@) -set(WIN32 @WIN32@) -set(CMAKE_CROSSCOMPILING @CMAKE_CROSSCOMPILING@) -set(CMAKE_FIND_LIBRARY_SUFFIXES @CMAKE_FIND_LIBRARY_SUFFIXES@) -set(CMAKE_FIND_LIBRARY_PREFIXES @CMAKE_FIND_LIBRARY_PREFIXES@) -set(CMAKE_SYSTEM_LIBRARY_PATH @CMAKE_SYSTEM_LIBRARY_PATH@) -set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) - -set(FREEDV_EXE ${CMAKE_BINARY_DIR}/src/freedv.exe) - -include(GetPrerequisites) -get_prerequisites("${FREEDV_EXE}" _deps 1 0 "" "") -foreach(_runtime ${_deps}) - message("Looking for ${_runtime}") - find_library(RUNTIME_${_runtime} ${_runtime}) - message("${RUNTIME_${_runtime}}") - if(RUNTIME_${_runtime}) - message("Looking for dependencies of ${_runtime}") - get_prerequisites("${RUNTIME_${_runtime}}" _deps2 1 0 "" "") - foreach(_runtime2 ${_deps2}) - find_library(RUNTIME_${_runtime2} ${_runtime2}) - message("${RUNTIME_${_runtime2}}") - if(RUNTIME_${_runtime2}) - file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" - TYPE EXECUTABLE FILES "${RUNTIME_${_runtime2}}") - endif() - endforeach() - endif() - if(RUNTIME_${_runtime}) - file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" - TYPE EXECUTABLE FILES "${RUNTIME_${_runtime}}") - endif() -endforeach() diff --git a/freedv/branches/1.2/cmake/MinGW.cmake b/freedv/branches/1.2/cmake/MinGW.cmake deleted file mode 100644 index 333c1dc0..00000000 --- a/freedv/branches/1.2/cmake/MinGW.cmake +++ /dev/null @@ -1,8 +0,0 @@ -# If we're cross-compiling then we need to set the target host manually. -if(MINGW AND CMAKE_CROSSCOMPILING) - if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(HOST x86_64-w64-mingw32) - else() - set(HOST i686-w64-mingw32) - endif() -endif() diff --git a/freedv/branches/1.2/cmake/Toolchain-Ubuntu-mingw32.cmake b/freedv/branches/1.2/cmake/Toolchain-Ubuntu-mingw32.cmake deleted file mode 100644 index 3507d720..00000000 --- a/freedv/branches/1.2/cmake/Toolchain-Ubuntu-mingw32.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# Sample toolchain file for building for Windows from an Ubuntu Linux system. -# -# Typical usage: -# *) install cross compiler: `sudo apt-get install mingw-w64 g++-mingw-w64` -# *) cd build -# *) cmake -DCMAKE_TOOLCHAIN_FILE=~/Toolchain-Ubuntu-mingw32.cmake .. - -set(CMAKE_SYSTEM_NAME Windows) -set(TOOLCHAIN_PREFIX i686-w64-mingw32) - -# cross compilers to use for C and C++ -set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) -set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) -set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) - -# target environment on the build host system -# set 1st to dir with the cross compiler's C/C++ headers/libs -set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) - -# modify default behavior of FIND_XXX() commands to -# search for headers/libs in the target environment and -# search for programs in the build host environment -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/freedv/branches/1.2/cmake/config.h.in b/freedv/branches/1.2/cmake/config.h.in deleted file mode 100644 index 8e3ab76b..00000000 --- a/freedv/branches/1.2/cmake/config.h.in +++ /dev/null @@ -1,19 +0,0 @@ -/*-------------------------------------------------------------------------- - ** This file is autogenerated from config.h.in - ** during the cmake configuration of your project. If you need to make changes - ** edit the original file NOT THIS FILE. - ** --------------------------------------------------------------------------*/ -#ifndef _CONFIGURATION_HEADER_GUARD_H_ -#define _CONFIGURATION_HEADER_GUARD_H_ - -#define SIZEOF_INT @SIZEOF_INT@ -#cmakedefine HAVE_LIMITS_H @HAVE_LIMITS_H@ -#cmakedefine HAVE_STDINT_H @HAVE_STDINT_H@ -#cmakedefine HAVE_STDDEF_H @HAVE_STDDEF_H@ -#cmakedefine HAVE_STDLIB_H @HAVE_STDLIB_H@ -#cmakedefine HAVE_STRING_H @HAVE_STRING_H@ -#cmakedefine HAVE_FLOOR @HAVE_FLOOR@ -#cmakedefine HAVE_MEMSET @HAVE_MEMSET@ -#cmakedefine HAVE_POW @HAVE_POW@ -#cmakedefine HAVE_SQRT @HAVE_SQRT@ -#endif diff --git a/freedv/branches/1.2/cmake/soxconfig.h.in b/freedv/branches/1.2/cmake/soxconfig.h.in deleted file mode 100644 index fb38608e..00000000 --- a/freedv/branches/1.2/cmake/soxconfig.h.in +++ /dev/null @@ -1,16 +0,0 @@ -#define PACKAGE_VERSION "14.4.2" - -#cmakedefine HAVE_BYTESWAP_H 1 -#cmakedefine HAVE_FMEMOPEN 1 -#cmakedefine HAVE_FSEEKO 1 -#cmakedefine HAVE__FSEEKOI64 1 -#cmakedefine HAVE_LTDL_H 1 -#cmakedefine HAVE_MAGIC 1 -#cmakedefine HAVE_POPEN 1 -#cmakedefine HAVE_STDINT_H 1 -#cmakedefine HAVE_INTTYPES_H 1 -#cmakedefine HAVE_STRCASECMP 1 -#cmakedefine HAVE_STRINGS_H 1 -#cmakedefine HAVE_SYS_STAT_H 1 -#cmakedefine HAVE_SYS_TYPES_H 1 -#cmakedefine HAVE_VSNPRINTF 1 diff --git a/freedv/branches/1.2/cmake/version.h.in b/freedv/branches/1.2/cmake/version.h.in deleted file mode 100644 index 43b3b7a8..00000000 --- a/freedv/branches/1.2/cmake/version.h.in +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef FREEDV_VER_DOT_H -#define FREEDV_VER_DOT_H 1 - -#define FREEDV_VERSION_MAJOR @FREEDV_VERSION_MAJOR@ -#define FREEDV_VERSION_MINOR @FREEDV_VERSION_MINOR@ -#define FREEDV_VERSION_PATCH @FREEDV_VERSION_PATCH@ -#define FREEDV_VERSION_SUFFIX "@FREEDV_VERSION_SUFFIX@" - -#define FREEDV_VERSION "@FREEDV_VERSION_STRING@" - -#endif //FREEDV_VER_DOT_H diff --git a/freedv/branches/1.2/contrib/CMakeLists.txt b/freedv/branches/1.2/contrib/CMakeLists.txt deleted file mode 100644 index 3f4b7e02..00000000 --- a/freedv/branches/1.2/contrib/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Install icons if we're on most *nix systems. -if(UNIX AND NOT APPLE) - set(ICON_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor - CACHE PATH "Prefix to use for installing icons.") - install(FILES freedv48x48.png - DESTINATION ${ICON_INSTALL_PREFIX}/48x48/apps - RENAME freedv.png) - install(FILES freedv64x64.png - DESTINATION ${ICON_INSTALL_PREFIX}/64x64/apps - RENAME freedv.png) - install(FILES freedv128x128.png - DESTINATION ${ICON_INSTALL_PREFIX}/128x128/apps - RENAME freedv.png) - install(FILES freedv256x256.png - DESTINATION ${ICON_INSTALL_PREFIX}/256x256/apps - RENAME freedv.png) - - set(DESKTOP_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/applications - CACHE PATH "Location to install desktop files.") - install(FILES freedv.desktop - DESTINATION ${DESKTOP_INSTALL_DIR}) -endif(UNIX AND NOT APPLE) diff --git a/freedv/branches/1.2/contrib/LICENSE b/freedv/branches/1.2/contrib/LICENSE deleted file mode 100644 index dc8853a7..00000000 --- a/freedv/branches/1.2/contrib/LICENSE +++ /dev/null @@ -1,393 +0,0 @@ -Attribution 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - d. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - g. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - i. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's - License You apply must not prevent recipients of the Adapted - Material from complying with this Public License. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public licenses. -Notwithstanding, Creative Commons may elect to apply one of its public -licenses to material it publishes and in those instances will be -considered the "Licensor." Except for the limited purpose of indicating -that material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the public -licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/freedv/branches/1.2/contrib/freedv.desktop b/freedv/branches/1.2/contrib/freedv.desktop deleted file mode 100644 index 96e82931..00000000 --- a/freedv/branches/1.2/contrib/freedv.desktop +++ /dev/null @@ -1,8 +0,0 @@ -[Desktop Entry] -Version=1.0 -Name=FreeDV -Exec=freedv -Icon=freedv -Type=Application -Terminal=false -Categories=GTK;GNOME;AudioVideo;Audio;HamRadio; diff --git a/freedv/branches/1.2/contrib/freedv.ico b/freedv/branches/1.2/contrib/freedv.ico deleted file mode 100644 index e6b9a2087ddae6e530734e7293da08b1d12d4dd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364646 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 B>KOn4 diff --git a/freedv/branches/1.2/contrib/freedv64x64.png b/freedv/branches/1.2/contrib/freedv64x64.png deleted file mode 100644 index eb89773bbcd93a4ed507271bd728734d0a5d7f4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6289 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

dh=(PosX@Z;=^IUxT@l?9i6IK zd)r=%i{!V7J>NriYUKD=O>RppZsQK9G8?j~kqK)HX($~iISLCMjLd(>u-;p9Cyy!P zgu^XtuL-G&M%zR>n}_ZcE}FeeF-_v_EaJVj}J>Ygq>#&|o z=iy_|4Spf9wWJV{G$5xlah`f z7^3W7|LX?WK&*T1+?t8TJFb z_hfv?!a?yh7Zq|(wSsW-8>T12H+QBN3Qybz3fptct7XOB3Qy2H{3ttbcf5u*Tf$em zv`cPR$(^@!#dK;u$X%1>wq3m`rh{W4SsB{4!LI5O&hId-E#j{FCXW2OfHG&OPO5M+ zm>1v|W#MOyeE)O6nHAu@4)*Wf#EShq!IJ8pqHV@nizC1af4^?7kQ6>D=pGlFAU`^% zeA-}fr9t$LP*lrP!0r^DZ~w!l`ngNyy(Qe1(wOaI-WU|x^7X8M=K)QumUha`;;F#X z+~vbQt?hP%;eHJ%1+GY+ax&kOG*Ol}f9UViZi?I(Enx&fixj~l`|UZ#Vc)nYa;A+Y z!wCTT#DruwX#%<8zD??4F0lf>{G5L|S{ory}EhK>FF_k{7XEi)`@B5#pe54?A2J`*=zd$IY zMiNdMijQ_hJ@>1*;s4>MLPB0E36FDP@E zL}Rnp`BEXuFOVdR20PD8gCD;1?|l5q+fQq5JbdDeXgad}&&ZqYYA zD}mz?l${lQn@ClkYn<{8DixTq8ZPac55OCs`24zXNk_sLLCE8YIGtAy!>ueUDYZxU zq63%VFLUpe^Ia*BQ#xIl0UtFcPZp-u-^DVU}ktk z(FQb#I?AwKXke<+I_UL;f!)Rx2i9VXfybm`yX9t2sV>CwX=T*R9Pzw6bSz8d0DdsK zqYBCzaoM^TXe|#Z*J(TUk(teptrXp(;k{tMAi(>>F; zjiA*qOhdJsK>;pTi=^b9bGI#j8CY#kBLDT4L%Q=Bs_l~ZUt4bAj=c$j+2~Ul93=;~1y{fOQ8pV^4Pf2Q{HV7q^N&7rl0M%sq9E=tki$ z25ZKTE!=9Uwy)wOOyaPS-|^GKLw)V}sFPKnad>XCl{zW;`4dYlds$T(O3k9Z+7g3`M z_^e|;HN?gpcy!JS2L4(#B}{%$YZxETXr_#&r{ir}V?G+B#>H0pX6CZD(=o@FbGLy)W9bG8S*1 z`q4L(33Bz$^6Z}0;}C&LsP3B0v@+Pqq7LI2cqj@F0YzReM%IstXSP5!J}i!ve6btmy2EWga8dAr)%JIxRGYZC*-86dwJk+qO{X&`Wk2_~`1;&nz_4qJZ~*A9$pYSDWtVfO6HH z1ts6MBZjA61d>UfY#FQje}YG&uGL%g5LZ^T?Kn@|wNhI5j2_Zz@sZi0k6XZe`FL;d zxuT;&N$Hz0HMy*Fi>hK9Zf*pJ{<+d#f|hv96<&DiV9DRW!eOau20zcMUBOVnP<^E^ zDH;fuUa3?|es4)j;5Hym@N7*71OmfB_Yx_o7fVOE?V z&9w5W3n!(TGkmff|6?|^#QG;D9?I~=?UfO^b20^@76`VZzMxnMiE!pd@~;_23Uk3i2Nu`#OYT1}{tKkV_ySb*j!joY z>Zb06XTJ2kjXOm(Fjo z+X6FG0;!d`Ef`Nau;pgVwBeaic$I>cJ}ZXTu6E40oBia>qmgotSLgA1a!HsQ>gu(9 zpH^?khC7B$bb4KMR8$A#S|68-p}6i*lL~tGHWVbEHaZJqy`!jnhi)-lVKt9xDZe}P zlgSSEh;e`M{wcf$=7kCEN&L0v{PzUEFuhVsEG8|t+aJD$nY1G_v(-OoM{xvY`XGvq z*=*`x8NsGsY7+4j$2g_qA*DI8cTv@X@`ZC>uRa79ob2N8pU%|UX~AmWN3UHVQnM5( za|AI4MJQzK#iMK9zs}@r;c}p|0B-&XZtmuM$TeYoZrh^E)MEBX4@x?$tjBxLzCB?E5B{CFN0|>+n(o*jE-B)d42!7emDw z??uNqcMR~#KT%pK^*&}|wT=k`;WtW}u3Js`H@hMCZf9pi9%Us}dwB;ukh}%Z z{jyrWHu*aKoHfd=*L`e-jRNDR!Fp!F0s3dn(K`%ow6XSL;`J*#XKYT#XS=<*M@k0I zb9IyF1AH!(51u>v>CSrz>4H3Y&V8sgBO4Rfiab<~q#&EDg;}7o_pchdy<8?fUD-b$ zfI#$yny=Y6@*p%T(^C{m`?8VBY^Mv%ca(-rGI!nP1${ozgg8)K%D`(1IJ22;M{d!o%) zopHF8?GL&Ue(XZ%I66fFRC?6E!L$!&`PI^q)pLzEjG5j?iUQO3wAT1B4b(C9;3rKL z2F3>%RL|4_b=Bp8lNx*Mkz2$u@Hqpa>0{zAcp2!h0L1^y`jwPs8lbS zIGV1+qs$9ZKL}dCbXyz{dLGkqa5qgC`2e1JtCK0hguXM3HPlJi;V=I zKb;eQ+TS0eT+W8}Z)o`y(O;}xtUCLe%abkAhj~8AMT|@dZqCPI_FwiYhHP|J(&CC- z>n5E{PnVx`?7t5}2~!CGI&A*XxK#+(2EQP>8aR0wsAD?D9&Kg+MQu^*`r}#`=d@wL zStN?yi;b1qlsw{*wdoY@t|Qd*gQwurFneab-l^|`oKVP;Wb@;`Ah)#Bf1U?&g#YV# zaA?yWMFC62rn0wN-;!(bo$fp@R7=_pT+l{HS3>L5d7bi&wcg?laVLE;4DZ@{W7>vV zTshm|^(O7X8@NE~dua4g>#+Kv{$vmtpg;nRBA;@@{z1(b3;;xs7wd7+dwHnT|2MOJ z;{L@+701-rSj!2ur{E2?bY=tvUW2sLg1fFK_loY?7ST)I&>% zmHN(@`oHOSvI!=$_{^6H?O&_ptgEQXewDT?QNW}f5*qneF#WUIlvo}_M?9C4es42n zi*W$u5(rnF;5?i#9ZVpJ2WFw)4MHuOz=Lf-a`ft<*9R0U-BQnOa6JLCN?wIMMjG-}l0Cz?>i>+dsPZ9UyX! z;+l%p#?JCi#qO8mA!=Qn&kw{|Uvll&2bM!kc2*9W*apVzPM##oPp!+92dcwpW${KE z9$?EHH7hgoz_3hwY7U@M$%|# zX{pfOuG%*OD?~0NocT2=s@eJWFCMi2-0jwA@R2N~}6(+d&;}_|!oYc=QeC2}oc!tj` ze)!DHTfe!V1_jT}7Y7sm_^r&_)#i|*HautGx!$%e#aJ6ylOOLGVHb! z3&GOei(>W|P3Auu17_(z8l%C&{EDG^ z-U@x1Nl9rO0R`QRmGL&O!FsXul%QiNE2!>QF(P+qYiDDIhPiNRQ>k9rEJok2+9(GL z!eHBo=^269POp11vKPJp7vKw6>r?mXzCJaicSs`HI1>Szv%+c!8)(TQ5hOqNC4Z2c zG&;|Z%+1dmuXf3jFcs5K5k3PyKZuCK3{6O+_Y_7smxyrkfAt(fEq3`tXp1&m$qfU$ z6L(zX(U}UvTrXg$d4e9!rJ`KV9mF1$8$r-?Jyc+rF1Q(W-gac8ybCl!{p}0@w1>)C znh&`zGK5qEn|xdbe_gfV;@2uZXLCpQ@)469(XZIR2|00ZiL{=@j_X73BN$4~Ik4Wq zjmL+pL#m296!8s9$U46qr2Bjqwb`%V8p^CbIICNl*UOe#vDB@qE>BWPEJ@_Wkkc2T zF9jNHkoj7%ItiKELw&sX+h+2zYvpq{i$nTKU3Ll(q8e*PRc2#=wkA zeMp|0<0+z}2g2`1E2|GfRTXlm;=&IiW{!%mo4p1pR3?`SGOZ||tKuqCr~tRC)F4&p z`!Lz1M;jR6DXT4XRZ?3j<8V*!5N}6t8%(9;De$U31VFX?F)eJp`U6}ZGpBx zh+-~Nocctda-de(lihWaNioO3Fxh#gve6?Raj;`&o5+}|hI3eQW61?4C!72BM(2@e&Mt}mBKR05%EnwU_X+9uHG%PLxJtGDiw zq}<|-Hw1b>vzR(X>UT5p0l z=zxkpkao$Pa?Sx(z%_pJ9pDuR($~WsDV8uzrDCl-mDl902#-uN4>_JnFoIJvu{s;w z4(kX_dS4EIm|2Ezt9%4ws)gmOYMC>1-C`g~052`g!+?nqCnX=h zZT+J@HI%Gu7$DUEO{Lf8o)$x$ED^0t<676xcPT5qOlt&DAGCBu@0)DWfGB{L^`pHC zlJrUAvz&~1syj>!OWt2p8j~K+6Ti24M2pS2EaVvFcP9>rt5V4~F8Lnz=bEPBJFedlOd-hQ~ zZEb}gTBCm0+U3V=DL>GV`qC==Z;&N+t^E}JDj60YmKy(}yaTsP^KChreSGz&Q?<^Y zjyzN-l!;;J`5eOoL9mIs%I?|qS9am)Q#S*k-vlz>U%n>Of6?Y6NrEZn$b$gM2xvunAv0n=0Y)q-1?e6Zq(PSi z&ZaWs)OZLTIlzD31awnb3i>wJyw3*uvmt2=HJD%vhAxjD<%g~v&&z51LFGF(12M&Q zq-yvfE1nI5$^_<(gE67CA!ebSMab-@{lQ=0WJA0kc<;`veCFT>vk+O2mm6vug%I~6 zJ*)T2O?|~T<)F*(4;oFgt`2QlVfDwdy<&YO*by{d+-_1d6C5v%#r_WB;^W0Ox?H8Y zigx2DfX^W`Y^6EEzwao&s0aff&1R1UQ{<$G)*9d8>(xxRv9Cu_v#P@5wKiVu{ia7K ziCNe6*7fq$PaOIc^ARCM!88*@fTQkzMIwT9eOw?lH8~zh42+V=$cFu%Q;IT!g{qi8 z?C$RU^V(bz@t_@q157)~1D5`WE07&!l$dNVGrvwMs4Fv#fwS*ik|3ZbzQ{JYQlCZJ`{S( zS(oglPMna`IQj4~N?{NxJS>aeTYvrphpi0avy~$0&Fkn_y6CziZb3L;&Hlvq_@Ua8 z>}!L%tZk|SK`JAuZewf~qoyT{^(1t0Yf0c9NYqf_@@X$o^Zg!o8%u(NZsB7ySf1Jb zI;Jvs;qz4Umy;)#|E>Ty`+E~ayh>Z&=~znais-A?AALo3wXx7W)E70do13pz$_zT{ z%36QTE!SEz(C>#eHDi}(9=<~q|3TLpAhK^X=x|J|%mzDqXGx1nS7(|zj5r3O)4{-AGs%xrEv3X7XJ6~VTiJFHZ?4c6`wyataw zUJJKVc70y(tn0|XQxfAN8hP=Jq4%tpi=SKnrGUvMG_2|nDryz{r@c2gmsTe9o2ia( z&_4LQJ4hT}eFU{hx^?O)9A$)*-X!CInN3duUF^>?)%6%#nR#NyMm;jGcV^ir?2uJ* z+01u8$+%PMyT3D(+sveIv$~qoh7Ew*r`mP|9=7R_n{$XgHjSfmS>Hyi#iVMfk7l^^ z{uR){y2zmPCo%4>{_^&3tTkG+@D~^93 zo}?MabQSu0ajG!a=G2%uE<)g3>O5@D0O)21HSlAD786MmVCaa6l5_aqtZ+z;s?{fG zHQi~GjDCB(G?`a?*m8*UyK+X3_T!;Tt{rbbdkUYQEi0ZFuCO>CB_RDmQ-hO5Xx2WX zR@l$L^j|@PU4vSBUalZwEKruF7F6c!aw-Ial}b&4|Xrc_s*bPivfoE$=@Uzt35Fk|LhB z1&J5G%8`~p-)rC~_!l+_r=v0pzy1|ZavABMtQz=6`7T4tzkNje_6dG2m9%|kL2Wu? zgXeXr=%6oV!(qst7XOxheq=*T4uT|`HwZ9*KcY~jZOr+IFs&@qG87+=xh>DO zTnk?8&WColn|o_%ArGkMHXtcTocEcy@{fa>gRg~LW6WAnv-=Y(Nj-3`4&$mCfiAvo zKB&3zb?n^{_o~;)_FN^h8uKHC!ooCo5RWlNXN1(Q+T#)Qkpfj}z(=U56nWwU9+YE+ zjRxC^HmyShunh|?87>k>BSyu%oQNS((217MS|7KXMaXb!p$c$Wf>GM%OQ7ul0Gre= zm!6a-I$6wqR;q-i#_Bw{j7^Rgv$v<)OTnWk$Xz{komRKfE8c-}w0tD+72Cc-^wE7z zhzxooNw<>f#@m5bJIy)ub$0c&Xywr_5j0(SM&KK-AE7P#J0Qj3wZJ1ZJ0Rz4uSH+) z@1IOheOAS{)>S>Qur1q;iqxTZx3qFX_G{v^>^D2}E>+fj{Dyxay7_M2v=EG)A&hRp z`C2X4e=?nEBGXW5KzJPdGQB{)UqG~AbP^(_Ph>V+dvI$Iv<|z!FkJnyc5x&eQub!~ z_xXLYunP)+0V%K$iY6CC(vYB|RGL^jI%wa*&SB9brTC)HRcT? zP5N4a5pb!c;+?B1W1jt3s3jJaxF}Hmc=2cC&}L_$W za8;cI`m?U3t1*z)iHCo$u}M6s#!Ctg^>aIDUx1&vFJxT$@@>2L=2rQz z8RkSjmwVyq#0C%9^-CA7wkn!b|IEhm|IA%2T>W_c_*E5(in8FO>%zuPJXWh0SBt8} z0I?S!fLMB0ojq(Dmw@E5WX;V~kC?RhPG{K|&Q&~%`p&Yq0r*kOj&tCB#}-*SUMWPl zesKg9EMMS~)pjRKGT~>Y@J}ZGPpr+{2spVl9~d0yfe);}9h^;2`dSCLB%*;|PR16$ z%SzfcWNC0II0gNS2E#_Mf8<>)XZfQp`Nxh*H{e5RguXu3{CKa~)fxTNAzYxam4Pa3 zy0UPRU9B*pG5APSg;-@PKI3>|g#AOB(fGiVr)~S%C1AWvQyZGcBwFwW3=%dvR;=Dz z1PSYm_4;R7v1{wkvC=_pWU=1Kja#L;_lo=(Q`Pm9AVK@tU?UyuzVhidNxh@_FHhTr9q=aaCWp2anI6-mEY|g8ukpYF}S!qb}=t+}J5m4fpaZ z&KqeG-d}ABqfN0=(!RCR-s!_5hXtXzhWypj@FL~he#_T$apH(LCr|t~%F5cMA=ZJJ zt?um3W3BwF83mut7U_AfqQ!Fq6Hixi4^kf1^@xO;nc<;Ad(!Q}Rs`dD38aa0;mXG? zab0K2JO{!XneYG~rPIZe!}Fm7xM^zpk`jHe;n;5ayrC$5sfL&UcN0Kw2!|if~G7ibC^=v7L4!(_pH?Rh2fGD$=%jzp)-aU`a_Y44;CMtN zev8IoW<=dQwf1j%$q2PvBSUuiFxK8EVPasM-s6wXCH+Va!Bj`5z5KD{Mj*2&OU zBm+~YW?G|!K6L7J;cs}ov(G2%1v3zlnndQ!94e@lDX}H^5eVe1I->DcqvH8gx0cH* z_aK6~r1;(5roa}*k@k{dlQVzcmaEi@A2h@ zBpMdDTA&Dow2#&b7-gF;r(xhIUOlFCLwD2zRnFwDBD}$EHp2FgNAuZYTIOo^gIr53 z=wvO2+iuoS+wJ5mX6_bTRW>#rF>no)g2ej`=~lQegSiq16paLpVwK!cJqsSg6oqU| zD4EGW0XN(&yHrAgQHfXdJT|hX6^`nRn|r~$~eL_H-+kx1L6&7;C!tIS`!SSC#q9nAD*S-sVq^35^=e9-p> z79(Smn1qyf3d7J`M&t){4K%u{z3m-cbxhE^quHhq2JGR7MB;6?-8e#n%R{^p#odl% zrqI>VUy63HvL-&G5f^J%6e=e}*Rt^2NChs5a2C~R%hAXaD=AHUU@8yW)9<$z`^w)p z0^)ZUMm5fLrfxlrnj@Dnb*-!{vxWd3G3!Gj$c~;{;V}@3HX@lNR5pl0RPWuh z_o_rbRn{QmP9&)ZCcDm^iEw8Rs;S+P7j?{i53=0NgBnqjJTfJVe3AghwUsF7x16LRV{xs!ODs(O0G=P4`>KB)5? z(|j+>*t)&FV0GYn)DJ>kTz{MfTRlReLuwSAoJ#NUHv-Zl>!xSEu)B;}N*q>M6rbY) zSb^QtGkW#_s6gozXmEjRh$wCt^q@BEE6ukMK7UrG-4lLmk%67h^7V9Nkyx4i!uD!E zA;PJ%5AVQmB;DrLZUv#p|HqH%aOj*M<@mi0JA#PIO7pAP;bc%)>W|>5GD;*u){qto zjT%s^+0t_nR6O>{C)=Ay0dLm_<|z#!?@cNl=dA9L9WJ@2`GskQ;(=UEYqHlfC(Cfc ztIRx+TIHm9t`xk?-h(Ygq)5`|)rZA4>VyQdn<`c#82Qg%WJmSYzo7%f3t&EviZua3 zr|3rmvMz~;^&MW}%5Q4|XXnkand7$aq6P;MX3Df9`;LBreLs}SR=Dw1A1DE?&{9mz z?5|K!* z(gCU6u7IG*K5&tM>+;5phR~f&_YWSmUp&;{6!s-tE5mhu+>W9{xY*WCxB8Q+h?$W& zeC1fXY$>C}ihfW*C?w1ggYaT{*fbQ@qCWVBZg;is~* zTMC9Y=lKdXXBct(rPjY=vu4DY@N>3xn9`1faF?9dsKK+5DN44!2N}=D5%A|lZu=@c zUe^!dv^u{xkOI|uMDhcRo4Mx*c7-#8J?B0uAg0;2v~R;P z_oHZ1dRGDAycc!krzi@Uc^o1{6H7u>?eC$Ecb*QIs}^X6HbV}yod@m_0rUV)P)Emxn8bg5+}0y@wDwSV zyQg%EldNHFw4aSh3#=Obw=gPh(SZ8*5Wu3-{aGDSiqyhSG*Gt1?4LEcK?&aynJ6jXte2Md-evP$lVTUD@VzF=1z-BB2NYI|$rJh-fL1 z=V(1TPDM=gTmhFIQFidwEg?e$6G}#7jQ+k55e3h=3X9=SpidP@ebp!Gz~HRh6yF`g zY(Mmy1TFNswNcqiZmTuk9yaT2bL2-1#9q`M5u0HP(^H%BPU>m39fDD8essZ!Rp z@Tz&ENLdLEx7MMIMKTR6>UF^2O|oM@)$OLb3c~5U>@TII1b$Xjyia|26P_^$tjb_I zA-yR8n3NEsRm`qxJiBtFI`M|hg(t=c8G9fC#7;pj4CpHOcakI?apzD;OP|erY8IzlA4YIb2)Y}>6da&U(W>C4c7uWc~^^qZLS(qBx{UwWVh@e0f89O!K;H8 zYdem218JSO3@?o;mShm@UVBf;aw3-t-hQyn$ED(tQC=7vL}`5zH;5oqp=DhiNETOU zT6d{a_vjnB{$(-`^OApy)}h85e7Dfjt2s#4{+}?!B*St>j^evN&Ygm8JV$|MX$t;o zz;T~~Hb;2*%s4gVr$zr~z~f~T1-dwrzctfA;}QnZN=q*aqp)UQx1b61H6=OErS#_d zidJq@2TM~sjx2ABT%q3vY`AQnT#Kx!SxNll?9Rr3T#_GwB73@M>m@srFPh(qnUUa$ zrq~ajM-}g#1r4fSMWC-Yw1*|}_tose8%XC*&*jhB`^;-h6^<@*v?X6h0os5>KhJM# zk28fi;zJt-bbjUL8Fdb4-SI_r>?wZuR6O--8!ZYxnZ{%u>ZHIMc}J9);JNViUS8dI zVhi~Nh8fbyiaZ#MgzGc+FL`#mdutYV1QWfFv+%5110%kW>K!S%inN2tq9+iu(s2<2 z6nKgCdab@!upsfeYjWL2N1pQ2h5++O3aRzXHF#(cUkL8P-7pBpr5yZ+$2fdc5AM;NZQN5OaruV zA|Iwu?scG(8eFXXD5eQjnWJ5a0aU|NO%Bk*x?yNIncUW+1$8-*7joS4 zO{;Ql^0HL_9TQ;KiM^!<4nKJy!@46ZpelW%j9bQiBUw1*^IuNOeY4^}Vtu#wl9`eX zK1hr|7%S%~^8~8({7AZ*66Kz*Cr4!eyo-7(Hr#%`bXLnHz0l~Y0CA}LTF))snq2+f zq0!rh%eN`6;~$UZc@Oq*NbQjj^J|YtP80S__H~NgR0q6S@euXh_k9BM<-2%~ndb3Jb@7;Z5K^#d z7HmI+?K+`{gv@7uw-fajyYOz8<@Y%$-3K|_jmVp`3h(PH)vUlB`Z*uY7oGu$HJ>RE1?w7sj3e8aRrHoX*QoNJ7SYsP{bK_q~M6_a!^#S;d1Od zGtEdk=36O%nxHFt{RLglIzR#hTCpeNk)ue|Lnl0W9 z(nALMladh~QFva2YuoCZYQrQA4~^lx&c}`l65cEa{+>*hLgW$%7LH=kl*U=dhpdvG z{i%}u`ieFbxw`WG1?x31esbm+-b7zqqGN*4uHy8+x4V;zxaGT0;-nC1AT|a#p|UC# zt9Y-Ao1+BdoD9x>LTsO3b3DQtKjw!Xz4N9UC zeiaLDb$LPe&cIT${a%b-wDZjBOr1Q3(mBqPdZR^DYIb^NzvT%g4kH`)<-?Yr55_I- zE^TVd#$nlvA9c#_m4i2XrUg@4RbVv3?z?T)%95dsSjL~)Uj9Kj_oH??$GKqsmoA28 z_aQU3xXj-(JBYjt;ldS7IyN-(O^0^|8%{{&^XUsOKM{!TPf)T!zcdh0egy`Ac^7rCQO?koPhKl&`OsNbo!ja~zDrjhA>0 zwQP93;+l?Fyf3rJA#?lz?C7H$OQE2bMiCRC%LJ4!pdC0S$mPNBb7H|OfmSQ};po7L zA@yG;MwiJbqN5pE|K)orxxLi<_w4*Di>H#}@K;vu=-^a0VN;UUbVAVbq?UDyGvtvH zs~lQRlAc$>OV>!^TEs7jN0)I2t89aZGds%fjyJBk8KQG~0O+Dl`z;ddrJXL0kbDw{ zzIivlXwCRzlu)H5Fm7Py8VX+;9!~U+%3jc;6XEE2{ELuvqznDgsfgc8LeYd$PaaJ# z-f;-$$nI)Dj(XW8X|=t>pv~u@gF?epy1H=DAn|}q)Zeg~k@JiEOP4(u4}sV*G`aZi z+_}^i29(ptmBxWaQ}&Pvd@VGw7%#rHaAB?%uO;UrdzqU@xzn9pybY8DLqc%2jueww-j|c=Rh%W!sKSVRSamig z>s^+GQMA}fgEFk#Qgm{>Tbgy)K?sGrB7L=56i~q_4b%6!G$1mp>cCP1VX)$P^ z7zzkOLEfwEUf3qwCK|MJ8#3190r&$8tHq|}A|;}UiqzZvn{pi1Z`FuDpNLm&B!x!+ zoN}*vm^pB>nm7@dZEjNSO#M>(Cg7K}JyAc_j#x~`uv2k08gLb_m z@)VxuIw@4~2wYH^&yt0U_k~9uK{w;bvtI`HaRqC-_7Zijnf@rr0B4cFvJeosF6ywQ zwi8*6N{J3D)K)en>`QK>o<|jaRK1F!W2LQ;r2Wo-YGW)XbN0kl(UEi&*7NIVEIK)U zUSAKG&)Cq!b3;vwyIekOxwxKgRl5OS^Cj0)|DRVlcHvs{U{h~Ebj&1kbBgp)FQZ_T ztWq_gm`QSw{L<}k95?c4A~~2^hiK`NJk8YWf>v9UL4~aw7-)v_4Kev|KP^*N)%Y<- zb!gFSJ>YD1W`EyL)_IqJ9Kvnz9t}rSaed5KCaS~x@%xBC5?K=POwL7!*?cFXhSM321*wNxez36SzN5>q)I3x$xf(P?=UyE#K0ZQ?-*SmyH!s$8uB zxIFvw>!TS5RYZg~LGvZO5~tFS%P6Rw<*Xiy=Yc>`5x)`1UVp7%T6Y&-)-AfL8vTPy zUjgu0Hq?Se@742#-xX>e?Ff%@GY}W z@2{BSUM7a-zw^m|h2Bo6A+0dQiC^yqQtQd%XXATCIgzPmdt&{NVm}nl#Muk(i~WZY za@p5(R%=r$%r2*~?lrj&-qlC0)xZWnddR)&jkfxKVKeQ6?5XO7q_lzUtH77%h{bBx zt({p)FDLBaor=Af=xA(Abc*G+DVu9|lIR;mcw>j=2S)fyB}bUH6RydhzxDniE;1R# zPM(;TYEA5(iJ8vcrMwjWsV~Wq)<8!tC{mI8qLNI9nEXS=z(PcG+7SKaOjZf;{YkeA zKK0wE?4P_*X5-(apv=_Y9?1Y0dU_*?JdF8TUVaSw51Z}Q^JUnH{!e8EgDBTiG$4bE z`8RC&^M@9-SC0LvCV9*&Arg4p+H*qUZ zliR`lG_NBpwaXY53dcw5+I(sBltoX?=Vy?=F}*yNKj^xx%bOJE`FAKC&%-nZge@gQ zqQe+&HGXdrS!@`-&%KiPWAs~;|*0PQT8Bsy~Mt)?B1~%=G)L(R}Pa=Xzjfb>_uiJ?sADDkfRJ;QRw(%W~#i z6W={W>F8|PI_xN0=u3{^x zkP0nj&ese5A_j-cfuy|*VBOD3a+kAffKU`TGp}2|WC{+f3ZJZRNZLGex&9r6$N6gk zp(U}hbI$x`*i$5ivmR8NQEYg4{!%Vf^3{$u@?eeAh|YtGoO&qo5&PrrW}DWX;-j^6 zbfI$PjPEmRQ_j;f=ff0kOSO36Y1W@Ozv_8+XV}NRHFuBt!2qb`-&#~*nadEG8#%mn z=XBwK$&c8_CpfDGFG1Eg1>3rMVgee`CH2lnKw`x7frO(6*>4d7fkV&Yr6%@->mvar z6q!jB)qJm|D>KPuqn&|oN@-8MjI3|zDn<4v@?F@ALR8IPzEqYcsKm;4Z@h?RfMlQ0 zyyQHhEMF^P9%ZI;W}CVs(z0!O!{3|J+%MYvhND45sBLKkIdKwC4|4w2jheIR*Fq# zqv>4Ni}zI0xDr&{m=}8<^mdqn1zb1Zbbij0W8`c^#worKaLp~TQ+-VTfC)tqD)DAC z`sjlIVh?k`O<#cEoA8>!?hL0M8=&I$$0|@EA{y-DB0^+gj$EY*phCI}c|L0sgO|RF zSTyL=k`;od`a`B+Vp_*~RFmZSI2B|Nk-{bunB9j)<<;@ffavWz2bLP(K0-Z{9v$Mv z3wL2}jZDQMxfz$mk_l=baLn`P?`B5&)B4OHqo83Ot^gG)p-9`wBB!%Bis4r}eVL+a z<#uzX4M@aGZAVbxCC9w4lWRRg+3cs=l%j?(u-jf>_OX6orADS8w0C z5IhUnlq~VVFE3t86EF2dGv+$BD-o;@Tz@}qEQdGxRsG|qkHJX@pj`^#fRNV+|+ZEh4b(J zS<3f{OhO-p7j)XDT-GUJk9lBo{<;|e7=BQ8#shp7Y&*)tZaY8-8O_4%Yo~TAT z;j*Dr=SB2me$FNE5gnR7B+haGwASZH2t|TIi$(mx=+YMEyq~r3Jq`J_0|i}5}1|$ z<9c$<}v{s?#$mP^BQ?sjZ>m~+}0iC#0WP=P7h zz{TJL+WN#(7lHD5!AL>ftEiPZ@or`Rp2s7uNB)#TKzc+lsZ{Q_x;g7wv{>Q367iKU))Q zcuKrlV=Tq6^kuv_<_2}wBB&TLPeV*nbr-b zt2mHTB=U@`_3^!SM(30`xbLnV9melO>{~<3axr_Ntpn#$7Iy~vvojAlj@km$6zh#8 z?BeIc`x9%^)~6n0q~EU)bjYI%o+mG!y*9<%^?D$9z4)e%^_rO>{LlIa7X6?4=f*Rf zXH+;J52T);w&yPkC=pGn;!3VqtIVzS)*7Gr_!Yds3=@qg_Y(BeCOd+=Gnb$J zx~vhFgsJPCjZN({Fh83~Zzf}orr_gv$p7^T+ygwm}q3?1<=u7D1BVh4`05ivZid4eiFRo?+1rA%HUdv67 zllss`#+Ox^cMWvEm9l!ZYThe5*!S*LP=(=tgCWg9TL$xaO)S`*(i$!fV)-!Xe00ND zyQrVd0;kGHeMx(XFlAGa{oq>39Hx1zxCh^5o%ZPXxL+|UyXPX$fE&F>A5h@7+(XZC zx5#vSS|#Cdx0*fw%cxa`(EeGBki#2H+7=|5nl;J$HmZ7ip=y9g=XdiB9 zU2#>+fCVZKZ~o}5aR7;lfu-MB5h>Y-%j+h0h>z&Qi826$-6A$O zJ1z4yE|!%GfY?)fnpRkj8>OTdS1kFShSV5c@$#=Wruc;L-s>@Dlx5J+B zUVKu$p-Gs%dC>HG|@=eTPA!L zH-5gQ-Wt&twGKDG$Yv0v?LXq{gHOn+8VSvswox|Qs2Z(2Gu0|U+dgpV>gr+Q6|H_A zr|Kmxz%lZ-j80ibkXpHaj6RufYb(InAh+!<>SCE3U(4RwV%Iqp33Laiwrq1E+p+eL zp;+_Yu}kgOk)Z@IxvJnyl)}dpzc@YY;|ERUxMGH0+F_54-$SETCVPpQc{Js0&w@+v zxc&EnhTg+xzhl}tu0~u;PpR>)lKR#sCdvi#P%2Ka1>a!dsTf3 z9p9iP1<8*54;V>XmO507GHBjNf2e;~m|%gSVPD!s;@L}Bj7qCMAOpzI=q#Rsbf*SC zFP`gDESBuAxG!ku0o4$uRqCQA5eAdWiA{Zb$wETowS7+~ti6F);#Rm%j&CBpX6RX2SzCAhxJ7@C(yUMXT70dmLIg@(ts(-aW17s` ztQ#6BZ|=hQUT5;dHBULRyxl{uJ@|bm?bo=LVcsei$Cm}X;*@jz*sy`0)?Wh4t`%3C zC6RsGd(8{IosNFdn6}4Ft_2Af54|r371odl^gYQ-Q(e8-!RO1bzP;vS#ah*C91sN| zTM8!U59Z3f7b{~wY@bj6yyM%dIHSITOx^FbrKV=)frE%?k<>8XF67yAbtc;Hj5jyR zo;Mb?L!UIt<73!vkD_T|A-!;Rewf~Fonb#}X(a#&Slm1)2d6~)1Y}md5F>dJb7M)cm(0KbXL)xlAjxa3#xxH!@>?cNvTXJ%Kjw0X z7!Lu5vr*c!dVDfeV3_>?iSb|l<*?h(V&h_pnhWZ^Ev#|y>ZOH+XgFElr-*#mZi?5c zM9QWUqzVHa6@e5%G-;wB%ov5?ks}2#C|k zo2-ihiDh-+$vN}Wrj7I7z6yY&`*ftEy`oY~9NtD=IC{0~>G(LZgKJs>$fdY{{i%-; zNstETC;^w#WN;@LoU3E)l(*w?J&Iv#uoeweXlOKfa=X^t{Bv$$u4Pr~{2pWvE?8Tf zS7tY|Ud%a{(NKVA9SWK<+qV>I-qdFdR#f1l{KSCWW|Q?JSlb+uiq2INpq!S};X4vX zJh~SURPi$Drf8sy_mg5-k$9(pa&o3r29o2J)X565Z41?sS|8=rS3JunR`g4>`O1}I zjk||iJv~so@v`EuX5;E5U$DkCpp4I@Z&<<<%@Q_nWlBSxLQ{kGJ6C^TU60Ff#)bOI z>Ms-@w0#dCcLYc5;}3dcEfNWZmNHo-SQ}{>MLZ+s2*)+WtR`M&T{!C=v=MvLR8+7E z>3o;CXawRevKc0BsWD(<4CDnx)uJ#O{>;zI2h@};Pa3Q8(DrBZBR(QdK<~^*e=sl{ z@a6PIWHGAa0$q>(3Wv$QijI!%LAXl33TR5&f1_?_L+Q&65O%e}{?j!!l!6@bxYfO+ zbGvqnAr-Tz?hr@@a&u_cFb-09a;Rq#;@L8WpPQXpT%6EN zYH-ESNOE;`nxpF)#P3&SlqQiEK!tV2GpoJh(??V zBii8BFi^jbZP54a#EB3{tNynSyrv66^cRHGqg&=xmS|ih0i3#SDeKvarqYF>VY-Ht z-UA<~)eO#dckOE(7k|!NF324pqX`uxI`OZI$8>iCbCQwEB9BM&SkhCbwie%X&2MTP2a?iCq{ov-Jhma)NRAQb2o{v zEJkMwXrFasQ8e#0HAUKGA`B+F-fo3iW&$eZRZCjBUf$xqoHf2N@IEj~=X&vSD6a0p z)p=zYbqV?7qE}~U5Pa$eZ-Q($j_YKqbSSH@Y_SD$;w}1#Or`vn#|n)HGPsN@T`bxv0)Ib~_IdH<{ zDrRJ|{w*$TuF0xTtLtn!&A_81g%0V23zt0OhHKZ8cPERryyLyr8b1+E;y$N+7(xZ5$2NF*N?rIGMCP{>jOHrIaadjG_HsGQ{ZpD*ZgJj`{n+6rr_Gc3F^RM0cu(7F8E3%tjOevmW0&7_ zPt8IrF6Fm7t+CfDd9XFxi}v`p$0z@PRK1YE!CiTkaug;erp;=NDUANjGQ-WTd=xga ztPF?&VsAbU!6D$N@Cun4mwL39ZlG4mQucIgta5iRl-N#SN=-|p9|*Qlmi87Ui&_0c zI~?7de!h&fE<-EHDRvvux((XXkP8adrRrMuB z^qT3U`h1y)P*HdORMKOG>)g1M_Ojo%ori8+60Pj+I|~7ajsVzi(G*ZZsIsp>_M(>q zTS8`#Qjz}6lVDpu2{lF{s2!Yb+x|U=-jfr_ z$0sgUU~dnpd)KAtU$!Sxp#3KY}rq)GJup&tBUI6tj+8+!t*#9u{u?@akx zlfOOlS|2bnUbh%JPy{*_++-0x)eT!sn$Nhy%<%H z=p|Rf)IP(ypOoThLf248CcR3338P4RlarPMOmEBl#(vZqr+#26@-|e4>CB&k6EO8M zma1P_?hQq0JwZ~Y4-TExCb=QsuyhsBdH=A778$Lt2)KuXulC*nY65?(>$JpRk_RW$ z4b_6D7W%%;;?+G}`*y2iPmeNQ@crMUh9)Nk-nWDaoAl~W z!@ZhRy66l^`=Lx-C@>+a$C#oxz%NPD;|~S?H641(My75uOpcC~g-zLi@*Bfe0Ld2L z8z7NP1`8=U1lhJrsk?lfvf*JDcqGu2iYkv)cqeS_!eG$gfrOmz76dH!j2LSAG>5*0 zfx+^}Ka{t>gYWMp?^JnFShyrxenW&;}N#|%m6nu(R(g}+!j;6u!W$n}?-f+Xx6G)kTT#FZ-I z|K^Jqk}H`=v&{uvN+3dt1Ey&?4cQWpCfEBkR0Dne>x9J|TWwR0)+tBzTgOZ6#^V+A z-qBDF*JD7r_%8d#Mer|7V<@ZnJ8hA{| zi)^GjK2U!4j#nq!xRGW75PF;0!u~Z07Nm5B7Rc!Sou7oIW5RlK1DNt~moG^IewP8@yWXPVsi zr&W|z_rFjs=GDqTnM;6HjdqlDVNHRrl=q|!jG)n`oGUe^?em0nTf$b8PmRsp{A;ql z3eIH$)T6G`pTau-V(n(0P*dr>thTipv|751vspjRB)&GhaMvqOU#AdZPQZK_5?*L> z0Ow_e+489C&IEQ$k$S*S=UJ|n#qyXGmpbOK@r$UEV+XA~ByWmgRk57FzVyPJStl8> zf1dcG3otpDk}YE&OYU|Kmo`RVb@uYhTw(0FIpjw>G2{7sauIS^N32OQ+--A*64 z-5&IHr3>LkF#h6oAeJ4MkKbOYD_MJY6H#u`yBGx(7aK3)R)4^JzKfFbT#IT=jq$$9}Xa?aL(GyQV8 zs&a)C@6O#?Udj?^skYO)UJdMw10irgth!Js!$N|F=g){0 zb2+TV{sh;cew=v^NFSqW4}3O3y!X%1!WOo|q6(_OmS#JCKw~KW$hAd%lW{|+Fu#I# zaj|dF+cM)74+Syhp#@DrZwJ%EQHF;%XF{2ZmXXI_%>Mu&=Z>|Y!JGoMV^`ukhd#u< zJCwy$P`Rk;qs3CP??~$|_-y`p<7RYXkV>te^?Z^{BI{bvSHB5_=c-;qo4F4B$0O*+ zNoLNY;X?}oAyXk%rMu?Q-Ev=mYjo+dG19=nRI8aQuu{XC`NA2#KZnK1QwV=fGRY%g zwAWT6!I}&z64w$sCzrqbz_*k@gIr+Pv@q-T?0fcKXV%5J@{C|z1l&QadJ z_2P{8(Ce-X%PI-s2UJW2Hv{`)!_S2xi1U4L2kprfzdpYznW%9^TYj>dB=1X!hwXo9 zdwPOpSCrpG#t9Ua`hWF9`(nd`-$m_;Sq;(+g9Hzhk~096r6_(6EhkuKPDeLUfbQ~2 z9xKL5cRrh&+ZJ@rno*N0O~r>YLa{Ml0-VyKPvi8vluOP-UxDYp z7T00!F(qMKd3{A*fXsdQ@lOL&9{zk*(m6<7?>MqwtDuc;&QQ-7UbyVj>v!38)2s$C z8g0IFdf>OH1Z?60=UIAiA-4Sv!6;b_-n9emAqQp68bTVH& zf+bMJP*O=othJdS>+Ki#0+RO#sMGxOI)Fh*-xN!>jspL);08oZd43Ywm7_JKdQee!Ad&jr>+C*`IBFjdT&mrm8fva*`<5 z`3$d8LL5o$N!D4a3=-eHINu5#ys3YMKQ%kyzv;Me8&|w|e?z*kqz=_YRsAcICh>n4 z;VDh}{Z)ily`k~%!(2{I(Rg71Up(b+F2Djk@KjQ%rQx4Q_3uXs{P-{8k<=91fBsp@ zN&ki#Tk!?p1vq{M4z6283g89G*56!He*-Q8i$Lu=9ec%w{duuK0U|etpl)$I;4ef+ zBSDRq5ixmn*OEvPXOpTHfw(L<0UGf73WkD+$9L8VZJB z%ZyIAw?812cH5J#W5=~fbQ|1@WEB;yFAv8cJH0yQ<`IPtq`FH0j879u_kr5U8@54I z;umh2S9(%OVE_?#H9g4g?e|cI9_jmLMFe$d3w2}ptgNirKNRUdOcvT2I5ydE$$k)$ z%2YMi7JQ-RLD1y2keL-CG|sl-XK9oNl7sNBv{I5P%)85+Ix(2HC>jsuQN>`lpkD-p054= zp;Rr)u!1zh{qdp?{;)a+*tqXZg9kfS*($HjF)X{f`;CVXuG=hgO37_i_-_5aCV1P`(^c<&1FBZOxsZOx_B-fb zD#S6Qr5?QS?gvKx_v}RlOjmqFpLIJ%lhvBZ&((M03L+O(*<&UbjhWs(aM_%!CrlQw zu-G@J;lpToCXG?3xz4vCOuA}Z2lO=mV_jZE1c6%hWn%_(m>B`U{0Uk``5rQ z?T9O>wN~RO2=Ho3y$$6{G`M2ngca9IV@*9E#j$a9N}SXO$twwCplWI6L>!;L&y7pP z)q5I2xKLZLcMzt#0O$&@S4ff^)!mEWfek%wb&>%Cd_s%)rohxDH^ z`x{+{6_mv~t9RjSs|J+(+Rxf6t?(l;KV0v6CtBOvs5P|o<|7on7b6lp9TLCsl?H+3 z$bo?Rr1JkfZ;B#^N!D>yzLU7lp#EK5{v%mt(mCRhXj9!fn~f>H==jgpZcEL+LhbR3za{V%azb zNAH9R&a8JGePaA=#1St?`;2#hTKCNdi_q^}d$ADL!?D2+jUyP!=2Omeec0-{ zy@}$UmD2tjSzmLfE(b^NmAk$B*CgXYnczkFy3^%myUdnfq7yj}F#?ZZ8H7KThbYpT zt}rBK1p;EGUtu*p0OVQEEJTZKEG77L3=GhU!doTqSuaHQt}=W9EGN2%aDZNS}4nxIK8aTD&sm488xW zjH6=9mVz+=|Kr&raiYhn)KpymqBjgp!@PS};%jLje!4Q~3$^V9M#>C#SC6X}@pMqg zPVa7U;^`Wh$n}P^w(FKq06rCDQ~fG{&S9#HYH;9V4y8q|>LVEOukl>*{*msM;6zn>d$H~hWL??1Jh{7#j53J>Mv1;2L{EAtX5E{3%@ z7s0+j!|xrOtYi|49G*^FmmY@6Q+_k!wzAL_HxHI#@IzI8PKd*5=>CBM8eXzlzMgQ1 z$YGy*qu@W16!!28?KwkHC~T?&Lrxp|xW<*+yd5KqO3e>D5E~s_vHY9fRgBl3Uh8W{ z_gsSy|4%WQ@U1Xng8P^@mg!G7LtMZKpDn|s7=$9w6Q20D1iS&(OD6VBsu;+SkW1h0 zGczHr4#u`5fXJIub?p$N}ej;{PBa3)vz)D5;+Yw z0Rb7b(hu6$p||Tsy6y+E+cCcS)GpZIx;}s21|C;-^)hHKC`^eQd^BkMsZ`*_9YWeC zQ0}UL^~VWP%FUVm_ma=J>Ijv_fEoPH$yBDN0SzbZvxuGZc~eQDc3v)byvB>{Y(O^a zI9wsEYwRq%3?js4h$QOwoaS5%X&=9o8Dg3<p2ws=On}VZ!KHdzi9Jkpwf) zrqi*10_PlB=2%*)<4D}W9PYmqI9j)Q{5wNjq9xeEWcRC%OFiL_-+l*Vr$5E_%3Qju z@3@mKVuYFJZ)26xdJSDeLep$94Gnm&Kaj%`&hGey*WC>QLN@o!&;VZsxLo_0$)OaI z;+Am)UpE?9dZeS!easET54A(hGpf(-O>Q)ort1tp)&0(%O0ANRVX;-#reR zbvu2~R|;`@%(sVcLJkQDDTT`8@hWZUButh{?r3zg4K_lyv)De=+Tkbn(oazi87K<) z7wTW8C@0kaL=!X>TKA|%r^=pfZMW_-YW>RTy7N=i)k)=*5_0jbT?rsQKLV~Y*cq)) zIGnQ}dz#dL43$_&UbzaY>~z#&h?0FrmC^Zo<4TsDDR##O5Jb5-GH;B4WV}*N9L2ER zCxdu53olSH72Sr}em+UL1X4>q4t`7>@!o2bLL@>w!blJyHrm2T?-)ul;rKo3UxcPy1H7>+1I%Kj+Q{i^ z2P*F-xGUL8YS)-xnmng6OP!jB2Jb;=xRkpXQ%HJn6~urvx%T(glYhKZyB|U1^+fi_ z#EIYP2C4JWfhcsc`1W>`+~)!^x(^!Nif!?L>BR}i>%U%S>bFeTSiSp*1C5#rCw1d- zZm3UC&^8^puFKD_4*|TqntqFV81TQR#-?uItNRC&wP-K3d(BRGxRf$bGOeaR><@L+ z`D(VN6}D$vk@KTfh{@Jpk2sn0o_QAdaU-5zfk`1*e<2joY>a- z(>m#21`q{H5!o+(z#|JKX4@Bz5LDaG_pqu=Uqof7{FLZX8u{5$UUO{ufXjNo!VSIy zU4Q~;>H%|2h*|YjY_(cEk=@ft9fINQy_~TP9>QZDVZ=(_BeD?(B`3pwM?a>#ZzBz{52nQdpq}~OFD0M9rqWD5B2)b zQr@sgfxND$5J^u^UN4`C9L<^|!IPGni>1cw?iYfHgQ#K(*CUrhRGv#4=r4T0#e^}_H5zE?NP!)?3#Od1(QP%I@Qf+_(9J%;dRu;44 z?XhG=ChFN=>vjP%EcUK&KT$eC>t#S{~ zulsrLW6aBB3!MVZB*`m&^;^&AuO*qI3LB^9F=7eCqs-Iaeth!;{^Li8e!-)*;nXux zkHC4PF<0SIFIv9qnqd54Lx1M-zMQth0V`e~zn@?u&`SB-1GjcxKD$y&)A+kVQWeQk9xFQcQQ=+bb!JsYSmq`x&K$s2O;9%Xx|kt;m} zgeK6wglHNT|0f1o2CXfks#mY{AuT-lAiK3{5(DMc7w&RLOL8$kmE;+_=FN~tu)MS)6lR`>v6!%@cvkrmuX?4=C!^2*)gH1 z{RIYL*{`n?Mm~?mj%nA@c?VWGd3$s71wN&WlQ?d^j&~A1OOL@6=vsf^mSyjNy;IhF<@tBJV>Bz|E}ZE^oN^wN3hqxvtz{5KBxybk#1J3R)9wCzf? zGT?zhS7&LAsvd9B0uQ+TYt6{D?=Ll9L5p`J^~jZ)4>+tO>X~(6&jFZJ&YX6nuNonP_NSAoi zPH<;%hXDo{+-G=`bMCq4ocHcu^>3=Enx5(I-Fxp|@_lQs)ewGMC|_arWj;R0m{u(1 zX8N)C#UAaf)74`Utd>(wC-Hg?m2**=8FT%|7ylUI5iVl?1@>7IAWoQ@+Hb_@4c|H= zwz_h(g`>o0b)2zWhaG)(mePP$ha0f~O}!p9rVF|$ttyZGn5D}DJ-@6#387^V3F}L} zZoURsR$iu7Ne z8%{)BU{{`=QakF&w+k4oCQt?>G!P%{R85rg)JHQwgOqg(r><4JxxB83mHgc?GXWl4 z+F1*z)Gl*tqu1I7-I!ye>pxdxX{7dYNM@U>{E1Y7^%0y>&Eduw^VpT1_}Zg&V%9u| zZIoC|8(yR7?~rCE?@p;tZYyQsC4L*%2MDU$y8tnlxh*J5`b-7E_9x;6ke8T3~J`0LvPW+EccJK*!nS%6*E!# z5Z}oCnTP5RewX;)c+@nf(RHccW9Np=8z)e*?@rA80f|VKVE&m841&}KaKoa^e9}1* zrAy9fvQ$N@B&y^I~`7jb$>9br+JjVv6qv!Z;>ouKz><`GmBFYi7-s z=RUEQF_?+E3>SxUA9jLLsyzudiCQ}Y!rU&N$)&tW=`qo^X9G9$4V+;t$i}@=bfAQo z@vJrLfHan$LCkD>=SrP7mR3>~FtKz$`L4Z=4};3W}b zoGm`bquFW+q35R85i%z$XM(j+{oF2>6kcB9k*1DV2W7CPzN0q=Nc64@COL*k<@Wdq zcfIE6vHx`P|0}YHIfmzD;{+hhjTKSkuC{zT{I9Lcw=TTfcgnSs#_g7hvFs^3>xi6N zK6soZaRZLD9#w3bGB|aW`>%hVk&kM4#laES~-Oq3Y}da7=}Ws+YtUDv`U>cq3vUXr0y@` zY{%U0ujVYY2C$|~(LN_i=3k%bcP;msAFG*>!|Tuiq7gSwLZ)6G|(56JYzZvTvu;5stRu6liWGGK&JJj6FzHvRo{M2 zeB`$y)1D@jEy5?cc5#(dEB3yddpqwCjjo);e{bYIK!XJeJLFth!s{vTzogX-6uG#j zot3;o8UsvaVT5~kkS~t0j%A9)i5M$^FT9hh`3UR*=QGuUMk&!fW?;t4u^BuBebc6j zU{2lC@_e^9$TkZLM*&XWO!zzFpe#YO08eG5pm- z;u6yv(f>1+rh^ghSXg@R7RH$U9rrw$%z>U&BaaKH9KdC6@aJjAT1W))6 zS~p&oT(OrjbD7Ui_F_>BFdm-Sl^D0)>1fZ zty%BrPVm|Ki7Z!}sX1})lkjfo^N%D5bVKBv)yxx5z1tlDR+3<8>7(IAG=_HnVAY;h zUU7PX_lEt=`^(ZVDNc+DajwC_&Ul{T~nTqMrx^Tt|5fMe2fXeAe9h-LdRUFCQRFYysH|-+dQu>p@36RKXpVlkLbfDhujzb z4HHc&Sy)^4Kh5c} zO9d|X|E9zJmm5zuZ>~f3*!62tf*zx9oLOn;_s{ZPgG_K1=I9&5Rz|PQ_ZBFhqTkr{ zfkR)>+@3>>A(#K#I6bp;xxU6gqy>(=S_w)xt>Hzr#J%_vcQ6F|{lj*aoG9_M$06nY zCKHCG$$d%lzn=Mxd;Z_v)329GsM3fEmEZqGa>=H#{cZWn;+Xw5VBg@U@LEQ~OW6M5 zK*ap~|0HIAKggf$D~$GeL{-qC-O^{pWG9JVBSY%%?5b+>QV@R8J1xDp$xh4U_W^&n zN5Sy4e_kG2yC#<;u3r&|3@hcRon<^CH?NvCi!QD|kWO4m+PY+TAWt@a_Xd(7p2VJ} zIuN^oc7^hXey1<^OR%70v}Ge)m^2`wrV5|U@whBc8RI~2)itmfwezuBbA2A0mtUKa z-{pRHnkE~$0n)S_qVD(q>!#U$e?zbI+RAESTY#`XJ}PK-;qg(@QvH@!e!bXAyM;)T zF9Oa*>(`a|+g}0}Ilgdi-q2}%;q&4O9@UUraCxXOJgN4-2oDsaGp^XbVgWGev7yS^ zSf!KrM=w$yDl5F`BQq2nReK7bjcETvcjD{oj`1=EI zfSRyB7xdwjDgFI2-hKKPiT_o?{k!pWu88_SIusRlX{rA8kL2Q)BVT&N1 zfBbywMFCC2Uh)h6 z&QaLjv92O%PM3SdN?mby|K;x~N&B4(>>pfp-#Fe{;4{A8ZpxqStJOD_ z|2b^UPmkLpQvb=WnW@8-+f*f}q~ zPPZ%mW9Re?`mHNKX z4JHZrWzr?qVA}nab!hnw%F>g^ZsP2EqUjy6<>PfNE0+G>BJzj*kI9^TfryQ=?I8(~ z6dmuYPX&%zt_YJ7bz3vz)~!LAL&x{~lqxdz**mxX;s)ORA|9Ay+p&u9U+m6Q<>H)Q z-w;W}r4DnAQY-(E|B!BPke5jLPADCCrpU>98#WeC^vT=lJAa|jP4&U)ju$Q0xya&u z(s8U%6I4X1@Bt_<(q0suifb?g?#wft`!J&fyRdAv={n$K$(_neZ){G`K0%bt5$Q1C z4iqJ3#X>BF_u9UldZ?zr%B;O#^YS(B5% z24fq}=_{}DhE-q#Q?_xQCC`Uopx(U8KWts>-%Os(-87WMYdQj@p}hNb*PKh}8~W&& zP**yH`S`?%{v$cY=Fs5Il&6^R;r#qRNj}Wc9Mm%s{~i=XfKr5IY#~p#?f(T#AHysg zP9eng8z*E{FW(M*^f|kAogHa#6CHCu%&vF8Y18Ir#_UQrUe^Zdcgj69hEef7bkrGV zj0A$H-wNUxhz7uGlwj(Hwh9?w?2AWWaXl&wWT5 zyPr~Pk{Hu$73C_olg~ad@~=cHZ}8m+ewTXqqs>$Q)v3RZd<{tn`x_q$GD~oxho$=g z3$k&&7>h?3m819N|7kRgKUoXJ|EFSt$s-g~QpIBvI)q~SMfC*bo+F5w;PG~jn_5%R z=64ci;>4&wXRfKpK!Z~-tr_Vk;SY8BAJKmIX%KM7X!S=aqdyPb#k@uWm0`#ul_FH# zf7LA6qgK^_DPCgMhr{&06mM!Jf`pXyF!~*N^)CbRfcno9f0LF-|C9KuMN;e?1R*9z zTSv?4#7ivB;fzYQJfYO$edeS!4;xKoO|rmkycL++0- zrj6pUy2SL!UR&|xP>R>av(KvJS^m3h>5;rxnq)4&$tpVUe6mD{m z;<&{`(44J7Q12ds6cgoodo~~!=uBYyzJ5Wq8SBl6W4$aFYR+#U-Jh`*7rY8XQ7?q2 z7eCy}A1G4b8FagQBjCMOG2g9;yGExDr6YiYV2dN=knH|F!Ds zf3#6nAu7;7QXpW6j{7mP$tR5&`YmB{x@j)u%=}(;cu>xkSv8%8LWC8b{#HILj@fJ< z)ba%RHCWD;u_@&TG=^4LQ6~6GVc4S{?^y(9*-q-J!Qh!-LTopa>qKuMH#Z1xrRl?D z#>!UzXv)fx%GM`I>-5#hh9pZ1xrUpqx&&WAIC&kUkl*Kt$uIIi5danT-1{A=AUfRh zTjYN4SzFy4vOfj`2Q1H6s| z&yF{Ay?uMLX?@4_=R^-yO#=&)99Uii4@KzoNWz=Pe=c!Jl#vU57mFBnaW^%v+*if| zs^B|XD^W|tJyO`8iftvBcKqBQy;gs{X4n?$EVDKOeWkMZ+AA!#G|b!Ctgw>uutuqT zXy1L3)9{+WyLB!N6L1lDYe7nhu3+P^!orjsd1Gu=v_yAfN{BvrFK2$HBEmAVeos&^CgYx;{e=>Nlxkhh$=@Awh8K>`)faTcrNPGD& z`k)#;RNaexzG2pr5hokV%}M zx@Bhn`%N^fACyo=)^SfyC_r-1p#RPDUY6CBYE70p0}O=RI#9N9uU3B?ssqjE)R>+x z)#H$3n0G&;{mL!)ye}pc5u}DdG@R(8x!BTGL5FKUt*!B~&~Q?`X2EkP_d!TR94Y;C zbc}12MQ_;M#rMxy5P}bp<~d&~20nPZOZOD9@!}WqXiVb_)xTh-eSQ7g>yRsvqtJw^ zSw<&U!EL*SS-ogm5t+3Zo(K}**bp8xbF8V=z^L33ETg?9w0YUNN zzh(-=wH9GvJO>*9KvNoUgk4!Y>Fa9Zhum0jG_{I_&P$$3*r^+pak>*`lgH5V$SopS z|J8bA|Iu2bpfVwG0s(%)wAWOT@ION~J}qnG-_3Jxi@p5;nZMtu>HZa=@$P;^e8vA# z!Rf}264-M0KDO~zs~PI`0S{tyOCHBXJ*ZkK@@dqFJ4;FL?$x*3DfX+KaYXRak`*c6 z-JaN#)@0iu@IyJzhAKCpn5J5Z^E0PMyHsAb|<_EYOZz@80hwl!dk6n}Yp zH!+UQTk0lArU=RB%jpd-^SqkD;Yk|j>Tkn+^4mSG1VLLaloIjby|Mol*^9(6LpR)H zc+sh;bIW?h#Uk6a6FP3A(*>;B)~KOkTbVSO@%!Jt=Cu9`Je%g2`RS*6%2FT?U?&#= ztnd!E>7+(x;KK5&_T1OYPr3-)U+8@|B(L;%rY=C0v_xA*d3#Lj?^s_8u87cX1Spy2 z;9s2obF1I3Wpx*1xBRIcc9(#^m`08R@&}UDv=(o6Q}w+>?Fcc@wPooHHvaG?e!0Xm zD(#O^>=ZcFUL&kEHMQwBQC`_zNGKh3bGJ@q6;D@#c9Dvrg5L;a2c5Tn z{Ia#XsiJ`wjM&|u{qe=0S{{4n!jCjy*JZZCXr7;3Y5E~2@#o;Af%3y@?bGkd&DM~z zeKpI&FpgEe83=$Vfda!2MK(U4sAXp~OCdZq^NHZL4(ze)Yb!=YVlH|HhvdE2wjxT{DU8ru3!dc;Dyn6QJT??Vi#7X?lSKRKfxcA($dR2AGKPl4# z3M*VY{NG#iIg0F6a=ARm!wH9^yylAfpslpt`xC*UwnpTi^CHm5;)G3M;n*qKMGkat z&Ys1|_wKC>y)ylJ{W6DISaSV}=gWGsIE;IJ4}y-UQ90d$BlyWz} zKQh6*uX374l`f_K+$)VL4a+I0kBALlC?|=GO+J|SwnyMxcZ8mm-bmu(G8XoqbrGv* z?vYfPGA1XI-ps>u9^pARnj2#LT&{d{PjQbjPw{D|-gh3YdA?gApWh_h$EF2vsqNKh z0zSm8IzIzWlb2{;E0sO|t{kBYD@K~zkSvqn_=)~q9{u6P*r#hAr(%(n_>CXt<(oge z*uru)8E&08XwmU6rss(fKuA?@ppN~MDw`)Rii5rgevJ~hPM?Q>l55_7wHyIh9B6BJ zB1MSVVS}axp!ORUv}i`1H14m z{e)p3>6o7iu~()Wio;JRhEADW_IfvMAV?^>XA9j*`YD}_m`v_UooS4=#>$Shgls-1 zCm>z?N3ULftmsVN>+bw9a_oLRFhObe&|z-@Up(!-w;pusQoPd%w8v6#6#x zNw9BhM}TI5_`poFdZ@!m@>Or?eA0Y~xm#-1 zv8Hdii=Y$2Q63VkK4D?Ty>@k=m!e}6(t5e^>0mUsypao`3?_wx4{^Gnen4!z4lasNg%W&}X4PqLgW>Nb~D?e{4k+ViM&ROK`E)iu8PBCmi$ z>Dw$}|7IzFAoDx$iOV9tIC0uB?5oGte7y>!rGFb_~!D7eC~(@tZ}cr%J0w`&LRjzdjoFn z*@hR0UQk4SS%y7PN$1b-GLkBly#I!6$gB1+a$Ajme2;{{{jOAmmJ4vI5Ijs7k zE(W54qHWr$zWP3=cOJG?IsiRYk2B9$sF1zr(~xvhug@0CL*Ot~p`(u}v^ZB^r8Y42 zXGwmHR9!W{49g6S>T{W$-`q>HoOBW@lsA6*%AFa5)X%R0L_wU5Ol~#=syylHhdi;~ zo@!F1GM$oaUj5v}O-rX3F&A4|LD;1A?x6#l?NG zgs1y!P*>fyPAD-dGKcCp!zUa!mv)0f6BfYA3F;Tu)|U-Lan%-=;t|x@lj-a-Q&e%+8zz>V=Qx}jt8v3Q>V`M3 z2i+%s_Rg8GdqE<$e*SQeqY$jLADbtpR%n?9@_nl3u`cd5wWbJp=mn(!VJ@-IwPgsj zx|L^j^UWPY<%|oQ@5H@_soH#1ERLfZ=G@}XsH(EqsgRr@&BPVj5YQoIJg`+ZAU{6q zI^R}S?{1goGvgDKEqitIumOZC82FhnwMy@%H~5~9P>iOEXVF@7^;h*R73T*E>^L(C zYTX672}c-~j=7TYWOboJ5eZk^n>Z@1SFGlL-Z_>Kek(ar-MH7767jnYc#41H!J9iI zwVp^e>RvoHLMj^H`e`Z^QQZx~0rk-o31_qqw5j+lcd#g9xB6)88@=(UISynh%Up|d{-4%5ur z&m~P|oBI)+U5KR@yVTDAtckAAT};@!;5q+>Ndj63uSUdGZ7Qr7&1F;Lj^#p6w0D%%6{3V?Xr`^6( z7MjrU`l3CFu&X#_8zMM~S1iul1Fn95*IwrVna`vOM04H!LiO{6lvb>L=Tn2A>#wF{ zzIqw7&_U+@kWG#vZ=*Hmp{2eZj0>s08Mh_jlyRaJzyXz{KyU+xxeB$K<=r>*^A%I53$1&BF7C zjMt&-4rmqyuoNb<@LpIzMyu^Ne$$WJUH z4lMeO-%hFUZNTm>OW!t;Ok^}dTlz0s>cxu#0S>)dZ<~#}ESa{|M4idvl#RQJFkgSM z$C?F_L&wjq)yA+M4H<8(<0f(M-#@b`gMAZe$G@J}g#8la?2N=S>MsVp8x5m~>$wrV z32?%y&!M`R71|cDU5KwWC#npz61kl$C5q2)}Zy$yyB>G#bM(Ob1@1Z4Miu< zN^OfVXY}f9NFEcZM=T#n{j;0OA`8TsM5bL^r6T-79g#w%RCilr?KVt#b0X$o2cwF^ zj5J20a_`x5qhVVAxi@;snFB)d*>9o4NRW1emq$fsET}scjB5M1{Ee;m}n&A|DO`E%fC$hrG9VzT2~j z_l;dPL67gd%26oL_3J};y-ekadg_U((M(UA^?JhMc_jnar2^1QLS&b4zO~@Qn!D6A zb2lPRR5@SF;W_!yiqrU2&!}G(lM_*|G~r0d|nY`rvlYDY|f5Q*a%W>z4#1>GS8l8XzXMSwAr|| zk^5`$XDDT6tch%4*{*?DYSY%E#)}cm9Em)#bf)PC5)ePd*tMwJ215d=J|0w{DtJa= zu_3l|;Ar$X2=!Y0-CXYD@}#S^FPEu46kM4tbsJ}tK`w4=00lRt^Pz{$s}tM2Z#5?V zv8}u8%~ql*KYcS)U51`|c~gz8z2d@YI;Rr({1N-4vFB_7Z>}c8Ff-2NjT#ybsRy-4 zbVf1T+!58Q*vizt$1ZMn+&PEfVZG)r@imee(=1UU5$m^!hnV?8#S$X35!(~A3LzUQ zf)8;8A&Bu4<;(N87sv65L>pf`cD2!*@?aUW;%CRjXP~W?@ z*cv}MD$o19BRnLIsvG@%Rejj{)a;m$aV72DI0nIOfXgl(Z^5Y9rg!rMo4a#+Kl`@r z7+}KOc;V&^T3y5u0QxnAStr{gmOHUJS~YxKILyQlJk5VH_~y!Jn5663p*6R8Ot#Uo z*6DU1PPAAPlCzu9K{?6nu~9{N;Nj%9^FH|9+q#kpcT>kz1@Q|}RjqeN#oLY7cB|R4 zDe9w4nt=LmGj3Ustkb38z*8gV)c(>_iX%K&f)2k{>V;ipKJB3^g{)dRxbE9nxPj=q*z8#Ooz08796%6{ViJd``)gdrEnkdsKz1^5{dPvw=y$~e%l>Epucf+&egqC> zpRD|z-m#}>8nq#J=q$@Bi(?aV9I^1Nf{JKUC9fbtcS2O;zGD#cBN&3jhqosg&rii7yuq8bUu4EZN(UliemsIEDIl+oaMavWeb= zy@mfuz>W-WT;sP((jU8`0Ud2=8Kg=#F@+0V8xBv zv7WORUb>BXW5u?ci92mJdQ(@&My*1DG!U=R!bY(7Zo;O9u61Q^RoGH-d7wr9z$$;X z%!P!CXAn~{pEG0*+I-36tLq5f)f;oy1TAYrFOqy(OR~Dz#;0mMM3Q2 zi(3aRn5t}ABb@2(g_WphL+kSw)vEVB9JT6z1&6zg2S+CvpqHde&EE84cl;R926Qg_ zRdy$~CZ6p~(nlq9+Fth5hQxOPMGz5;YZ(M>XRecEV+R>b+b1pft zhnUGczFk_5y7a;`9(bI;QVB_7oZ-7u+Q`d^Yur1vZ`yW_J3a6}n}1#uz|=(8n}U&i z23ft&(yureOU9KLc`qefuL6b_!jzhCjHm*3Umm3`C0#)Qi5E0aoP&~Es119MAxG;! z(5ctpi-5kydQ>d2D~DTRtj&#dCq$UFXeL$r?xlo0JY)49es%9)VN`BhzchJ4fZOyD zD0z(6KvkE;1iu$gmAa*S@Imm+2Z3?6#!$O8k?(e#vR)M?oeP;XOQ^NvS-nQa-&wR|V~NOfnxZK^Lpl`<9NBnI{G`fsC16k#yC?&< zR=1Dt)>&)J{QBsGiO1DL&9ZX#+vRU1;-sGlD+f?afq{?F984&UIap}SzMH`)lA4$I zG}j{9lZ3K63m-zwQ%4gj*}wOcQ+2N;$vLo)y#lkV--)oh-rEl)h+Y;hhzKsb#&CtW z?0I?lN9~Guurnd5ujItn9Xv^Al%(1_twO#@x)domP4V7S94*jgUv>O|v^s38cOgMk zo}tzpz_>~fYa-pwbeq!fMJ5bfg9u>Ls1jN=ugiQe>kpa&u`h3bEH}A0=#7AR*DQV^ zUm2`_ipkg&t69nt!GBo`n!Yg_-W5#hB&0sM?nvV!S%fXu1enZon5^G(f}iAk{P=|RV{q^}tkbq^Z#6p}QuzHcja@?{f?)iea{w^NPowAA6fn&wQ_^s~bR(;V>&0p6|q zPd<95(Yq(6@T|?4A3N~urn{ErLj3SSbeEbq>Na!{JuDU~1H#66~2(rLJ_c@D{pHwwINy!yh3&^@gvZbH<^Mjkq~}VU>Us-S@kCZ zUY0qCB`)1*7B0!DW;`gXpE&$FCYH*Fe#ktSt4oMcTwVK`AL3)#<+6d)%krEX0}+x9s?LmXO&Q)#XG$h~G*`KdWLW7}c_d^cqU~sj zcdht)-*%&L#+hMF?=S0|@ox=?BGo>8X2d>ToQvk1FxvG%dmewU>p_d2*kPA3{kEz4 zyN$@fgC|Cm`sqvbG?tGre!(%~Pxu&BR`Hq@?I3ZR`AxHLzTsD!8W^p0fOQ8P`i zktD-1Cq;JREmaItiBY6jR)9?*3JC zEXo*}k#VW3c*c;DTVH zVI>MDwEk<8v68j@7p|b7_G^+7qr&2v+mZtV{;7tX!JIb4+wFneBN*J816-g}xK8gPMC?r#(!vdx_~5@}UuTB|$Z zprw&cm_8C_SNmh3j6|YM$5S|y#$Cu%kl2WKyKJhClNJQI&?=W`3XdDc6tF zSwh?dbT;k#!EJ5q4!b&y0Hf_P@C*;1!u6_!6V`1M7~b-c1}Y(gPTkR(Od1yyJk}U- zE+zhYe3H;u^OY8dTVVtTdrch{z!4Jrmcd0$46c}3tn z--jrU%A<+X>cmZ=3qOlH-<<*Fn{{??b=H@sFAMf97fX(M&;ML&qwu)Rx2v=Ar6SmdN0WxxRH3Zq=9%^3aI zwpUpuiSOo}n+EJ*zBj!t)`d#-NlUBh&Og6Addc2vz)gr?!A2b#hjyGW{|^^dCMmviis)|FNae@0MliD zY^fpZ?GZR1v#RKl+R-~_Y7dd+mYFr6t}s#akw5SHR8|EE5+^Zcer)wpMx#gIuj-^t zn(ZV}0=L$lUP8pepy9-hNAjTaYon@d!pj{9oiTHb;$@5c*l9v9CN{Ju5rNuaY1Vl| z0`U-Sh;sEV!4;Wx6VF?EAO0#fxzT5%v%g=PRrZOR5dYf(WR`ujgOuakbNUyJo%AoB zG8|H5W)myJ)@|;X41?t?b8^f@0*;YfPq0spk3Hf#{RMsv4Q*dnoA(yRwZ!SZ*YHb@ zKrJ+LGaw+ORkB3~}ytuU@9BZjola|((_ z9a1gz=C$)n1xtQyS!fs$c^RT=xttxGLu&A~aqi@ZPdVlX5Qw~`-Duot0v z?^G;9)30bCe)1v|&ojrT1IU{Iu!$}B@GbR8Pd*e>T!j2xJ@5qwlHAP45Sd8)4@ZN` z{pAB=WA1x8Ur4;O|2ZERG@&jWWQ3|-5QOf?;7~w%+VBGesZJRoQDzh(5&9kwne!zv zH6pno4kZ*1G2n=QpY=*hlREU8&fr>t#8vt$BgrMidym*<8M7=9(V12_g%0=l%f1dq>Mz&mTI;tc$U9&KITi_w#-Z*nQgdWN8Td!H44gc4q zq_?LN-MlJMAvw$rOUKFjpbyE(Ld7hFZbBq^+x5SMdZf5`TY#nO75AvE7#BSE4MjOF#bCNIX%avm+8C3LH&l zQ#C7xcrL!bao<-|x?O)lRBW4Em>8HdklQ6*q*X9#C;dG5zuhS!)fd$dGWZ4cuTg{e zE!*LQ3p$&szWt%!aOFncYrbHcA2bMHcu*@b`oh_f6Hus?<;Ridl@hDQ<1s4$m$9} zRf|pQyv62f+CL{%^U+-rXF`=8(Pqp$XyH(c2PRv}`O33B z0ihIu+)MW~(U#r1oNoq*XrnZ}#U=weOE&%48qeo=B+-@GQeq9x3>*T9YhS)R1v%I1 zDyXWOpRJPHEakDp#HX$8Z8IP<|2aL4FKTLQ->52XR?d_*<~#)R9eJ0Ga&A?-&!)4B zlJ#Fyw)cFdhi-%#BeFrvW$V-&Tx9}PH?s0=Ep!R*&BR;}C9Zh$A}RxY6`ukEVLW`v z4jVr0I1kqqSS@!dX!IhI4F$E9orMALO;H?6dy;!{p0$CK=(o(ZVj^PK2gStK4A)+a z67JX&ed3oKH{Wo<6Cg{$~5?PNyZQWat9B!mpe5vEP$7c#Y zuB_(iTUGm;LaA+VBqrXG;H>Wj+Afa7bo$8;ok=pfIudlY?uUWVl(Q6o%r?ov#gzmY zE&bg7tCL1Ay%W`PCGWL(xn(xPB7$UnS39g?1!5Dmg%k6Bo0Y*8MBSk+Bh3Mx04rKg0R`{_&$?YRaf= zpj5hGS+05Vk&Pa980+BTNEczMk9Xjg&X`s<$3yGL!`>qMbo{bRvt^;FR7Ab8p1@<{ zh3l>AO+@CgpR^f}qZ?{UnVLEND;X=L>rg+al&sk5tIJ`(EMj28dM813u+mN`5w(l4x+;74;A&P;ZOcWPa@cy` zjK@wi%M25SG_h5k-tqA<^F@|>ykfPKYfmg44Vq2K{(A*!Wz8{mf3+KaYzl>roUymC z^>f_sRaM_lFYL*5Q&Uk;!{3ZYyuH|+$ejhYxNW~JmZsrhK#bdGG7QWp6YypE^!epz znrraZt0E99S|w``i0KFZvM9aTqqHyzYgpsz&6x{L^33Y5B}>wujSremN6$hZ@walt z0FSbJYXWb6#;gu!P{*kQdeX)D*~8x30&|K1<%QRq%eDbtt(bAjt*JZhmzmYu1qkEK zQNKP%LB^SYY^Kqv)l6@yUXzwF4bRJrA$>=FD%aRs&mTD^+Clf^xmbO^i6fah(;|N2 zl6tbA(In27_Z-NX$(}Rp-#0V_^FO8)2V%c@H2$f$L~nG@V$9bzwny9bN*laQKy0s7 z`YnU=67)8;nlDpdHc9yrtI7p)&wQ!!tfpzOyFuxy3WP3exfHiU@d}k_*XkB^dqFo`oL& z@%xT7)lja3QFiHaGym{QsC$$SF9{Y33hE{VtbN4>8oXiQKpGmc|5lPsCg1F$D;L3D zo*|NxiEn7xkM0qYhWyt1j)S$nMARD}EmOs{_yq6MpNgj|wWK63L-<2U2}*d_`u5?c zq|BG0$d^ZN3)Uh{N+i0Pa^%CqO6Hx5_*?h*y~KqCJdRQe)}C#}xoODco}%P>*JjaE z+=P6Bj{aCy%(Ynu>nza4+CNAep{L~NCVRm0=Yn&9D znZS(gmasQi0oQKgojsug^x|vGXH_M)DB*#j5gG}O#IBD4ue!R%IwkxjRQDbmxKd^s zO6+dAfftBRH^#?D1Ljt_6yWm-X48k=ZjqYVDqH2f13LSD%XI-oC0eXEy7fVk`#+dP zm^qNxFl(>`M*`!Df%L=;la!!vn0Ql1po5UUktHo(4nCE|-ic&xmSZ zfH9-1ZYAB6 zZ(93Q*hQ8`Tr_QWLq4!8;5?-(9j|=I1d^mq81!)Rxh2iDaz*v-{8Q_FpILk+u}14> zJFzeRMghshcj#4slehymlyk|@n^(8+1cGVHilU6ec_^`?)5-DqDJ^e_BIgJ2~&+LY3l8d zNxyOaI*mYuM^(@3HD<@|(ip^ei#0jsdFQG(a0MyZs^o3wLBx(trPbL<_+=>q>G}R{ zV@2dcog(5wC;ma%H{$pJMGkoDzec3OqM+~@1=)@b`w!8usMFlTU9r7`+VC+dK)jIk z!*Iz3;OuIF@Lt%SHT&M{k-g?kO@|j2kTyjf(`frb7g!Sn5frS1CCVv2Fv!;eg1QN5 z=*Ln!wkiSEu-S44Js5c@&bC+}^?UBum!=2mpl9uYqWHjh#Sdt+>h?D$;5vuZ+GK#Y zlj-8R!ST~ReRLAYM6n`0DnxQ87q#@-AEG7byrMd*JFO2dRK^DYx`N+sC?7)y6a@rU!1oXm;aA67 z%Z3;eL&VrumNYkGKn!w`4{Ft=K7Nx#HU3u&(#5-h#XSF!tM2len$fTEOZ1w7!IF7Q zxIogbG52=Ufw3uFfiW}^jH1;PoX6#w58XK%ilt&kI|IRc!$zWspq)i&sJy||;$DC+ zUBHFp&&Ey5tkdki&fj3B992xI@;EBEbv6S;K^~B_jUal1gq}A{6Z)y zCCdpJ>$=~2Ln;o7VFkq`r}LPJG~rItI1ol;Ul!b%TotNCk2ZbudN|Dpe!29uu#i}* z6jg3w#=&Zj^=P1!3Tb?=Fi?pvxkNjEP<4o$x!THr6}bo#i%AT|Po=3@rXpX{``No2 zrEu1$!uS7l_0@4vEn)v40xBQ~NG_n#-O`{)NQ;C>NGUB#!%{0PEuBkBr*tk2l1q0k zuyp6b0`Ge7``&whzw_t$%sDe>X3jIu^ZkCGndh!)^trp8v^nOZ1q&44bkAKR!7k!b zfQQ+Gtxv)%0l^7^;T>WwKQgzKuyb5SR5`OUNMU&z0@M`}tU8*Jf~D8vhM=Ps;4iK$ zI#=-5yP#!a_MA^o4`ibTEGcs!z_b$Ah@YJ`x zS)d+h4wIK%eavqWdo3pEp+xeZ76XmRnnCeB`(=x3LGc<`LI8@K7@n3AW{*~{{ZrT1rqi}G<5_mq1yH}@s<_d>T9?!+=$mR+?h9;psV?GPe1G6khnriEQ7)_@U5@FQs|3v8 z;z7?oGEgm0HcNi}6pl+FI?{=^)GTYbG!|Dj7vGnzVYnLeC1obzJ;vq8N-t%Tx2%Y| zrrq}(I_MB{XpP;d&9f?X68xlC{_G^$mUq9y>%;Bs!H46j+)Yk#&EVippU&7$O|2c` zzoWEyz2ni6yI6;0Zjq&8_K2U;loHh31@md}RJh;!@CpFuZO9|t)C@bTrv>%)c&&fp zU$^TFz1l=S`RL#x@<2i6MyH#sZ;QX7o)2sm!hcUf3>vXBa@IVV^CQ1spU+MVKrsWasp zlHg_XNlslAhgC@#P^{(0g7wOqY~0I(RGZldihiZNti#_6y&uYyO>b`XZN31Dt36#j z#RKKd^|qX+D(9!fF_f%uyw!8@8o6*aP+H=4j?~gspKnpSG?DDv{G?!f@|eQ+=?bvra&a@ntkmI1u& zv=2n2Y92EEGB-VKu{CqeRm|q0L97w%4h2;?U-wdz1=Aq`I+b*sE40Z-#D`{EK|h+7 zvUQzPnx@0g?7lYj76g7H8VEXyapg%B?;j7PuI<*nxQDsheisblC7lC7y;N9H67Dfl zAU+LGzVB2h@$=pRHupQ??^waC!3BkrfBao^a4&e!D@ZCJTUm?2F$88ke`v+__NPJL zx-(a3c7D!#@2&!=9O0Xf>|8MzVa4QKy{kQkb!XU*61>`K46K7oKSZ{EBPvh)^>Og$ z8mS!%KivfLkMj%3$gH|^{4&`uV*+PkAAw58PD2M%96xL*afP)XoaxH1X3q4fwTr0a zXIir9PRb!Tl=8Z;%IqB@UkwWo@6+`rd$fF>fqKr?oOiO8#Ijj5!@6~ zY*5X6Qw{T77#Or-3No)XU#Ya%?Xba@>X^M6g}SinI8ZD_`}zZA*LYhLmC0Lp8kQ$( zn)}=2zK<=$xdNnYQHcSKC!3=UArwP89aX@X;AT@MwMd(9Pd2BS;!)t+j)bvWc&?B; zi4LIV(e2>%kKc{ODCQs^W67VkA7QMrkqsVCgB~@EN{b9^?}20pt=`z3Q!!~*+x8@T zk%*2on~p`VU_a{LHNT>Q>LjbSdmWsN|E9POsCMRQBro~m2s(Nq_~VG=I@7*+Y*!49 zU)n~$w`=8IEgjOkZ6hq@j&~3U>FgAnTaIcxns~wJ*z9{5*5bPP8Wwd8z&QW64jIy7*5dirnUky=4x|%UL&DGtVnN8-}Yp*^vAW z6HF1Y-B-!b2<0}g@ZgFRI$007A$1D>GDp{Ad6_=qHh>Hk>?Nbx5h|Ay^rcQ}l~a-sTo?Ve3{@Hj|r^lDqoVgbw`_ z$~^VX=f1Uu8nrcizM}SCeQW1>)tPJrq_O>AFB)0sn$#2R9cCKvx>M$A*;8YXCvU5F!fg#S!V z+9mt#>;bB|#X|p8o@6Uk$XMMsRcFlZM63&7+~tv6)P3qNv*z1O+r zlP%e>IiKntmXl!x0fdW*0F@7DPu#G`hn^EN)e~ ze+=U#;F)L)!wD3aEE9QO!)#v9fxB@y-+6%aLYye0I424wk^{U9#XBWd6YO=Ho#1~B zOri51Z&g||^o$hjb%zDsg~`SoH>wT6>aWX8z{0c0II9QdYAE$lz~TEL%y`xy)x6kP z7%T7QJWT^iwCFWdxs<0=X<}C>I$1aRvc2H0$x*3w)(Nv_w#mZv+Q4ZZ`ItZ5JI%|l zG{Z~!L0Sz!m3F=(*xj$bI+VO^@kaxQQP15#sVA`pe}{U&|KQV?5C^OTA%eCa=nm^Hq_(*2oY`nQxW_BlC}}3=1|3 zQ2}|0NzJxW%BJ&@B*b1aG0kpp@~?C)N9cNN=|iC|PzK(1dpoCK>mJ`bG7UZuTz;J!r6v=A3)XRl`e^nTQ2OK--I_y zdAEjTkA!*F@MEs~7xOl7Pj#wntWp-pgw=%#$6y*IuGQPy6JWcXs*&a{TUuIG!0w{g-drWX^d{;t zKEYz^WLQz1xQ?sB-!5g`j@4%P3vfIdhWV~UPZP z-Hz5nH$xlncu7HV`ibNic)pLdl39H((C#A9xaw4@b`LVC3Qef}klw9->O908c1gBC z>3PD_FnJ*kUh?94hujg~R@@)7PsYUR8hKo<{FO{7rLi;Sn`-Z1R&IEgBNlta?7P&R z_1#>DxceFGhILwl1=tzc)aCS!`VBkigZMD+ze9-~9mXDUB=JY)C`p++M-cG9>EdL9 zd|nneymOzQPp_uqSUE8i9od#Ts-n7i5s8Eu?N_LQOnnGWy*r&7!yikK9EMW1)7R!4 zAJvY5z7}}-OT_?%!?T-cI4L0KjlTA{Md^ebY>w*uCOLH?zX~ljL$=Gh7iKkW6p5e( zc$dd#+zR|yZX`2Vgcr?7jHyW>3`{4_Q;D=QZ%Cw+cUn59yEK(xo94*#cb=BlON1cK zL(beGaCn|s{xYS?f&AQPqwyMgy2N3^<*$RJmM50c`QnX2ZdSPjr5by(x82CMB6ju_n$v_WAk%Yn5iOob({MtnspsQftT?3e)pBI9w6ZaNAbGGh%qw z@YD9+(r+xOik>(|_PJpIhJ=nlLJlr)HW`C765%3Jo-+(Wc)!;ucRrm zwUIrkNIUaG-gxEf(VpHs`Y7xz2+{u`$e6OQp zC+9^o+Y))%$)GtASXVc`HqG(rQep}p`Z#n(zc2-;-rrKeiIK^WVdX%)&Eib)nXwMnbm&jaQ{5B1XcN`og+)s_x`<8^S&SE?#Hf3oeul@2CPvKh$?oTqN zg`vypY1iz&4CyhuRST_+tBvqH(wjxaVAd7gzp?NW_*{J@1>Y`?%}c%Yi^Lim7f;kP z826d!GczwKyQ;M8`eo_`AI#c?!Qq#$8B4+ne}!qgg-JmZ`-%-+ z(OvM2MG~{4O@adG87dScpWUz15gW8*q84PH=wQe`^t)gkxB>AnG_t-(03DQeyJbb) zT#)XA*Xi8VJR`-3RW^!0B}6o)6{JO;bcI4fJmf#$oxR9rhAcO*0v$C{rH>=gc~c1~ z3W8X)##MDz0;a(ISKn7gxxmgR#1jU#w^NmrqMrA$v61KVvLogC)8@3CS07Uw?Yc@C zlN}|y7j+a^tsZW=8Rm^;k8K^Ep2FvV2%g>UD8WLoCFW+kX31y#)-2u=fVuLKazocI zgatvKrEa*!W}#JwP06Ky&Fp~A;E;WZagCOEVO*D4sU3<%t2zoeShNNDoEOVg1h4vr zcw0eksHyqX>71#i`Valug)eiysdBs4t9;b^%UQQV#|YAPOaiRHtMJKBGFJYabT*OJ<7vS0sBXcm-5t%8Dyy<;iq0lU&rEHE%cak@0d%j@j_}&9Mv`2=%m-LdeDH;X!1%(~iObhv&V4yQU8;B7$A2PUh|nS7NV{l{)P? zKXgCcw7-4H#LLL*%-a+DC*B{bZxY zs~N(#Wp(56VZ^e6L=h8(z=k-}zThSh3R6Lw*eN6pp=Bq$7G$hk3pyuG+TV<|g80)(>M~XD75a zr~#-0EF&u|+Hx-NI)VW29TWq9jt)@%T=NC`FV&YBmD!oB!U3ijFMldjvhcY|^una} zR}jl!5a^h2^J-y`{(_0PlQ+KUh|F5Q0Hu|0dy6LDN2=x39@9?4D$>cPIPCY#s5+U4 zOFkU1C9rdYHDBKwe!IOz>g(&5>X#}p@9g|ulHaUx;p|hJ(TOtx|(HEm0!k~sF;|vetFY`r@SE!s_kCtLsDg0tNGa9SGSNxcd7}DxeK` zqbnzFwUIj`cG`vL6bEum_tk-Mr>6~WF9V-h=hLc(nymr?^pxn0*l!gD6OMpADj(#J zPf-bp`HRwb+AE0;u2HE6PTcwv9Ce=*@F|HpLdup9t5%xI91luy%{Y zD6j7ZVxC7;WIvp(q^QBjw53YF`U>zHlvnfv)8}|CALWc9-B8YXB551?3taDR+1}c2 zp^7-bKS{~RVpA|HbW7IOKgU4gI1Q+|??Ju2{R8E*STqmddp{CV`rELsMe4wGAkb)7 z4bz{w2h~Wh;x1zmbrO|jQ~k5fpvge>VS$pq>z+5)7f;d?{R?LsqjdRQ4o^~nIu$PD zy)!RgMXJ3eJ^EUx#>Z_mR=Ydn22ATlUhv^xdp8v;yWnN?l9j-c*H?Rx+Q> z5+L?*14r0ifmL^0_NXroCzCZpDi}k|iFg&hZaFT5>yNQf_{H}?yV8$GguoS2{hw-? zS{_glQuT~=z;`)sxkio?!jt;`>KhiYrt!UDk%AQ~(>8-Xa^Rk)%GGrk2e?eQshoq-#s?6R{>?9v*I_80W5zhU(J63ucQDi1HR3fWkh)yCay@7W*aSKyWZU%}`431MSQ zh1oTZB;P~PTB`r$s?o1mtqNxAoxh*J<)`WMPwCI8FaJF8e?pnR?&I;3l99gU{JY}6 zYtQu=liET23u(-4ph36&eE7Z3br2#Mguu`w7`Q3+9aXJzXn zb9(Tv{q*!8<`(Y6uA-F3{I+ljeS~_Dhfy)?ThAx6)_RtOdVSX~#Ra?bNv~fe-0_ub z&?}eH%90nR{X29racs702}-Zs!;($Gi>H|d;!|z7#alMFwm612O;M(xWsMfj8zAlAk4YbM z?JTpLR{SJ%_S)C}J>W#6_X`qSkIJ(hwWLNfp`fuxm~^`HXOGlQAHO!7xT3kf9bCc{ zPWpm*dS7tAqcsAjrc7tBrUR{bdSM~4sk%=$O4?EZY5cln!{IuInOb-_EfQj?ihz9_ zL%Mg_z8}Q#g~Lb8{eES+2PRJ>{5yArnN=C+ioChxHdewhsRPKWQ;&{L?+fholn@dy zP0O#I)&wZ0TyTw1H4m=Rpx$oI6Ws(3?)FhA+9cAB+nn1fwM`$IWvP@;fV8Y=$DZFEANrAnFycOhavAIP7Mx@<1s0|DRNtpw9mu45J~$?Q}y z;S zSL1vS$r}^id%552PPZSPm(H~Ze>WC)`(TQi@T{7~IGy;|V!Fg{5_mGaEa!&UH9k58 z4a}3s!lG)A?TVSVs1bZ$7aIpG&zW2WxhmDHRFQL~r_;!63lSTLqz|DsXzXhzMMDpT zaPy83g3dt^-Nyc_zp1;w=C(7tByU0V{S31aR~ zeK7sfDXh9hG3;b@vyb^BbdNOt@$KcyebqM$%&2;a7;i7%k*5HYG4+6H^@nck_3O5a zV!4c8^_Y#a7*#snPos9(CIP%-Y*u(D-jc7UGajNFQCcM#NrhGxyJ8(3^TWT2X^v3k+ylG;66 z=UtCmyIbR<7zyDucf0=Fz520K!K;_1kalt%Ke*SrV%E$%L z7YSKEZAl%zw>8aBH9IH^bN2cRIQ`2`eh-1Xm6StgiJ(74%Ly}rvb%1C^H9(oPEM+- zlY!gth^!ibOj9^O`Io}>!kR0e`!JmJ&mEk67`dm2QBrCVv2wF+GqfoKM|}M(=alXI z-N)gIBwe8lErrU(OeUaq$8Qj|l14P`D}@aiy=n_9VVTRL;Msaz;(cC5)ua0Q^_xag z_P76->~{2le@t=irA2lM2Q0+Yf-iU_1HXqvgCmqRjE}h@#(un>y2x}k0idda?7`i0mK-X`9W zKztV)HP-Deym$?pxwwGmqQN|?Ghfg3%UbxfXv9o)Ot-1|W@@%BlL(}^+mHWgnRHzv zs!9L!|&%2rw%BJ>9Wb|%n^n89u6Ezv0WoZAQSbz8TkfnM9@E2t*fLK=x zfsj6B+vNc>pKP;_H)lKV`c21?Z)m0Xzbn;#zD@1YwlijF*z?qV`cjjHal=a!OL?Xx z7f!;+zU7z$Npm>gwT?T}l3nyRQdot;gaOgl#&9D5k@)4eMfHHTu!`a?ADOiqt{&dlLdCzr7Xh;>ueq> zK9jus|N92RjSWR@vu-5ye2E?57#-wyF!j=MFYW8NQ>jwQ{wQRa$FY#^4e(JdbKmZN zESfYaZ|VuBML?>goj>4s`eaPt_7EC!1Q}vNnlphW_*^z$1mmB}CU_(|FLA;@+tD?2 zy**a@&-e%RHQc0mkuN8g1I%ZFxDVe{M6~{7uXCM$2Zu4KD8!m1W^I<Vj|YcV+%%0UJpH$(2P+NuEgVR7tJnXzM}7uw X%Xb{{VgDe5fxZ-ERb@)0jeP$Hyj%aW diff --git a/freedv/branches/1.2/credits.txt b/freedv/branches/1.2/credits.txt deleted file mode 100644 index 431e399c..00000000 --- a/freedv/branches/1.2/credits.txt +++ /dev/null @@ -1,13 +0,0 @@ -Credits (code or ideas borrowed from): -============================================== -Dave Witten and David Rowe (obviously) -Mel Whitten K0PFX (material and moral support) -Bruce Perens (cheerleader, promotion and publicity) -Mooneer Salem KG6AOV(Mac OSX Patch) -Soeren Straarup OZ2DAK (FreeBSD Port) -Don Mak -Steve Nance (K5FR) -Joel Stanley (Hamlib prototyping) and Mark Jessop (Mac OSX) -James Ahlstrom (Quisk) -FLDIGI -All the folks on the digital voice google group... diff --git a/freedv/branches/1.2/db/current b/freedv/branches/1.2/db/current deleted file mode 100644 index d00491fd..00000000 --- a/freedv/branches/1.2/db/current +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/freedv/branches/1.2/db/format b/freedv/branches/1.2/db/format deleted file mode 100644 index db06890e..00000000 --- a/freedv/branches/1.2/db/format +++ /dev/null @@ -1,2 +0,0 @@ -4 -layout sharded 1000 diff --git a/freedv/branches/1.2/db/fs-type b/freedv/branches/1.2/db/fs-type deleted file mode 100644 index 4fdd9531..00000000 --- a/freedv/branches/1.2/db/fs-type +++ /dev/null @@ -1 +0,0 @@ -fsfs diff --git a/freedv/branches/1.2/db/fsfs.conf b/freedv/branches/1.2/db/fsfs.conf deleted file mode 100644 index cc08cebb..00000000 --- a/freedv/branches/1.2/db/fsfs.conf +++ /dev/null @@ -1,38 +0,0 @@ -### This file controls the configuration of the FSFS filesystem. - -[memcached-servers] -### These options name memcached servers used to cache internal FSFS -### data. See http://www.danga.com/memcached/ for more information on -### memcached. To use memcached with FSFS, run one or more memcached -### servers, and specify each of them as an option like so: -# first-server = 127.0.0.1:11211 -# remote-memcached = mymemcached.corp.example.com:11212 -### The option name is ignored; the value is of the form HOST:PORT. -### memcached servers can be shared between multiple repositories; -### however, if you do this, you *must* ensure that repositories have -### distinct UUIDs and paths, or else cached data from one repository -### might be used by another accidentally. Note also that memcached has -### no authentication for reads or writes, so you must ensure that your -### memcached servers are only accessible by trusted users. - -[caches] -### When a cache-related error occurs, normally Subversion ignores it -### and continues, logging an error if the server is appropriately -### configured (and ignoring it with file:// access). To make -### Subversion never ignore cache errors, uncomment this line. -# fail-stop = true - -[rep-sharing] -### To conserve space, the filesystem can optionally avoid storing -### duplicate representations. This comes at a slight cost in -### performance, as maintaining a database of shared representations can -### increase commit times. The space savings are dependent upon the size -### of the repository, the number of objects it contains and the amount of -### duplication between them, usually a function of the branching and -### merging process. -### -### The following parameter enables rep-sharing in the repository. It can -### be switched on and off at will, but for best space-saving results -### should be enabled consistently over the life of the repository. -### rep-sharing is enabled by default. -# enable-rep-sharing = true diff --git a/freedv/branches/1.2/db/min-unpacked-rev b/freedv/branches/1.2/db/min-unpacked-rev deleted file mode 100644 index 573541ac..00000000 --- a/freedv/branches/1.2/db/min-unpacked-rev +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/freedv/branches/1.2/db/rep-cache.db b/freedv/branches/1.2/db/rep-cache.db deleted file mode 100644 index 63c6f0b8a5181c3954b89f8a6fb505c09e81c4e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmWFz^vNtqRY=P(%1ta$FlJz3U}R))P*7lCU|#F<%m)i%A($C- zAIbAF|6yQa`p&@go%tjdHKRhKAwcgCX!K`f7nhf3Y|1T3Ov*_uN-c;_PE5`~FqoZ# zTpdGP6+#@Hd|Vaa@(LOX3JMvC#Tg1At`Q*$e*Qol>f@sj5aj9W7!;}C?HZ{AR8f># zmRX#cpQqsI7vk#f8U$AelFUy_D^4xJDpj0Wm5Nm&wW1&~FC{f49;*tVp_+zFY~rr+ zj0~ATWfjGRIlx>UpIBOw59Y_iJrHjQXM*xD3phi=ay7kUVbs3S5Eu=C0Sy6OknPB| j{D8V<)bh~~7!3h>h5#4HveEoc&mbSQYcvD~O$Y!0OWInE diff --git a/freedv/branches/1.2/db/revprops/0/0 b/freedv/branches/1.2/db/revprops/0/0 deleted file mode 100644 index d0b90dee..00000000 --- a/freedv/branches/1.2/db/revprops/0/0 +++ /dev/null @@ -1,5 +0,0 @@ -K 8 -svn:date -V 27 -2012-08-21T18:27:59.389906Z -END diff --git a/freedv/branches/1.2/db/revprops/0/1 b/freedv/branches/1.2/db/revprops/0/1 deleted file mode 100644 index 0af71a2e..00000000 --- a/freedv/branches/1.2/db/revprops/0/1 +++ /dev/null @@ -1,13 +0,0 @@ -K 10 -svn:author -V 9 -OFA-Staff -K 8 -svn:date -V 27 -2012-08-21T18:28:08.741468Z -K 7 -svn:log -V 25 -Imported folder structure -END diff --git a/freedv/branches/1.2/db/revs/0/0 b/freedv/branches/1.2/db/revs/0/0 deleted file mode 100644 index 10f5c45f..00000000 --- a/freedv/branches/1.2/db/revs/0/0 +++ /dev/null @@ -1,11 +0,0 @@ -PLAIN -END -ENDREP -id: 0.0.r0/17 -type: dir -count: 0 -text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e -cpath: / - - -17 107 diff --git a/freedv/branches/1.2/db/revs/0/1 b/freedv/branches/1.2/db/revs/0/1 deleted file mode 100644 index fd802a9f..00000000 --- a/freedv/branches/1.2/db/revs/0/1 +++ /dev/null @@ -1,49 +0,0 @@ -id: 3-1.0.r1/0 -type: dir -count: 0 -cpath: /tags -copyroot: 0 / - -id: 0-1.0.r1/62 -type: dir -count: 0 -cpath: /trunk -copyroot: 0 / - -id: 2-1.0.r1/126 -type: dir -count: 0 -cpath: /branches -copyroot: 0 / - -PLAIN -K 8 -branches -V 16 -dir 2-1.0.r1/126 -K 4 -tags -V 14 -dir 3-1.0.r1/0 -K 5 -trunk -V 15 -dir 0-1.0.r1/62 -END -ENDREP -id: 0.0.r1/306 -type: dir -pred: 0.0.r0/17 -count: 1 -text: 1 194 99 99 7b6cc14dddba4e09be5255b475d1a0a8 -cpath: / -copyroot: 0 / - -_0.0.t0-0 add-dir false false /trunk - -_2.0.t0-0 add-dir false false /branches - -_3.0.t0-0 add-dir false false /tags - - -306 431 diff --git a/freedv/branches/1.2/db/transactions/.gitignore b/freedv/branches/1.2/db/transactions/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/db/txn-current b/freedv/branches/1.2/db/txn-current deleted file mode 100644 index d00491fd..00000000 --- a/freedv/branches/1.2/db/txn-current +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/freedv/branches/1.2/db/txn-current-lock b/freedv/branches/1.2/db/txn-current-lock deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/db/txn-protorevs/.gitignore b/freedv/branches/1.2/db/txn-protorevs/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/db/uuid b/freedv/branches/1.2/db/uuid deleted file mode 100644 index 0f362976..00000000 --- a/freedv/branches/1.2/db/uuid +++ /dev/null @@ -1 +0,0 @@ -a56d66ce-6468-4744-9be7-52ce95ca47a4 diff --git a/freedv/branches/1.2/db/write-lock b/freedv/branches/1.2/db/write-lock deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/debian/changelog b/freedv/branches/1.2/debian/changelog deleted file mode 100644 index ddfe80b5..00000000 --- a/freedv/branches/1.2/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -freedv (1.0-150830) unstable; urgency=low - - * Subversion snapshot of tag 1.0. - - -- Stuart Longland Sun, 30 Aug 2015 09:01:13 +1000 diff --git a/freedv/branches/1.2/debian/compat b/freedv/branches/1.2/debian/compat deleted file mode 100644 index ec635144..00000000 --- a/freedv/branches/1.2/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/freedv/branches/1.2/debian/control b/freedv/branches/1.2/debian/control deleted file mode 100644 index d1472b25..00000000 --- a/freedv/branches/1.2/debian/control +++ /dev/null @@ -1,19 +0,0 @@ -Source: fdmdv2 -Section: main -Priority: optional -Maintainer: Stuart Longland -Build-Depends: debhelper (>= 9), cmake, libcodec2-dev, libgtk2.0-dev, - libhamlib-dev, libsamplerate-dev, libasound2-dev, libao-dev, libgsm1-dev, - portaudio19-dev, libsox-dev, libsndfile1-dev, libwxgtk3.0-dev -Standards-Version: 3.9.5 -Homepage: http://www.freedv.org -#Vcs-Git: git://anonscm.debian.org/collab-maint/freedv.git -#Vcs-Browser: http://anonscm.debian.org/?p=collab-maint/freedv.git;a=summary - -Package: freedv -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libcodec2 -Description: FreeDV: Open-Source Digital Voice modem - FreeDV is a digital voice modem that can transmit voice-quality - audio digitally over HF radio links in as little as 1.25kHz - bandwidth in varying conditions. diff --git a/freedv/branches/1.2/debian/copyright b/freedv/branches/1.2/debian/copyright deleted file mode 100644 index b55a293b..00000000 --- a/freedv/branches/1.2/debian/copyright +++ /dev/null @@ -1,38 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: freedv -Source: - -Files: * -Copyright: - -License: - - - . - - -# If you want to use GPL v2 or later for the /debian/* files use -# the following clauses, or change it to suit. Delete these two lines -Files: debian/* -Copyright: 2015 unknown -License: GPL-2+ - This package 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 package 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, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". - -# Please also look if there are files or directories which have a -# different copyright/license attached and list them here. -# Please avoid to pick license terms that are more restrictive than the -# packaged work, as it may make Debian's contributions unacceptable upstream. diff --git a/freedv/branches/1.2/debian/docs b/freedv/branches/1.2/debian/docs deleted file mode 100644 index acfbcb33..00000000 --- a/freedv/branches/1.2/debian/docs +++ /dev/null @@ -1,3 +0,0 @@ -credits.txt -README.txt -README.txt diff --git a/freedv/branches/1.2/debian/format b/freedv/branches/1.2/debian/format deleted file mode 100644 index 163aaf8d..00000000 --- a/freedv/branches/1.2/debian/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/freedv/branches/1.2/debian/rules b/freedv/branches/1.2/debian/rules deleted file mode 100755 index ad892150..00000000 --- a/freedv/branches/1.2/debian/rules +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/make -f -# See debhelper(7) (uncomment to enable) -# output every command that modifies files on the build system. -#DH_VERBOSE = 1 - -# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* -DPKG_EXPORT_BUILDFLAGS = 1 -include /usr/share/dpkg/default.mk - -# see FEATURE AREAS in dpkg-buildflags(1) -#export DEB_BUILD_MAINT_OPTIONS = hardening=+all - -# see ENVIRONMENT in dpkg-buildflags(1) -# package maintainers to append CFLAGS -#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic -# package maintainers to append LDFLAGS -#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed - - -# main packaging script based on dh7 syntax -%: - dh $@ - -# debmake generated override targets -# This is example for Cmake (See http://bugs.debian.org/641051 ) -override_dh_auto_configure: - dh_auto_configure -- \ - -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) \ - -DUSE_STATIC_CODEC2=FALSE \ - -DUSE_STATIC_SPEEXDSP=FALSE diff --git a/freedv/branches/1.2/freedv-dev/.clang/.gitignore b/freedv/branches/1.2/freedv-dev/.clang/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/freedv-dev/CMakeLists.txt b/freedv/branches/1.2/freedv-dev/CMakeLists.txt deleted file mode 100644 index fdc1c01e..00000000 --- a/freedv/branches/1.2/freedv-dev/CMakeLists.txt +++ /dev/null @@ -1,463 +0,0 @@ -# -# FreeDV - HF Digital Voice for Radio Amateurs -# -# CMake configuration contributed by Richard Shaw (KF5OIM) -# Please report questions, comments, problems, or patches to the freetel -# mailing list: https://lists.sourceforge.net/lists/listinfo/freetel-codec2 -# - -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7" CACHE STRING "Minimum OS X deployment version") - -cmake_minimum_required(VERSION 2.8) - -# Prevent in-source builds to protect automake/autoconf config. -# If an in-source build is attempted, you will still need to clean up a few -# files manually. -set(CMAKE_DISABLE_SOURCE_CHANGES ON) -set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) -if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") - message(FATAL_ERROR "In-source builds in ${CMAKE_BINARY_DIR} are not " - "allowed, please remove ./CMakeCache.txt and ./CMakeFiles/, create a " - "separate build directory and run cmake from there.") -endif("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") - -# Set local module path. -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") - -project(FreeDV) - -# -# Set FreeDV version and generate src/version.h -# -set(FREEDV_VERSION_MAJOR 1) -set(FREEDV_VERSION_MINOR 2) -set(FREEDV_VERSION_PATCH 2) -set(FREEDV_VERSION ${FREEDV_VERSION_MAJOR}.${FREEDV_VERSION_MINOR}) -if(FREEDV_VERSION_PATCH) - set(FREEDV_VERSION ${FREEDV_VERSION}.${FREEDV_VERSION_PATCH}) -endif() -set(FREEDV_VERSION_SUFFIX "devel") -if(FREEDV_VERSION_SUFFIX) - set(FREEDV_VERSION_STRING "${FREEDV_VERSION} ${FREEDV_VERSION_SUFFIX}") -else() - set(FREEDV_VERSION_STRING "${FREEDV_VERSION}") -endif() -message(STATUS "FreeDV version: ${FREEDV_VERSION_STRING}") -configure_file(cmake/version.h.in src/version.h @ONLY) - -# Set default build type -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug") - message(STATUS "Build type not specified, defaulting to ${CMAKE_BUILD_TYPE}") -endif(NOT CMAKE_BUILD_TYPE) - -# Work around for not using a svn working copy. -add_definitions(-D_NO_AUTOTOOLS_) -find_program(SVN_PATH svn) -if(SVN_PATH) - execute_process(COMMAND ${SVN_PATH} info --show-item revision - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE SVN_REVISION_RESULT - OUTPUT_VARIABLE SVN_CURRENT_REVISION - ERROR_QUIET - ) -else() - set(SVN_REVISION_RESULT 1) -endif() - -if(SVN_REVISION_RESULT EQUAL 0) - string(STRIP ${SVN_CURRENT_REVISION} SVN_REVISION) - add_definitions(-DSVN_REVISION="${SVN_REVISION}") -else() - add_definitions(-DSVN_REVISION="None") -endif() - - -# Set default build flags. -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") -if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11") -endif(APPLE) - -# -# Setup cmake options -# -set(CMAKE_VERBOSE_MAKEFILE TRUE CACHE BOOL "Verbose makefile.") -set(USE_STATIC_DEPS FALSE CACHE BOOL - "Download and build static libraries instead of system libraries.") -set(USE_STATIC_PORTAUDIO FALSE CACHE BOOL - "Download and build static portaudio instead of the system library.") -set(USE_STATIC_SNDFILE FALSE CACHE BOOL - "Download and build static sndfile instead of the system library.") -set(USE_STATIC_SAMPLERATE FALSE CACHE BOOL - "Download and build static samplerate instead of the system library.") -set(USE_STATIC_CODEC2 TRUE CACHE BOOL - "Download and build static codec2 instead of the system library.") -set(USE_STATIC_SPEEXDSP TRUE CACHE BOOL - "Download and build static speex instead of the system library.") -set(BOOTSTRAP_WXWIDGETS FALSE CACHE BOOL - "Download and build static wxWidgets instead of the system library.") - -if(USE_STATIC_DEPS) - set(USE_STATIC_PORTAUDIO TRUE FORCE) - set(USE_STATIC_SNDFILE TRUE FORCE) - set(USE_STATIC_SAMPLERATE TRUE FORCE) - set(USE_STATIC_CODEC2 TRUE FORCE) -endif(USE_STATIC_DEPS) - -# -# Pull in external wxWidgets target if performing static build. -# -if(BOOTSTRAP_WXWIDGETS) - message(STATUS "Adding wxWidgets build target...") - include(cmake/BuildWxWidgets.cmake) -endif(BOOTSTRAP_WXWIDGETS) - -# -# Perform bootstrap build of wxWidgets -# -if(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) - message(STATUS "Will perform bootstrap build of wxWidgets. - After make step completes, re-run cmake and make again to perform FreeDV build.") -# -# Continue normal build if not bootstrapping wxWidgets or is already built. -# -else(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) - - -# -# Various hacks and work arounds for building under MinGW. -# -if(MINGW) - message(STATUS "System is MinGW.") - # Setup HOST variable. - include(cmake/MinGW.cmake) - # This sets up the exe icon for windows under mingw. - set(RES_FILES "") - set(RES_FILES "${CMAKE_SOURCE_DIR}/contrib/freedv.rc") - set(CMAKE_RC_COMPILER_INIT windres) - enable_language(RC) - set(CMAKE_RC_COMPILE_OBJECT - " -O coff -i -o ") - include(InstallRequiredSystemLibraries) -endif(MINGW) - -# Math library is automatic on MinGW -if(UNIX) - set(CMAKE_REQUIRED_INCLUDES math.h) - set(CMAKE_REQUIRED_LIBRARIES m) -endif(UNIX) - -# Find some standard headers and functions. -include(CheckIncludeFiles) -check_include_files("byteswap.h" HAVE_BYTESWAP_H) -check_include_files("limits.h" HAVE_LIMITS_H) -check_include_files("stddef.h" HAVE_STDDEF_H) -check_include_files("stdlib.h" HAVE_STDLIB_H) -check_include_files("string.h" HAVE_STRING_H) -check_include_files("strings.h" HAVE_STRINGS_H) -check_include_files("ltdl.h" HAVE_LTDL_H) -check_include_files("inttypes.h" HAVE_INTTYPES_H) -check_include_files("sys/stat.h" HAVE_SYS_STAT_H) -check_include_files("sys/types.h" HAVE_SYS_TYPES_H) - -include(CheckTypeSize) -check_type_size("int" SIZEOF_INT) - -include(CheckFunctionExists) -check_function_exists(floor HAVE_FLOOR) -check_function_exists(memset HAVE_MEMSET) -check_function_exists(pow HAVE_POW) -check_function_exists(sqrt HAVE_SQRT) -check_function_exists(fseeko HAVE_FSEEKO) -check_function_exists(fmemopen HAVE_FMEMOPEN) -check_function_exists(strcasecmp HAVE_STRCASECMP) -check_function_exists(vsnprintf HAVE_VSNPRINTF) - -include(CheckSymbolExists) -check_symbol_exists("_fseeki64" "stdio.h" HAVE__FSEEKI64) - -# fdmdv2_main.h requires patching to find config.h as it current looks in the -# source directory and the generated file goes in the binary directory. -configure_file ("${PROJECT_SOURCE_DIR}/cmake/config.h.in" - "${PROJECT_BINARY_DIR}/config.h" ) -include_directories(${PROJECT_BINARY_DIR}) -add_definitions(-DHAVE_CONFIG_H) - -# Config file for bundled sox sources -configure_file("${PROJECT_SOURCE_DIR}/cmake/soxconfig.h.in" - "${PROJECT_BINARY_DIR}/soxconfig.h") - -# Pthread Library -find_package(Threads REQUIRED) -message(STATUS "Threads library flags: ${CMAKE_THREAD_LIBS_INIT}") - -# -# Find codec2 -# -if(NOT USE_STATIC_CODEC2) - message(STATUS "Looking for codec2...") - # 'CONFIG' removed due to incompatibility with cmake version - # in Ubuntu 12.04 (Precise) -- Stuart Longland - find_package(codec2 QUIET) - if(codec2_FOUND) - get_target_property(CODEC2_LIBRARY codec2 LOCATION) - message(STATUS " codec2 library: ${CODEC2_LIBRARY}") - message(STATUS " codec2 headers: ${codec2_INCLUDE_DIRS}") - else() - # Try to find manually - find_path(CODEC2_INCLUDE_DIRS codec2.h - PATH_SUFFIXES codec2) - find_library(CODEC2_LIBRARY NAMES codec2) - if(CODEC2_LIBRARY AND CODEC2_INCLUDE_DIRS) - message(STATUS " codec2 library: ${CODEC2_LIBRARY}") - message(STATUS " codec2 headers: ${CODEC2_INCLUDE_DIRS}") - list(APPEND FREEDV_LINK_LIBS ${CODEC2_LIBRARY}) - include_directories(${CODEC2_INCLUDE_DIRS}) - else() - message(FATAL_ERROR "codec2 library not found. -Linux: -Codec2 may not be in your distribution so build yourself or use the cmake option to build statically into FreeDV. -Windws: -It's easiest to use the cmake option: USE_STATIC_CODEC2" - ) - endif() - endif() -else(NOT USE_STATIC_CODEC2) - message(STATUS "Will attempt static build of codec2.") - include(cmake/BuildCodec2.cmake) -endif(NOT USE_STATIC_CODEC2) - -# -# Find or build portaudio Library -# -if(NOT USE_STATIC_PORTAUDIO) - message(STATUS "Looking for portaudio...") - find_package(Portaudio REQUIRED) - if(PORTAUDIO_FOUND) - message(STATUS " portaudio library: ${PORTAUDIO_LIBRARIES}") - message(STATUS " portaudio headers: ${PORTAUDIO_INCLUDE_DIRS}") - list(APPEND FREEDV_LINK_LIBS ${PORTAUDIO_LIBRARIES}) - include_directories(${PORTAUDIO_INCLUDE_DIRS}) - else() - message(FATAL_ERROR "portaudio library not found. -On Linux systems try installing: - portaudio-devel (RPM based systems) - libportaudio-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_PORTAUDIO" - ) - endif() - if(NOT ${PORTAUDIO_VERSION} EQUAL 19) - message(WARNING "Portaudio versions other than 19 are known to have issues. You have been warned!") - endif() -else(NOT USE_STATIC_PORTAUDIO) - message(STATUS "Will attempt static build of portaudio.") - include(cmake/BuildPortaudio.cmake) -endif(NOT USE_STATIC_PORTAUDIO) - -# -# Hamlib library -# -message(STATUS "Looking for hamlib...") -find_path(HAMLIB_INCLUDE_DIR hamlib/rig.h) -find_library(HAMLIB_LIBRARY hamlib PATH_SUFFIXES hamlib) -message(STATUS "Hamlib library: ${HAMLIB_LIBRARY}") -message(STATUS "Hamlib headers: ${HAMLIB_INCLUDE_DIR}") -if(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - message(STATUS "Hamlib library found.") - include_directories(${HAMLIB_INCLUDE_DIR}) - list(APPEND FREEDV_LINK_LIBS ${HAMLIB_LIBRARY}) -else(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - message(FATAL_ERROR "hamlib not found. -On Linux systems try installing: - hamlib-devel (RPM based systems) - libhamlib-dev (DEB based systems)" - ) -endif(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - - -# -# Samplerate Library -# -if(NOT USE_STATIC_SAMPLERATE) - message(STATUS "Looking for samplerate...") - find_library(LIBSAMPLERATE samplerate) - find_path(LIBSAMPLERATE_INCLUDE_DIR samplerate.h) - message(STATUS " samplerate library: ${LIBSAMPLERATE}") - message(STATUS " samplerate headers: ${LIBSAMPLERATE_INCLUDE_DIR}") - if(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) - list(APPEND FREEDV_LINK_LIBS ${LIBSAMPLERATE}) - include_directories(${LIBSAMPLERATE_INCLUDE_DIR}) - else(LIBSTAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) - message(FATAL_ERROR "samplerate library not found. -On Linux systems try installing: - samplerate-devel (RPM based systems) - libsamplerate-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_SAMPLERATE" - ) - endif(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) -else(NOT USE_STATIC_SAMPLERATE) - message(STATUS "Will attempt static build of samplerate.") - include(cmake/BuildSamplerate.cmake) -endif(NOT USE_STATIC_SAMPLERATE) - -# -# sndfile Library -# -if(NOT USE_STATIC_SNDFILE) - message(STATUS "Looking for sndfile...") - find_library(LIBSNDFILE sndfile) - find_path(LIBSNDFILE_INCLUDE_DIR sndfile.h) - message(STATUS " sndfile library: ${LIBSNDFILE}") - message(STATUS " sndfile headers: ${LIBSNDFILE_INCLUDE_DIR}") - if(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) - list(APPEND FREEDV_LINK_LIBS ${LIBSNDFILE}) - else(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) - message(FATAL_ERROR "sndfile library not found. -On Linux systems try installing: - libsndfile-devel (RPM based systems) - libsndfile-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_SNDFILE" - ) - endif(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) -else(NOT USE_STATIC_SNDFILE) - message(STATUS "Will attempt static build of sndfile.") - include(cmake/BuildSndfile.cmake) -endif(NOT USE_STATIC_SNDFILE) - -# -# Find wxWidgets -# -if(NOT BOOTSTRAP_WXWIDGETS) - set(WXCONFIG "" CACHE FILEPATH "Location of wx-config binary.") - set(WXRC "" CACHE FILEPATH "Location of wxrc binary.") -endif(NOT BOOTSTRAP_WXWIDGETS) -#if(BOOTSTRAP_WXWIDGETS) -# set(WXCONFIG "${CMAKE_BINARY_DIR}/external/dist/bin/wx-config") -# set(WXRC "${CMAKE_BINARY_DIR}/external/dist/bin/wxrc") -# list(APPEND FREEDV_STATIC_DEPS wxWidgets) -#endif(BOOTSTRAP_WXWIDGETS) -message(STATUS "Looking for wxWidgets...") -if(WXCONFIG) - message(STATUS "wx-config: ${WXCONFIG}") - set(wxWidgets_CONFIG_EXECUTABLE ${WXCONFIG}) -endif(WXCONFIG) -if(WXRC) - message(STATUS "wxrc: ${WXRC}") - set(wxWidgets_wxrc_EXECUTABLE ${WXRC}) -endif(WXRC) -set(WX_VERSION_MIN 3.0.0) -find_package(wxWidgets REQUIRED core base aui html net adv) -execute_process(COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" --version - OUTPUT_VARIABLE WX_VERSION) -string(STRIP ${WX_VERSION} WX_VERSION) -if(WX_VERSION VERSION_EQUAL ${WX_VERSION_MIN} - OR WX_VERSION VERSION_GREATER ${WX_VERSION_MIN}) - message(STATUS "wxWidgets version: ${WX_VERSION}") -else() - message(FATAL_ERROR "wxWidgets must be installed on your system. -Please check that wx-config is in path, the directory -where wxWidgets libraries are installed (returned by -'wx-config --libs' or 'wx-config --static --libs' command) -is in LD_LIBRARY_PATH or equivalent variable and -wxWidgets version is ${WX_VERSION_MIN} or above.") -endif() -if(wxWidgets_FOUND) - include("${wxWidgets_USE_FILE}") - list(APPEND FREEDV_LINK_LIBS ${wxWidgets_LIBRARIES}) -endif(wxWidgets_FOUND) - -# -# Find speex library -# -if(NOT USE_STATIC_SPEEXDSP) - message(STATUS "Looking for Speex DSP library.") - find_path(SPEEXDSP_INCLUDE_DIR NAMES speex/speex.h speex/speexdsp_types.h) - find_library(SPEEXDSP_LIBRARY speexdsp) - message(STATUS " Speex DSP headers: ${SPEEXDSP_INCLUDE_DIR}") - message(STATUS " Speex DSP library: ${SPEEXDSP_LIBRARY}") - if(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) - include_directories(${SPEEXDSP_INCLUDE_DIR}) - list(APPEND FREEDV_LINK_LIBS ${SPEEXDSP_LIBRARY}) - else(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) - message(FATAL_ERROR "Speex DSP library not found!") - endif(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) -else() - message(STATUS "Will attempt static build of speex.") - include(cmake/BuildSpeex.cmake) -endif() - -# -# Find libdl for dlopen/dlclose -# -if(UNIX) - message(STATUS "Looking for dl library.") - find_library(DL_LIBRARY dl) - if(DL_LIBRARY) - message(STATUS " dl library: ${DL_LIBRARY}") - list(APPEND FREEDV_LINK_LIBS ${DL_LIBRARY}) - else() - message(FATAL_ERROR "dl library not found. -On Linux systems try installing: - glibc-devel (RPM based systems) - glibc-dev (DEB based systems)" - ) - endif() -endif(UNIX) - - -#Freedv -add_subdirectory(src) - -# Icons and desktop file -add_subdirectory(contrib) - -message(STATUS "Build type will be: ${CMAKE_BUILD_TYPE}") - -# -# Cpack NSIS configuration for Windows. -# -if(WIN32) - # Detect if we're doing a 32-bit or 64-bit windows build. - if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(CMAKE_CL_64 TRUE) - set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") - endif() - if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - set(CPACK_STRIP_FILES TRUE) - endif() - configure_file(cmake/GetDependencies.cmake.in cmake/GetDependencies.cmake - @ONLY - ) - install(SCRIPT ${CMAKE_BINARY_DIR}/cmake/GetDependencies.cmake) - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "HF Digital Voice for Radio Amateurs") - set(CPACK_PACKAGE_VENDOR "CMake") - #set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README") - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") - set(CPACK_PACKAGE_VERSION_MAJOR ${FREEDV_VERSION_MAJOR}) - set(CPACK_PACKAGE_VERSION_MINOR ${FREEDV_VERSION_MINOR}) - # CPack expects a patch level version so set it here and override if we - # are actually setting one. - set(CPACK_PACKAGE_VERSION_PATCH 0) - if(FREEDV_VERSION_PATCH) - set(CPACK_PACKAGE_VERSION_PATCH ${FREEDV_VERSION_PATCH}) - endif() - if(FREEDV_VERSION_SUFFIX) - set(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${FREEDV_VERSION_SUFFIX}") - endif() - # There is a bug in NSI that does not handle full unix paths properly. Make - # sure there is at least one set of four (4) backlasshes. - #set(CPACK_PACKAGE_ICON "${CMake_SOURCE_DIR}/Utilities/Release\\\\InstallIcon.bmp") - set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\freedv.exe") - set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY}") - set(CPACK_NSIS_PACKAGE_NAME "FreeDV") - set(CPACK_PACKAGE_EXECUTABLES freedv;FreeDV) - set(CPACK_NSIS_URL_INFO_ABOUT "http://freedv.org") - set(CPACK_NSIS_MODIFY_PATH OFF) - set(CPACK_NSIS_MENU_LINKS - "http://freedv.org" "FreeDV Homepage") - include(CPack) -endif(WIN32) - -endif(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) diff --git a/freedv/branches/1.2/freedv-dev/COPYING b/freedv/branches/1.2/freedv-dev/COPYING deleted file mode 100644 index cfd4e991..00000000 --- a/freedv/branches/1.2/freedv-dev/COPYING +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see - . - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/freedv/branches/1.2/freedv-dev/README.osx b/freedv/branches/1.2/freedv-dev/README.osx deleted file mode 100644 index c71544ff..00000000 --- a/freedv/branches/1.2/freedv-dev/README.osx +++ /dev/null @@ -1,107 +0,0 @@ -Building under OSX is similar to building under linux, but there are some additional steps that need to be performed to produce a working app-bundle. - -For the following instructions, I'm assuming you will be placing everything in: -/Users//Dev/ - -1/ DEPENDENCIES -Using Macports, most of the appropriate dependencies can be installed by: - -$ sudo port install subversion git libtool libsamplerate sox portaudio dylibbundler cmake - -It should be fairly similar using HomeBrew, but you will need to replace all the /opt/ paths in the following instructions. - -1.1/ HAMLIB -First, we will need to build hamlib from source, as we need hamlib to be statically compiled (Macports won't do this..) - -$ git clone git://git.code.sf.net/p/hamlib/code hamlib-code -$ cd hamlib-code - -You will now need to edit line 12 of autogen.sh, to change "libtoolize" to "glibtoolize" - -$ ./autogen.sh -$ ./configure --disable-shared --prefix /Users//Dev/hamlib -$ make -$ make install - -You should now have an installation of hamlib in ~/Dev/hamlib - -Just in case you have hamlib installed via Macports, it may be a good idea to run -$ sudo port deactivate hamlib - -1.2/ WXWIDGETS -To be able to produce an appbundle, we need wxWidgets to be build statically. Again, Macports won't do this out of the box. - -Edit the wxWidgets-3.0 port file using: -$ sudo port edit wxWidgets-3.0 - -and add the following to the bottom of the file: - -variant static description { build a static version of the libraries with some other options... } { - configure.args-append --enable-std_iostreams - configure.args-append --disable-shared - configure.args-delete --with-sdl - configure.args-delete --with-opengl - set installtype release-static -} - -Now you can build and install a static variant of wxWidgets with: -$ sudo port install wxWidgets-3.0 +static - -Note: This will probably break anything else which is using wxWidgets. Once you have finished building FreeDV, you may -want to go back to the dynamically compiled version using: -$ sudo port install wxWidgets-3.0 - -HomeBrew Users: Anyone know how to do the above? - -1.3/ CODEC2 LIBRARIES -The FreeDV CMake procedure will automatically checkout and compile Codec2. -If you want to build and install your own copy (i.e. for access to the command-line tools), you can do so: - -$ wget http://files.freedv.org/codec2/codec2-0.4.tar.gz -or -$ svn checkout https://svn.code.sf.net/p/freetel/code/codec2-dev/ - -$ cd codec2-0.4 -or -cd codec2-dev -$ mkdir build_osx && cd build_osx -$ cmake ../ && make -$ sudo make install - -3/ BUILDING FREEDV -Get the FreeDV source by either: - -Getting the current 'stable' release (1.0): -$ wget http://files.freedv.org/freedv/freedv-1.0.tar.gz -$ tar -xzf freedv-1.0.tar.gz - -or - -Checking the latest revision out from SVN: -$ svn checkout https://svn.code.sf.net/p/freetel/code/freedv-dev/ - -$ cd freedv-1.0 -or -$ cd freedv-dev - -$ mkdir build_osx && cd build_osx - -Assuming you are intending on building Codec2 as part of the build process, run: - -$ cmake -DWXCONFIG=/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.0/lib/wx/config/osx_cocoa-unicode-static-3.0 -DCMAKE_EXE_LINKER_FLAGS="-L/opt/local/lib" -DHAMLIB_INCLUDE_DIR=../../hamlib/include -DHAMLIB_LIBRARY=../../hamlib/lib/libhamlib.a ../ - -Then, build FreeDV: -$ make - -The build process will create an appbundle (FreeDV.app) and a compressed disk image (FreeDV.dmg) in ./build_osx/src -Move these to wherever you want, and run! - -Happy DVing! - -Acknowledgements: -A big thank you to Mooneer Salem, K6AQ, for walking me through this process, and figuring out how to solve the wxWidgets and Hamlib issues. - -Please e-mail any corrections to either the digitalvoice google group list, or myself, at: -vk5qi(at)rfhead.net -Mark Jessop VK5QI - diff --git a/freedv/branches/1.2/freedv-dev/README.txt b/freedv/branches/1.2/freedv-dev/README.txt deleted file mode 100644 index 26e731d1..00000000 --- a/freedv/branches/1.2/freedv-dev/README.txt +++ /dev/null @@ -1,233 +0,0 @@ -================================== - FreeDV GUI README.txt -================================== - -This document describes how to build the FreeDV GUI program for -various operating systems. See also: - - http://freedv.org - introduction, documentation, downloads - RELEASE_NOTES.txt - changes made to each version - USER_MANUAL.txt - FreeDV GUI Manual - -================================== - Building and installing on Linux -================================== - -First the basics: - - $ sudo apt-get install build-essential cmake subversion - -To install the required development libraries instead of building them -statically: - -Debian/Ubuntu: - - $ sudo apt-get install libwxgtk3.0-dev portaudio19-dev \ - libhamlib-dev libsamplerate-dev libasound2-dev libao-dev libgsm1-dev \ - libsndfile-dev - -Fedora: - - $ sudo dnf install wxGTK3-devel portaudio-devel libsamplerate-devel \ - libsndfile-devel speexdsp-devel hamlib-devel alsa-lib-devel libao-devel \ - gsm-devel - - -RHEL/CentOS and derivitves (requires Fedora EPEL repository) - - $ sudo yum install wxGTK3-devel portaudio-devel libsamplerate-devel \ - libsndfile-devel speexdsp-devel hamlib-devel alsa-lib-devel libao-devel \ - gsm-devel - - -Quickstart 1 ------------- - -1/ Using a modern Linux, and the development library packages - installed above: - - $ cd /path/to/freedv - $ mkdir build_linux - $ cd build_linux - $ cmake ../ - $ make - $ ./src/freedv - -Quickstart 2 ------------- - -Builds static versions of wxWidgets, portaudio, codec2-dev, which are commonly -missing on older Linux systems. - -1/ Assumes static build of wxWidgets and the freedv-dev source is checked out into ~/freedv-dev: - - $ cd ~/freedv-dev - $ mkdir build_linux - $ cd build_linux - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE ../ - $ make - -2/ Then you can configure FreeDV using your local codec-dev, something like: - - $ cmake -DCMAKE_BUILD_TYPE=Debug -DBOOTSTRAP_WXWIDGETS=TRUE -DCODEC2_INCLUDE_DIRS=/path/to/codec2-dev/src -DCODEC2_LIBRARY=/path/to/codec2-dev/build_linux/src/libcodec2.so -DUSE_STATIC_CODEC2=FALSE -DUSE_STATIC_PORTAUDIO=TRUE ../ - -3/ OR build a local copy of codec2-dev: - - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE -DUSE_STATIC_CODEC2=TRUE -DUSE_STATIC_PORTAUDIO=TRUE ../ - -4/ Build and run FreeDV: - - $ make - $ ./src/freedv - -======================================================= - Building for Windows on Ubuntu Linux (Cross compiling) -======================================================= - -1/ Install the cross compiling toolchain: - - $ sudo apt-get install mingw-w64 - -2/ Patch cmake using: http://www.cmake.org/gitweb?p=stage/cmake.git;a=patch;h=33286235048495ceafb636d549d9a4e8891967ae - -3/ Checkout a fresh copy of codec2-dev and build for Windows, pointing to the generate_codebook built by a linux build of generate_codebook, using this cmake line - - $ cmake .. -DCMAKE_TOOLCHAIN_FILE=/home/david/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake -DUNITTEST=FALSE -DGENERATE_CODEBOOK=/home/david/codec2-dev/build_linux/src/generate_codebook - -4/ Build WxWidgets - - $ cd /path/to/freedv-dev - $ mkdir build_windows - $ cd build_windows - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE .. -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-Ubuntu-mingw32.cmake -DCMAKE_BUILD_TYPE=Debug - $ make - -5/ Download and install the Windows version of Hamlib: - - $ wget http://internode.dl.sourceforge.net/project/hamlib/hamlib/1.2.15.3/hamlib-win32-1.2.15.3.zip - $ unzip hamlib-win32-1.2.15.3.zip - -6/ Build All the libraries and FreeDV: - - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-Ubuntu-mingw32.cmake -DUSE_STATIC_PORTAUDIO=TRUE -DUSE_STATIC_SNDFILE=TRUE -DUSE_STATIC_SAMPLERATE=TRUE -DUSE_STATIC_CODEC2=FALSE -DCODEC2_INCLUDE_DIRS=/home/david/tmp/codec2-dev/src -DCODEC2_LIBRARY=/home/david/tmp/codec2-dev/build_windows/src/libcodec2.dll.a -DHAMLIB_INCLUDE_DIR=hamlib-win32-1.2.15.3/include -DHAMLIB_LIBRARY=hamlib-win32-1.2.15.3/lib/gcc/libhamlib.dll.a -DCMAKE_BUILD_TYPE=Debug .. - $ make - -7/ Test on Linux with "wine", this will tell you if any DLLs are missing: - - $ wine src/freedv.exe - -8/ When moving to an actual Windows machine, I needed: - - /usr/lib/gcc/i686-w64-mingw32/4.8/libstdc++-6.dll - /usr/lib/gcc/i686-w64-mingw32/4.8/libgcc_s_sjlj-1.dll - /usr/i686-w64-mingw32/lib/libwinpthread-1.dll - - Wine seems to find these automagically, so I found them on my system by - looking at ~/.wine/system.reg for PATH: - - [System\\CurrentControlSet\\Control\\Session Manager\\Environment] 1423800803 - "PATH"=str(2):"C:\\windows\\system32;C:\\windows;C:\\windows\\system32\\wbem;Z:\\usr\\i686-w64-mingw32\\lib;Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\4.8" - - -==================================== - Building and installing on Windows -==================================== - -The windows build is similar to linux and follows the same basic workflow, -however, while codec2 and FreeDV (freedv) build well on windows, some of the -dependencies do not. For that reson current windows releases are cross-compiled -from linux. - -Only MinGW is supported. While it is likely possible to perform a pure MinGW -build, installing MSYS2 will make your life easier. - -CMake may not automatically detect that you're in the MSYS environment. If this -occurs you need to pass cmake the proper generator: - -cmake -G"MSYS Makefiles" [other options] - -=============================== - Bootstrapping wxWidgets build -=============================== - -If wxWidgets (>= 3.0) is not available then one option is to have CMake boot- -strap the build for FreeDV. - -This is required because the tool wx-config is used to get the correct compiler -and linker flags of the wxWidgets components needed by FreeDV. Since this is -normally done at configure time, not during "make", it is not possible for CMake -or have this information prior to building wxWidgets. - -In order to work around this issue you can "bootstrap" the wxWidgets build using -the CMake option, "BOOTSTRAP_WXWIDGETS". wxWidgets will be built using static -libraries. - -NOTE: This forces "USE_STATIC_WXWIDGETS" to be true internally regarless of the -value set manually. - -(from any directory, but empty directory outside of the source is prefered.) -$ cmake -DBOOTSTRAP_WXWIDGETS=TRUE /path/to/freedv -$ make -(wxWidgets is downloaded and built) -$ cmake . -(wxWidgets build should be detected) -$ make -(if all goes well, as root) -$ make install - -==================================== - Building and installing on OSX -==================================== - -Pls see README.osx - -==================================== - Building and installing on FreeBSD -==================================== - -As per "Quickstart 2" above but change build_linux to build_freebsd - -======= -Editing -======= - -Please make sure your text editor does not insert tabs, and -used indents of 4 spaces. The following .emacs code was used to -configure emacs: - -(setq-default indent-tabs-mode nil) - -(add-hook 'c-mode-common-hook - (function (lambda () - (setq c-basic-offset 4) - ))) - -FreeDV GUI TODO List --------------------- - -[ ] Ubuntu packaging -[ ] default sound card in/out setting for rx out of the box -[ ] When application close on windows while "Start" down sometimes crashes - + Also on Linux it reports an unterminated thread when exiting -[ ] Tool-Audio Config Dialog sound device names truncated on Windows -[ ] Serialport::closeport() on Linux takes about 1 second - + delays 'Stop' on main window test on Tools-PTT Test -[ ] Voice keyer file name at bottom on main screen truncated - + need a bigger field -[ ] Start/Stop file rec/playback, work out a better UI, - maybe buttons on front page -[ ] feature for evaluating yr own sound quality - + trap bad mic response/levels - + zero in on different sound quality from different users -[ ] feeding audio over UDP say from from gqrx - + could also be used to netcat stored files -[ ] refactoring - [ ] fdmdv2_main.cpp is way too long - [ ] rename fdmdv2*.cpp -> freedv*.cpp - [ ] dlg_ptt uses ComPortsDlg name internally, rename PttDlg or similar -[ ] Add RSID - + use case, when would it be used? -[ ] clean up dialogs - + were based on auto generation code - + must be an easier/clearer way to write them - diff --git a/freedv/branches/1.2/freedv-dev/RELEASE_NOTES.txt b/freedv/branches/1.2/freedv-dev/RELEASE_NOTES.txt deleted file mode 100644 index 79eaeca0..00000000 --- a/freedv/branches/1.2/freedv-dev/RELEASE_NOTES.txt +++ /dev/null @@ -1,7 +0,0 @@ -V1.2.2 July 2017 ----------------- - -1/ Improvements to Hamlib support, error message reporting, serial rate box. - -2/ Disabled unused UDP comms/egexp processing to clean up Options dialog. - diff --git a/freedv/branches/1.2/freedv-dev/USER_MANUAL.txt b/freedv/branches/1.2/freedv-dev/USER_MANUAL.txt deleted file mode 100644 index 2cc44945..00000000 --- a/freedv/branches/1.2/freedv-dev/USER_MANUAL.txt +++ /dev/null @@ -1,100 +0,0 @@ -====================== -FREEDV GUI USER MANUAL -====================== - -Introduction ------------- - -This document describes additional features in the latest FreeDV -releases that haven't been documented in other sources. See also -freedv.org - -PTT Configuration ------------------ - -Tools-PTT Dialog - -Hamlib comes with a default serial rate for each radio. If your radio -has a different serial rate change the Serial Rate drop down box to -match your radio. - -When "Test" is pressed, the "Serial Params" field is populated and -displayed. This will help track down any mis-matches between Hamlib -and your radio. - -Voice Keyer ------------ - -Voice Keyer Button on Front Page -Options-PTT Dialog - -Puts FreeDV and your radio into transmit, reads a wave file of your -voice to call CQ, then switches to receive to see if anyone is -replying. If you press space bar the voice keyer stops. If a signal -with a valid sync is received for a few seconds the voice keyer stops. - -Options-PTT dialog can be used to select the wave file, set the Rx -delay, and number of times the tx/rx cycle repeats. - -The wave file for the voice keyer should be in 8kHz mono 16 bit sample -form. Use a free application such as Audacity to convert a file you -have recorded to this format. - -Test Frame Histogram --------------------- - -Test Frame Histogram tab on Front Page - -Displays BER of each carrier when in "test frame" mode. As each QPSK -carrier has 2 bits there are 2*Nc histogram points. - -Ideally all carriers will have about the same BER (+/- 20% after 5000 -total bit errors). However problems can occur with filtering in the -tx path. If one carrier has less power, then it will have a higher -BER. The errors in this carrier will tend to dominate overall -BER. For example if one carrier is attenuated due to SSB filter ripple -in the tx path then the BER on that carrier will be higher. This is -bad news for DV. - -Suggested usage: - -i) Transmit FreeDV in test frame mode. Use a 2nd rx (or -get a friend) to monitor your rx signal with FreeDV in test frame -mode. - -ii) Adjust your rx SNR to get a BER of a few % (e.g. reduce tx -power, use a short antenna for the rx, point your beam away, adjust rx -RF gain). - -iii) Monitor the error histogram for a few minutes, until you -have say 5000 total bit errors. You have a problem if the BER of any -carrier is more than 20% different from the rest. - -A typical issue will be one carrier at 1.0, the others at 0.5, -indicating the poorer carrier BER is twice the larger. - -Full Duplex Testing with loopback ---------------------------------- - -Options - Half Duplex check box - -FreeDV GUI can operate in full duplex mode which is useful for -development of listening to your own FreeDV signal as only one PC is -required. Normal operation is half duplex. - -Tx and Rx signals can be looped back via an analog connection between -the sound cards. - -On Linux, using the Alsa loopback module: - - $ sudo modprobe snd-aloop - $ ./freedv - - In Tools - Audio Config - Receive Tab - From Radio select -> Loopback: Loopback PCM (hw:1,0) - - Transmit Tab - To Radio select -> Loopback: Loopback PCM (hw:1,1) - -TODO ----- - -[ ] Merge this information into existing start up guides - diff --git a/freedv/branches/1.2/freedv-dev/cmake/BuildCodec2.cmake b/freedv/branches/1.2/freedv-dev/cmake/BuildCodec2.cmake deleted file mode 100644 index 038826d5..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/BuildCodec2.cmake +++ /dev/null @@ -1,26 +0,0 @@ -set(SPEEXDSP_CMAKE_ARGS -DBUILD_SHARED_LIBS=FALSE -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external/dist) - -if(USE_STATIC_SPEEXDSP) - list(APPEND SPEEXDSP_CMAKE_ARGS - -DSPEEXDSP_LIBRARIES=${CMAKE_BINARY_DIR}/external/dist/lib/libspeexdsp.a - -DSPEEXDSP_INCLUDE_DIR=${CMAKE_BINARY_DIR}/external/dist/include) -endif() - -set(CODEC2_CMAKE_ARGS -DUNITTEST=FALSE) - -if(CMAKE_CROSSCOMPILING) - set(CODEC2_CMAKE_ARGS ${CODEC2_CMAKE_ARGS} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) -endif() - -include(ExternalProject) -ExternalProject_Add(codec2 - SVN_REPOSITORY https://svn.code.sf.net/p/freetel/code/codec2-dev - CMAKE_ARGS ${CODEC2_CMAKE_ARGS} ${SPEEXDSP_CMAKE_ARGS} - CMAKE_CACHE_ARGS -DCMAKE_OSX_DEPLOYMENT_TARGET:string=10.7 - INSTALL_COMMAND "" -) -set(CODEC2_LIBRARIES - ${CMAKE_BINARY_DIR}/codec2-prefix/src/codec2-build/src/libcodec2.a) -include_directories(${CMAKE_BINARY_DIR}/codec2-prefix/src/codec2/src) -list(APPEND FREEDV_LINK_LIBS ${CODEC2_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS codec2) diff --git a/freedv/branches/1.2/freedv-dev/cmake/BuildHamlib.cmake b/freedv/branches/1.2/freedv-dev/cmake/BuildHamlib.cmake deleted file mode 100644 index 4166f5ae..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/BuildHamlib.cmake +++ /dev/null @@ -1,20 +0,0 @@ -set(HAMLIB_TARBALL "hamlib-1.2.15.3") - -include(ExternalProject) -ExternalProject_Add(hamlib - URL http://downloads.sourceforge.net/hamlib/${HAMLIB_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(HAMLIB_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/portaudio.lib) -else(WIN32) - set(HAMLIB_LIBRARIES - ) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${HAMLIB_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS hamlib) diff --git a/freedv/branches/1.2/freedv-dev/cmake/BuildPortaudio.cmake b/freedv/branches/1.2/freedv-dev/cmake/BuildPortaudio.cmake deleted file mode 100644 index cc33d061..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/BuildPortaudio.cmake +++ /dev/null @@ -1,52 +0,0 @@ -set(PORTAUDIO_TARBALL "pa_stable_v19_20111121") - -# required linking libraries on linux. Not sure about windows. -find_library(ALSA_LIBRARIES asound) - -if(UNIX AND NOT ALSA_LIBRARIES) - message(ERROR "Could not find alsa library which is required for portaudio. -On Linux systems try installing: - alsa-lib-devel (RPM based systems) - libasound2-dev (DEB based systems)" - ) -endif() - -# Make sure that configure knows what system we're using when cross-compiling. -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --enable-cxx --without-jack --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) -else() - set(CONFIGURE_COMMAND ./configure --enable-cxx --without-jack --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) -endif() - -include(ExternalProject) -ExternalProject_Add(portaudio - URL http://www.portaudio.com/archives/${PORTAUDIO_TARBALL}.tgz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(PORTAUDIO_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudio.a - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudiocpp.a -) -else(WIN32) - find_library(RT rt) - find_library(ASOUND asound) - set(PORTAUDIO_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudio.a - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudiocpp.a - ${RT} - ${ASOUND} - ) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) - -# Add the portaudio library to the list of libraries that must be linked. -list(APPEND FREEDV_LINK_LIBS ${PORTAUDIO_LIBRARIES}) - -# Setup a dependency so that this gets built before linking to freedv. -list(APPEND FREEDV_STATIC_DEPS portaudio) diff --git a/freedv/branches/1.2/freedv-dev/cmake/BuildSamplerate.cmake b/freedv/branches/1.2/freedv-dev/cmake/BuildSamplerate.cmake deleted file mode 100644 index 8b6b7a36..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/BuildSamplerate.cmake +++ /dev/null @@ -1,27 +0,0 @@ -set(SAMPLERATE_TARBALL "libsamplerate-0.1.8") - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-sndfile --disable-fftw) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist) -endif() - -include(ExternalProject) -ExternalProject_Add(samplerate - URL http://www.mega-nerd.com/SRC/${SAMPLERATE_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(SAMPLERATE_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libsamplerate.a) -else(WIN32) - set(SAMPLERATE_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libsamplerate.a) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SAMPLERATE_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS samplerate) diff --git a/freedv/branches/1.2/freedv-dev/cmake/BuildSndfile.cmake b/freedv/branches/1.2/freedv-dev/cmake/BuildSndfile.cmake deleted file mode 100644 index c49b6388..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/BuildSndfile.cmake +++ /dev/null @@ -1,26 +0,0 @@ -set(SNDFILE_TARBALL "libsndfile-1.0.25") - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-external-libs --disable-shared --disable-sqlite) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-external-libs --disable-shared --disable-external-libs) -endif() - -include(ExternalProject) -ExternalProject_Add(sndfile - URL http://www.mega-nerd.com/libsndfile/files/${SNDFILE_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) V=1 - INSTALL_COMMAND $(MAKE) install -) -if(MINGW) - set(SNDFILE_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libsndfile.a) -else() - set(SNDFILE_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libsndfile.a) -endif() - -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SNDFILE_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS sndfile) diff --git a/freedv/branches/1.2/freedv-dev/cmake/BuildSpeex.cmake b/freedv/branches/1.2/freedv-dev/cmake/BuildSpeex.cmake deleted file mode 100644 index 8d287ead..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/BuildSpeex.cmake +++ /dev/null @@ -1,30 +0,0 @@ -set(SPEEXDSP_TARBALL "speexdsp-1.2rc3.tar.gz") - -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-examples) -else() - if(APPLE) - set(CONFIGURE_COMMAND ${CMAKE_BINARY_DIR}/../configure_speexdsp_osx.sh ${CMAKE_BINARY_DIR}/external/dist) - else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-examples) - endif() -endif() - -include(ExternalProject) -ExternalProject_Add(speex - URL http://downloads.xiph.org/releases/speex/${SPEEXDSP_TARBALL} - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) - -set(SPEEXDSP_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libspeexdsp.a) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SPEEXDSP_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS speex) -if(USE_STATIC_CODEC2) - add_dependencies(codec2 speex) -endif() diff --git a/freedv/branches/1.2/freedv-dev/cmake/BuildWxWidgets.cmake b/freedv/branches/1.2/freedv-dev/cmake/BuildWxWidgets.cmake deleted file mode 100644 index 901d8062..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/BuildWxWidgets.cmake +++ /dev/null @@ -1,43 +0,0 @@ -set(WXWIDGETS_TARBALL "wxWidgets-3.0.2") - -# If we're cross-compiling then we need to set the target host manually. -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) -endif() - -# If not cross-compiling then use the built-in makefile, otherwise use standard configure. -if(MINGW AND NOT CMAKE_CROSSCOMPILING) -# set(CONFIGURE_COMMAND "true") -# set(MAKE_COMMAND $(MAKE) -C build/msw -f makefile.gcc SHARED=0 UNICODE=1 BUILD=release PREFIX=${CMAKE_BINARY_DIR}/external/dist) - set(CONFIGURE_COMMAND ./configure --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -if(NOT MINGW) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --target=${HOST} --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -include(ExternalProject) -ExternalProject_Add(wxWidgets - URL http://downloads.sourceforge.net/wxwindows/${WXWIDGETS_TARBALL}.tar.bz2 - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND ${MAKE_COMMAND} - INSTALL_COMMAND $(MAKE) install -) - -ExternalProject_Get_Property(wxWidgets install_dir) -message(STATUS "wxWidgets install dir: ${install_dir}") -if(NOT WXCONFIG) - set(WXCONFIG "${install_dir}/bin/wx-config") -endif() -if(EXISTS ${WXCONFIG}) - set(BS_WX_DONE TRUE) -endif() diff --git a/freedv/branches/1.2/freedv-dev/cmake/FindPortaudio.cmake b/freedv/branches/1.2/freedv-dev/cmake/FindPortaudio.cmake deleted file mode 100644 index 158e20ee..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/FindPortaudio.cmake +++ /dev/null @@ -1,107 +0,0 @@ -# - Try to find Portaudio -# Once done this will define -# -# PORTAUDIO_FOUND - system has Portaudio -# PORTAUDIO_INCLUDE_DIRS - the Portaudio include directory -# PORTAUDIO_LIBRARIES - Link these to use Portaudio -# PORTAUDIO_DEFINITIONS - Compiler switches required for using Portaudio -# PORTAUDIO_VERSION - Portaudio version -# -# Copyright (c) 2006 Andreas Schneider -# -# Redistribution and use is allowed according to the terms of the New BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - - -if (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - # in cache already - set(PORTAUDIO_FOUND TRUE) -else (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - if (NOT WIN32) - include(FindPkgConfig) - pkg_check_modules(PORTAUDIO2 portaudio-2.0) - endif (NOT WIN32) - - if (PORTAUDIO2_FOUND) - set(PORTAUDIO_INCLUDE_DIRS - ${PORTAUDIO2_INCLUDE_DIRS} - ) - if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_LIBRARIES "${PORTAUDIO2_LIBRARY_DIRS}/lib${PORTAUDIO2_LIBRARIES}.dylib") - else (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_LIBRARIES - ${PORTAUDIO2_LIBRARIES} - ) - endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_VERSION - 19 - ) - set(PORTAUDIO_FOUND TRUE) - else (PORTAUDIO2_FOUND) - find_path(PORTAUDIO_INCLUDE_DIR - NAMES - portaudio.h - PATHS - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ) - - find_library(PORTAUDIO_LIBRARY - NAMES - portaudio - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - find_path(PORTAUDIO_LIBRARY_DIR - NAMES - portaudio - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - set(PORTAUDIO_INCLUDE_DIRS - ${PORTAUDIO_INCLUDE_DIR} - ) - set(PORTAUDIO_LIBRARIES - ${PORTAUDIO_LIBRARY} - ) - - set(PORTAUDIO_LIBRARY_DIRS - ${PORTAUDIO_LIBRARY_DIR} - ) - - set(PORTAUDIO_VERSION - 18 - ) - - if (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) - set(PORTAUDIO_FOUND TRUE) - endif (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) - - if (PORTAUDIO_FOUND) - if (NOT Portaudio_FIND_QUIETLY) - message(STATUS "Found Portaudio: ${PORTAUDIO_LIBRARIES}") - endif (NOT Portaudio_FIND_QUIETLY) - else (PORTAUDIO_FOUND) - if (Portaudio_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Portaudio") - endif (Portaudio_FIND_REQUIRED) - endif (PORTAUDIO_FOUND) - endif (PORTAUDIO2_FOUND) - - - # show the PORTAUDIO_INCLUDE_DIRS and PORTAUDIO_LIBRARIES variables only in the advanced view - mark_as_advanced(PORTAUDIO_INCLUDE_DIRS PORTAUDIO_LIBRARIES) - -endif (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - diff --git a/freedv/branches/1.2/freedv-dev/cmake/GetDependencies.cmake.in b/freedv/branches/1.2/freedv-dev/cmake/GetDependencies.cmake.in deleted file mode 100644 index 7470aa6d..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/GetDependencies.cmake.in +++ /dev/null @@ -1,37 +0,0 @@ -# As this script is run in a new cmake instance, it does not have access to -# the existing cache variables. Pass them in via the configure_file command. -set(CMAKE_BINARY_DIR @CMAKE_BINARY_DIR@) -set(CMAKE_SOURCE_DIR @CMAKE_SOURCE_DIR@) -set(UNIX @UNIX@) -set(WIN32 @WIN32@) -set(CMAKE_CROSSCOMPILING @CMAKE_CROSSCOMPILING@) -set(CMAKE_FIND_LIBRARY_SUFFIXES @CMAKE_FIND_LIBRARY_SUFFIXES@) -set(CMAKE_FIND_LIBRARY_PREFIXES @CMAKE_FIND_LIBRARY_PREFIXES@) -set(CMAKE_SYSTEM_LIBRARY_PATH @CMAKE_SYSTEM_LIBRARY_PATH@) -set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) - -set(FREEDV_EXE ${CMAKE_BINARY_DIR}/src/freedv.exe) - -include(GetPrerequisites) -get_prerequisites("${FREEDV_EXE}" _deps 1 0 "" "") -foreach(_runtime ${_deps}) - message("Looking for ${_runtime}") - find_library(RUNTIME_${_runtime} ${_runtime}) - message("${RUNTIME_${_runtime}}") - if(RUNTIME_${_runtime}) - message("Looking for dependencies of ${_runtime}") - get_prerequisites("${RUNTIME_${_runtime}}" _deps2 1 0 "" "") - foreach(_runtime2 ${_deps2}) - find_library(RUNTIME_${_runtime2} ${_runtime2}) - message("${RUNTIME_${_runtime2}}") - if(RUNTIME_${_runtime2}) - file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" - TYPE EXECUTABLE FILES "${RUNTIME_${_runtime2}}") - endif() - endforeach() - endif() - if(RUNTIME_${_runtime}) - file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" - TYPE EXECUTABLE FILES "${RUNTIME_${_runtime}}") - endif() -endforeach() diff --git a/freedv/branches/1.2/freedv-dev/cmake/MinGW.cmake b/freedv/branches/1.2/freedv-dev/cmake/MinGW.cmake deleted file mode 100644 index 333c1dc0..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/MinGW.cmake +++ /dev/null @@ -1,8 +0,0 @@ -# If we're cross-compiling then we need to set the target host manually. -if(MINGW AND CMAKE_CROSSCOMPILING) - if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(HOST x86_64-w64-mingw32) - else() - set(HOST i686-w64-mingw32) - endif() -endif() diff --git a/freedv/branches/1.2/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake b/freedv/branches/1.2/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake deleted file mode 100644 index 3507d720..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# Sample toolchain file for building for Windows from an Ubuntu Linux system. -# -# Typical usage: -# *) install cross compiler: `sudo apt-get install mingw-w64 g++-mingw-w64` -# *) cd build -# *) cmake -DCMAKE_TOOLCHAIN_FILE=~/Toolchain-Ubuntu-mingw32.cmake .. - -set(CMAKE_SYSTEM_NAME Windows) -set(TOOLCHAIN_PREFIX i686-w64-mingw32) - -# cross compilers to use for C and C++ -set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) -set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) -set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) - -# target environment on the build host system -# set 1st to dir with the cross compiler's C/C++ headers/libs -set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) - -# modify default behavior of FIND_XXX() commands to -# search for headers/libs in the target environment and -# search for programs in the build host environment -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/freedv/branches/1.2/freedv-dev/cmake/config.h.in b/freedv/branches/1.2/freedv-dev/cmake/config.h.in deleted file mode 100644 index 8e3ab76b..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/config.h.in +++ /dev/null @@ -1,19 +0,0 @@ -/*-------------------------------------------------------------------------- - ** This file is autogenerated from config.h.in - ** during the cmake configuration of your project. If you need to make changes - ** edit the original file NOT THIS FILE. - ** --------------------------------------------------------------------------*/ -#ifndef _CONFIGURATION_HEADER_GUARD_H_ -#define _CONFIGURATION_HEADER_GUARD_H_ - -#define SIZEOF_INT @SIZEOF_INT@ -#cmakedefine HAVE_LIMITS_H @HAVE_LIMITS_H@ -#cmakedefine HAVE_STDINT_H @HAVE_STDINT_H@ -#cmakedefine HAVE_STDDEF_H @HAVE_STDDEF_H@ -#cmakedefine HAVE_STDLIB_H @HAVE_STDLIB_H@ -#cmakedefine HAVE_STRING_H @HAVE_STRING_H@ -#cmakedefine HAVE_FLOOR @HAVE_FLOOR@ -#cmakedefine HAVE_MEMSET @HAVE_MEMSET@ -#cmakedefine HAVE_POW @HAVE_POW@ -#cmakedefine HAVE_SQRT @HAVE_SQRT@ -#endif diff --git a/freedv/branches/1.2/freedv-dev/cmake/soxconfig.h.in b/freedv/branches/1.2/freedv-dev/cmake/soxconfig.h.in deleted file mode 100644 index fb38608e..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/soxconfig.h.in +++ /dev/null @@ -1,16 +0,0 @@ -#define PACKAGE_VERSION "14.4.2" - -#cmakedefine HAVE_BYTESWAP_H 1 -#cmakedefine HAVE_FMEMOPEN 1 -#cmakedefine HAVE_FSEEKO 1 -#cmakedefine HAVE__FSEEKOI64 1 -#cmakedefine HAVE_LTDL_H 1 -#cmakedefine HAVE_MAGIC 1 -#cmakedefine HAVE_POPEN 1 -#cmakedefine HAVE_STDINT_H 1 -#cmakedefine HAVE_INTTYPES_H 1 -#cmakedefine HAVE_STRCASECMP 1 -#cmakedefine HAVE_STRINGS_H 1 -#cmakedefine HAVE_SYS_STAT_H 1 -#cmakedefine HAVE_SYS_TYPES_H 1 -#cmakedefine HAVE_VSNPRINTF 1 diff --git a/freedv/branches/1.2/freedv-dev/cmake/version.h.in b/freedv/branches/1.2/freedv-dev/cmake/version.h.in deleted file mode 100644 index 43b3b7a8..00000000 --- a/freedv/branches/1.2/freedv-dev/cmake/version.h.in +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef FREEDV_VER_DOT_H -#define FREEDV_VER_DOT_H 1 - -#define FREEDV_VERSION_MAJOR @FREEDV_VERSION_MAJOR@ -#define FREEDV_VERSION_MINOR @FREEDV_VERSION_MINOR@ -#define FREEDV_VERSION_PATCH @FREEDV_VERSION_PATCH@ -#define FREEDV_VERSION_SUFFIX "@FREEDV_VERSION_SUFFIX@" - -#define FREEDV_VERSION "@FREEDV_VERSION_STRING@" - -#endif //FREEDV_VER_DOT_H diff --git a/freedv/branches/1.2/freedv-dev/contrib/CMakeLists.txt b/freedv/branches/1.2/freedv-dev/contrib/CMakeLists.txt deleted file mode 100644 index 3f4b7e02..00000000 --- a/freedv/branches/1.2/freedv-dev/contrib/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Install icons if we're on most *nix systems. -if(UNIX AND NOT APPLE) - set(ICON_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor - CACHE PATH "Prefix to use for installing icons.") - install(FILES freedv48x48.png - DESTINATION ${ICON_INSTALL_PREFIX}/48x48/apps - RENAME freedv.png) - install(FILES freedv64x64.png - DESTINATION ${ICON_INSTALL_PREFIX}/64x64/apps - RENAME freedv.png) - install(FILES freedv128x128.png - DESTINATION ${ICON_INSTALL_PREFIX}/128x128/apps - RENAME freedv.png) - install(FILES freedv256x256.png - DESTINATION ${ICON_INSTALL_PREFIX}/256x256/apps - RENAME freedv.png) - - set(DESKTOP_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/applications - CACHE PATH "Location to install desktop files.") - install(FILES freedv.desktop - DESTINATION ${DESKTOP_INSTALL_DIR}) -endif(UNIX AND NOT APPLE) diff --git a/freedv/branches/1.2/freedv-dev/contrib/LICENSE b/freedv/branches/1.2/freedv-dev/contrib/LICENSE deleted file mode 100644 index dc8853a7..00000000 --- a/freedv/branches/1.2/freedv-dev/contrib/LICENSE +++ /dev/null @@ -1,393 +0,0 @@ -Attribution 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - d. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - g. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - i. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's - License You apply must not prevent recipients of the Adapted - Material from complying with this Public License. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public licenses. -Notwithstanding, Creative Commons may elect to apply one of its public -licenses to material it publishes and in those instances will be -considered the "Licensor." Except for the limited purpose of indicating -that material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the public -licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/freedv/branches/1.2/freedv-dev/contrib/freedv.desktop b/freedv/branches/1.2/freedv-dev/contrib/freedv.desktop deleted file mode 100644 index 96e82931..00000000 --- a/freedv/branches/1.2/freedv-dev/contrib/freedv.desktop +++ /dev/null @@ -1,8 +0,0 @@ -[Desktop Entry] -Version=1.0 -Name=FreeDV -Exec=freedv -Icon=freedv -Type=Application -Terminal=false -Categories=GTK;GNOME;AudioVideo;Audio;HamRadio; diff --git a/freedv/branches/1.2/freedv-dev/contrib/freedv.ico b/freedv/branches/1.2/freedv-dev/contrib/freedv.ico deleted file mode 100644 index e6b9a2087ddae6e530734e7293da08b1d12d4dd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364646 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 B>KOn4 diff --git a/freedv/branches/1.2/freedv-dev/contrib/freedv64x64.png b/freedv/branches/1.2/freedv-dev/contrib/freedv64x64.png deleted file mode 100644 index eb89773bbcd93a4ed507271bd728734d0a5d7f4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6289 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

dh=(PosX@Z;=^IUxT@l?9i6IK zd)r=%i{!V7J>NriYUKD=O>RppZsQK9G8?j~kqK)HX($~iISLCMjLd(>u-;p9Cyy!P zgu^XtuL-G&M%zR>n}_ZcE}FeeF-_v_EaJVj}J>Ygq>#&|o z=iy_|4Spf9wWJV{G$5xlah`f z7^3W7|LX?WK&*T1+?t8TJFb z_hfv?!a?yh7Zq|(wSsW-8>T12H+QBN3Qybz3fptct7XOB3Qy2H{3ttbcf5u*Tf$em zv`cPR$(^@!#dK;u$X%1>wq3m`rh{W4SsB{4!LI5O&hId-E#j{FCXW2OfHG&OPO5M+ zm>1v|W#MOyeE)O6nHAu@4)*Wf#EShq!IJ8pqHV@nizC1af4^?7kQ6>D=pGlFAU`^% zeA-}fr9t$LP*lrP!0r^DZ~w!l`ngNyy(Qe1(wOaI-WU|x^7X8M=K)QumUha`;;F#X z+~vbQt?hP%;eHJ%1+GY+ax&kOG*Ol}f9UViZi?I(Enx&fixj~l`|UZ#Vc)nYa;A+Y z!wCTT#DruwX#%<8zD??4F0lf>{G5L|S{ory}EhK>FF_k{7XEi)`@B5#pe54?A2J`*=zd$IY zMiNdMijQ_hJ@>1*;s4>MLPB0E36FDP@E zL}Rnp`BEXuFOVdR20PD8gCD;1?|l5q+fQq5JbdDeXgad}&&ZqYYA zD}mz?l${lQn@ClkYn<{8DixTq8ZPac55OCs`24zXNk_sLLCE8YIGtAy!>ueUDYZxU zq63%VFLUpe^Ia*BQ#xIl0UtFcPZp-u-^DVU}ktk z(FQb#I?AwKXke<+I_UL;f!)Rx2i9VXfybm`yX9t2sV>CwX=T*R9Pzw6bSz8d0DdsK zqYBCzaoM^TXe|#Z*J(TUk(teptrXp(;k{tMAi(>>F; zjiA*qOhdJsK>;pTi=^b9bGI#j8CY#kBLDT4L%Q=Bs_l~ZUt4bAj=c$j+2~Ul93=;~1y{fOQ8pV^4Pf2Q{HV7q^N&7rl0M%sq9E=tki$ z25ZKTE!=9Uwy)wOOyaPS-|^GKLw)V}sFPKnad>XCl{zW;`4dYlds$T(O3k9Z+7g3`M z_^e|;HN?gpcy!JS2L4(#B}{%$YZxETXr_#&r{ir}V?G+B#>H0pX6CZD(=o@FbGLy)W9bG8S*1 z`q4L(33Bz$^6Z}0;}C&LsP3B0v@+Pqq7LI2cqj@F0YzReM%IstXSP5!J}i!ve6btmy2EWga8dAr)%JIxRGYZC*-86dwJk+qO{X&`Wk2_~`1;&nz_4qJZ~*A9$pYSDWtVfO6HH z1ts6MBZjA61d>UfY#FQje}YG&uGL%g5LZ^T?Kn@|wNhI5j2_Zz@sZi0k6XZe`FL;d zxuT;&N$Hz0HMy*Fi>hK9Zf*pJ{<+d#f|hv96<&DiV9DRW!eOau20zcMUBOVnP<^E^ zDH;fuUa3?|es4)j;5Hym@N7*71OmfB_Yx_o7fVOE?V z&9w5W3n!(TGkmff|6?|^#QG;D9?I~=?UfO^b20^@76`VZzMxnMiE!pd@~;_23Uk3i2Nu`#OYT1}{tKkV_ySb*j!joY z>Zb06XTJ2kjXOm(Fjo z+X6FG0;!d`Ef`Nau;pgVwBeaic$I>cJ}ZXTu6E40oBia>qmgotSLgA1a!HsQ>gu(9 zpH^?khC7B$bb4KMR8$A#S|68-p}6i*lL~tGHWVbEHaZJqy`!jnhi)-lVKt9xDZe}P zlgSSEh;e`M{wcf$=7kCEN&L0v{PzUEFuhVsEG8|t+aJD$nY1G_v(-OoM{xvY`XGvq z*=*`x8NsGsY7+4j$2g_qA*DI8cTv@X@`ZC>uRa79ob2N8pU%|UX~AmWN3UHVQnM5( za|AI4MJQzK#iMK9zs}@r;c}p|0B-&XZtmuM$TeYoZrh^E)MEBX4@x?$tjBxLzCB?E5B{CFN0|>+n(o*jE-B)d42!7emDw z??uNqcMR~#KT%pK^*&}|wT=k`;WtW}u3Js`H@hMCZf9pi9%Us}dwB;ukh}%Z z{jyrWHu*aKoHfd=*L`e-jRNDR!Fp!F0s3dn(K`%ow6XSL;`J*#XKYT#XS=<*M@k0I zb9IyF1AH!(51u>v>CSrz>4H3Y&V8sgBO4Rfiab<~q#&EDg;}7o_pchdy<8?fUD-b$ zfI#$yny=Y6@*p%T(^C{m`?8VBY^Mv%ca(-rGI!nP1${ozgg8)K%D`(1IJ22;M{d!o%) zopHF8?GL&Ue(XZ%I66fFRC?6E!L$!&`PI^q)pLzEjG5j?iUQO3wAT1B4b(C9;3rKL z2F3>%RL|4_b=Bp8lNx*Mkz2$u@Hqpa>0{zAcp2!h0L1^y`jwPs8lbS zIGV1+qs$9ZKL}dCbXyz{dLGkqa5qgC`2e1JtCK0hguXM3HPlJi;V=I zKb;eQ+TS0eT+W8}Z)o`y(O;}xtUCLe%abkAhj~8AMT|@dZqCPI_FwiYhHP|J(&CC- z>n5E{PnVx`?7t5}2~!CGI&A*XxK#+(2EQP>8aR0wsAD?D9&Kg+MQu^*`r}#`=d@wL zStN?yi;b1qlsw{*wdoY@t|Qd*gQwurFneab-l^|`oKVP;Wb@;`Ah)#Bf1U?&g#YV# zaA?yWMFC62rn0wN-;!(bo$fp@R7=_pT+l{HS3>L5d7bi&wcg?laVLE;4DZ@{W7>vV zTshm|^(O7X8@NE~dua4g>#+Kv{$vmtpg;nRBA;@@{z1(b3;;xs7wd7+dwHnT|2MOJ z;{L@+701-rSj!2ur{E2?bY=tvUW2sLg1fFK_loY?7ST)I&>% zmHN(@`oHOSvI!=$_{^6H?O&_ptgEQXewDT?QNW}f5*qneF#WUIlvo}_M?9C4es42n zi*W$u5(rnF;5?i#9ZVpJ2WFw)4MHuOz=Lf-a`ft<*9R0U-BQnOa6JLCN?wIMMjG-}l0Cz?>i>+dsPZ9UyX! z;+l%p#?JCi#qO8mA!=Qn&kw{|Uvll&2bM!kc2*9W*apVzPM##oPp!+92dcwpW${KE z9$?EHH7hgoz_3hwY7U@M$%|# zX{pfOuG%*OD?~0NocT2=s@eJWFCMi2-0jwA@R2N~}6(+d&;}_|!oYc=QeC2}oc!tj` ze)!DHTfe!V1_jT}7Y7sm_^r&_)#i|*HautGx!$%e#aJ6ylOOLGVHb! z3&GOei(>W|P3Auu17_(z8l%C&{EDG^ z-U@x1Nl9rO0R`QRmGL&O!FsXul%QiNE2!>QF(P+qYiDDIhPiNRQ>k9rEJok2+9(GL z!eHBo=^269POp11vKPJp7vKw6>r?mXzCJaicSs`HI1>Szv%+c!8)(TQ5hOqNC4Z2c zG&;|Z%+1dmuXf3jFcs5K5k3PyKZuCK3{6O+_Y_7smxyrkfAt(fEq3`tXp1&m$qfU$ z6L(zX(U}UvTrXg$d4e9!rJ`KV9mF1$8$r-?Jyc+rF1Q(W-gac8ybCl!{p}0@w1>)C znh&`zGK5qEn|xdbe_gfV;@2uZXLCpQ@)469(XZIR2|00ZiL{=@j_X73BN$4~Ik4Wq zjmL+pL#m296!8s9$U46qr2Bjqwb`%V8p^CbIICNl*UOe#vDB@qE>BWPEJ@_Wkkc2T zF9jNHkoj7%ItiKELw&sX+h+2zYvpq{i$nTKU3Ll(q8e*PRc2#=wkA zeMp|0<0+z}2g2`1E2|GfRTXlm;=&IiW{!%mo4p1pR3?`SGOZ||tKuqCr~tRC)F4&p z`!Lz1M;jR6DXT4XRZ?3j<8V*!5N}6t8%(9;De$U31VFX?F)eJp`U6}ZGpBx zh+-~Nocctda-de(lihWaNioO3Fxh#gve6?Raj;`&o5+}|hI3eQW61?4C!72BM(2@e&Mt}mBKR05%EnwU_X+9uHG%PLxJtGDiw zq}<|-Hw1b>vzR(X>UT5p0l z=zxkpkao$Pa?Sx(z%_pJ9pDuR($~WsDV8uzrDCl-mDl902#-uN4>_JnFoIJvu{s;w z4(kX_dS4EIm|2Ezt9%4ws)gmOYMC>1-C`g~052`g!+?nqCnX=h zZT+J@HI%Gu7$DUEO{Lf8o)$x$ED^0t<676xcPT5qOlt&DAGCBu@0)DWfGB{L^`pHC zlJrUAvz&~1syj>!OWt2p8j~K+6Ti24M2pS2EaVvFcP9>rt5V4~F8Lnz=bEPBJFedlOd-hQ~ zZEb}gTBCm0+U3V=DL>GV`qC==Z;&N+t^E}JDj60YmKy(}yaTsP^KChreSGz&Q?<^Y zjyzN-l!;;J`5eOoL9mIs%I?|qS9am)Q#S*k-vlz>U%n>Of6?Y6NrEZn$b$gM2xvunAv0n=0Y)q-1?e6Zq(PSi z&ZaWs)OZLTIlzD31awnb3i>wJyw3*uvmt2=HJD%vhAxjD<%g~v&&z51LFGF(12M&Q zq-yvfE1nI5$^_<(gE67CA!ebSMab-@{lQ=0WJA0kc<;`veCFT>vk+O2mm6vug%I~6 zJ*)T2O?|~T<)F*(4;oFgt`2QlVfDwdy<&YO*by{d+-_1d6C5v%#r_WB;^W0Ox?H8Y zigx2DfX^W`Y^6EEzwao&s0aff&1R1UQ{<$G)*9d8>(xxRv9Cu_v#P@5wKiVu{ia7K ziCNe6*7fq$PaOIc^ARCM!88*@fTQkzMIwT9eOw?lH8~zh42+V=$cFu%Q;IT!g{qi8 z?C$RU^V(bz@t_@q157)~1D5`WE07&!l$dNVGrvwMs4Fv#fwS*ik|3ZbzQ{JYQlCZJ`{S( zS(oglPMna`IQj4~N?{NxJS>aeTYvrphpi0avy~$0&Fkn_y6CziZb3L;&Hlvq_@Ua8 z>}!L%tZk|SK`JAuZewf~qoyT{^(1t0Yf0c9NYqf_@@X$o^Zg!o8%u(NZsB7ySf1Jb zI;Jvs;qz4Umy;)#|E>Ty`+E~ayh>Z&=~znais-A?AALo3wXx7W)E70do13pz$_zT{ z%36QTE!SEz(C>#eHDi}(9=<~q|3TLpAhK^X=x|J|%mzDqXGx1nS7(|zj5r3O)4{-AGs%xrEv3X7XJ6~VTiJFHZ?4c6`wyataw zUJJKVc70y(tn0|XQxfAN8hP=Jq4%tpi=SKnrGUvMG_2|nDryz{r@c2gmsTe9o2ia( z&_4LQJ4hT}eFU{hx^?O)9A$)*-X!CInN3duUF^>?)%6%#nR#NyMm;jGcV^ir?2uJ* z+01u8$+%PMyT3D(+sveIv$~qoh7Ew*r`mP|9=7R_n{$XgHjSfmS>Hyi#iVMfk7l^^ z{uR){y2zmPCo%4>{_^&3tTkG+@D~^93 zo}?MabQSu0ajG!a=G2%uE<)g3>O5@D0O)21HSlAD786MmVCaa6l5_aqtZ+z;s?{fG zHQi~GjDCB(G?`a?*m8*UyK+X3_T!;Tt{rbbdkUYQEi0ZFuCO>CB_RDmQ-hO5Xx2WX zR@l$L^j|@PU4vSBUalZwEKruF7F6c!aw-Ial}b&4|Xrc_s*bPivfoE$=@Uzt35Fk|LhB z1&J5G%8`~p-)rC~_!l+_r=v0pzy1|ZavABMtQz=6`7T4tzkNje_6dG2m9%|kL2Wu? zgXeXr=%6oV!(qst7XOxheq=*T4uT|`HwZ9*KcY~jZOr+IFs&@qG87+=xh>DO zTnk?8&WColn|o_%ArGkMHXtcTocEcy@{fa>gRg~LW6WAnv-=Y(Nj-3`4&$mCfiAvo zKB&3zb?n^{_o~;)_FN^h8uKHC!ooCo5RWlNXN1(Q+T#)Qkpfj}z(=U56nWwU9+YE+ zjRxC^HmyShunh|?87>k>BSyu%oQNS((217MS|7KXMaXb!p$c$Wf>GM%OQ7ul0Gre= zm!6a-I$6wqR;q-i#_Bw{j7^Rgv$v<)OTnWk$Xz{komRKfE8c-}w0tD+72Cc-^wE7z zhzxooNw<>f#@m5bJIy)ub$0c&Xywr_5j0(SM&KK-AE7P#J0Qj3wZJ1ZJ0Rz4uSH+) z@1IOheOAS{)>S>Qur1q;iqxTZx3qFX_G{v^>^D2}E>+fj{Dyxay7_M2v=EG)A&hRp z`C2X4e=?nEBGXW5KzJPdGQB{)UqG~AbP^(_Ph>V+dvI$Iv<|z!FkJnyc5x&eQub!~ z_xXLYunP)+0V%K$iY6CC(vYB|RGL^jI%wa*&SB9brTC)HRcT? zP5N4a5pb!c;+?B1W1jt3s3jJaxF}Hmc=2cC&}L_$W za8;cI`m?U3t1*z)iHCo$u}M6s#!Ctg^>aIDUx1&vFJxT$@@>2L=2rQz z8RkSjmwVyq#0C%9^-CA7wkn!b|IEhm|IA%2T>W_c_*E5(in8FO>%zuPJXWh0SBt8} z0I?S!fLMB0ojq(Dmw@E5WX;V~kC?RhPG{K|&Q&~%`p&Yq0r*kOj&tCB#}-*SUMWPl zesKg9EMMS~)pjRKGT~>Y@J}ZGPpr+{2spVl9~d0yfe);}9h^;2`dSCLB%*;|PR16$ z%SzfcWNC0II0gNS2E#_Mf8<>)XZfQp`Nxh*H{e5RguXu3{CKa~)fxTNAzYxam4Pa3 zy0UPRU9B*pG5APSg;-@PKI3>|g#AOB(fGiVr)~S%C1AWvQyZGcBwFwW3=%dvR;=Dz z1PSYm_4;R7v1{wkvC=_pWU=1Kja#L;_lo=(Q`Pm9AVK@tU?UyuzVhidNxh@_FHhTr9q=aaCWp2anI6-mEY|g8ukpYF}S!qb}=t+}J5m4fpaZ z&KqeG-d}ABqfN0=(!RCR-s!_5hXtXzhWypj@FL~he#_T$apH(LCr|t~%F5cMA=ZJJ zt?um3W3BwF83mut7U_AfqQ!Fq6Hixi4^kf1^@xO;nc<;Ad(!Q}Rs`dD38aa0;mXG? zab0K2JO{!XneYG~rPIZe!}Fm7xM^zpk`jHe;n;5ayrC$5sfL&UcN0Kw2!|if~G7ibC^=v7L4!(_pH?Rh2fGD$=%jzp)-aU`a_Y44;CMtN zev8IoW<=dQwf1j%$q2PvBSUuiFxK8EVPasM-s6wXCH+Va!Bj`5z5KD{Mj*2&OU zBm+~YW?G|!K6L7J;cs}ov(G2%1v3zlnndQ!94e@lDX}H^5eVe1I->DcqvH8gx0cH* z_aK6~r1;(5roa}*k@k{dlQVzcmaEi@A2h@ zBpMdDTA&Dow2#&b7-gF;r(xhIUOlFCLwD2zRnFwDBD}$EHp2FgNAuZYTIOo^gIr53 z=wvO2+iuoS+wJ5mX6_bTRW>#rF>no)g2ej`=~lQegSiq16paLpVwK!cJqsSg6oqU| zD4EGW0XN(&yHrAgQHfXdJT|hX6^`nRn|r~$~eL_H-+kx1L6&7;C!tIS`!SSC#q9nAD*S-sVq^35^=e9-p> z79(Smn1qyf3d7J`M&t){4K%u{z3m-cbxhE^quHhq2JGR7MB;6?-8e#n%R{^p#odl% zrqI>VUy63HvL-&G5f^J%6e=e}*Rt^2NChs5a2C~R%hAXaD=AHUU@8yW)9<$z`^w)p z0^)ZUMm5fLrfxlrnj@Dnb*-!{vxWd3G3!Gj$c~;{;V}@3HX@lNR5pl0RPWuh z_o_rbRn{QmP9&)ZCcDm^iEw8Rs;S+P7j?{i53=0NgBnqjJTfJVe3AghwUsF7x16LRV{xs!ODs(O0G=P4`>KB)5? z(|j+>*t)&FV0GYn)DJ>kTz{MfTRlReLuwSAoJ#NUHv-Zl>!xSEu)B;}N*q>M6rbY) zSb^QtGkW#_s6gozXmEjRh$wCt^q@BEE6ukMK7UrG-4lLmk%67h^7V9Nkyx4i!uD!E zA;PJ%5AVQmB;DrLZUv#p|HqH%aOj*M<@mi0JA#PIO7pAP;bc%)>W|>5GD;*u){qto zjT%s^+0t_nR6O>{C)=Ay0dLm_<|z#!?@cNl=dA9L9WJ@2`GskQ;(=UEYqHlfC(Cfc ztIRx+TIHm9t`xk?-h(Ygq)5`|)rZA4>VyQdn<`c#82Qg%WJmSYzo7%f3t&EviZua3 zr|3rmvMz~;^&MW}%5Q4|XXnkand7$aq6P;MX3Df9`;LBreLs}SR=Dw1A1DE?&{9mz z?5|K!* z(gCU6u7IG*K5&tM>+;5phR~f&_YWSmUp&;{6!s-tE5mhu+>W9{xY*WCxB8Q+h?$W& zeC1fXY$>C}ihfW*C?w1ggYaT{*fbQ@qCWVBZg;is~* zTMC9Y=lKdXXBct(rPjY=vu4DY@N>3xn9`1faF?9dsKK+5DN44!2N}=D5%A|lZu=@c zUe^!dv^u{xkOI|uMDhcRo4Mx*c7-#8J?B0uAg0;2v~R;P z_oHZ1dRGDAycc!krzi@Uc^o1{6H7u>?eC$Ecb*QIs}^X6HbV}yod@m_0rUV)P)Emxn8bg5+}0y@wDwSV zyQg%EldNHFw4aSh3#=Obw=gPh(SZ8*5Wu3-{aGDSiqyhSG*Gt1?4LEcK?&aynJ6jXte2Md-evP$lVTUD@VzF=1z-BB2NYI|$rJh-fL1 z=V(1TPDM=gTmhFIQFidwEg?e$6G}#7jQ+k55e3h=3X9=SpidP@ebp!Gz~HRh6yF`g zY(Mmy1TFNswNcqiZmTuk9yaT2bL2-1#9q`M5u0HP(^H%BPU>m39fDD8essZ!Rp z@Tz&ENLdLEx7MMIMKTR6>UF^2O|oM@)$OLb3c~5U>@TII1b$Xjyia|26P_^$tjb_I zA-yR8n3NEsRm`qxJiBtFI`M|hg(t=c8G9fC#7;pj4CpHOcakI?apzD;OP|erY8IzlA4YIb2)Y}>6da&U(W>C4c7uWc~^^qZLS(qBx{UwWVh@e0f89O!K;H8 zYdem218JSO3@?o;mShm@UVBf;aw3-t-hQyn$ED(tQC=7vL}`5zH;5oqp=DhiNETOU zT6d{a_vjnB{$(-`^OApy)}h85e7Dfjt2s#4{+}?!B*St>j^evN&Ygm8JV$|MX$t;o zz;T~~Hb;2*%s4gVr$zr~z~f~T1-dwrzctfA;}QnZN=q*aqp)UQx1b61H6=OErS#_d zidJq@2TM~sjx2ABT%q3vY`AQnT#Kx!SxNll?9Rr3T#_GwB73@M>m@srFPh(qnUUa$ zrq~ajM-}g#1r4fSMWC-Yw1*|}_tose8%XC*&*jhB`^;-h6^<@*v?X6h0os5>KhJM# zk28fi;zJt-bbjUL8Fdb4-SI_r>?wZuR6O--8!ZYxnZ{%u>ZHIMc}J9);JNViUS8dI zVhi~Nh8fbyiaZ#MgzGc+FL`#mdutYV1QWfFv+%5110%kW>K!S%inN2tq9+iu(s2<2 z6nKgCdab@!upsfeYjWL2N1pQ2h5++O3aRzXHF#(cUkL8P-7pBpr5yZ+$2fdc5AM;NZQN5OaruV zA|Iwu?scG(8eFXXD5eQjnWJ5a0aU|NO%Bk*x?yNIncUW+1$8-*7joS4 zO{;Ql^0HL_9TQ;KiM^!<4nKJy!@46ZpelW%j9bQiBUw1*^IuNOeY4^}Vtu#wl9`eX zK1hr|7%S%~^8~8({7AZ*66Kz*Cr4!eyo-7(Hr#%`bXLnHz0l~Y0CA}LTF))snq2+f zq0!rh%eN`6;~$UZc@Oq*NbQjj^J|YtP80S__H~NgR0q6S@euXh_k9BM<-2%~ndb3Jb@7;Z5K^#d z7HmI+?K+`{gv@7uw-fajyYOz8<@Y%$-3K|_jmVp`3h(PH)vUlB`Z*uY7oGu$HJ>RE1?w7sj3e8aRrHoX*QoNJ7SYsP{bK_q~M6_a!^#S;d1Od zGtEdk=36O%nxHFt{RLglIzR#hTCpeNk)ue|Lnl0W9 z(nALMladh~QFva2YuoCZYQrQA4~^lx&c}`l65cEa{+>*hLgW$%7LH=kl*U=dhpdvG z{i%}u`ieFbxw`WG1?x31esbm+-b7zqqGN*4uHy8+x4V;zxaGT0;-nC1AT|a#p|UC# zt9Y-Ao1+BdoD9x>LTsO3b3DQtKjw!Xz4N9UC zeiaLDb$LPe&cIT${a%b-wDZjBOr1Q3(mBqPdZR^DYIb^NzvT%g4kH`)<-?Yr55_I- zE^TVd#$nlvA9c#_m4i2XrUg@4RbVv3?z?T)%95dsSjL~)Uj9Kj_oH??$GKqsmoA28 z_aQU3xXj-(JBYjt;ldS7IyN-(O^0^|8%{{&^XUsOKM{!TPf)T!zcdh0egy`Ac^7rCQO?koPhKl&`OsNbo!ja~zDrjhA>0 zwQP93;+l?Fyf3rJA#?lz?C7H$OQE2bMiCRC%LJ4!pdC0S$mPNBb7H|OfmSQ};po7L zA@yG;MwiJbqN5pE|K)orxxLi<_w4*Di>H#}@K;vu=-^a0VN;UUbVAVbq?UDyGvtvH zs~lQRlAc$>OV>!^TEs7jN0)I2t89aZGds%fjyJBk8KQG~0O+Dl`z;ddrJXL0kbDw{ zzIivlXwCRzlu)H5Fm7Py8VX+;9!~U+%3jc;6XEE2{ELuvqznDgsfgc8LeYd$PaaJ# z-f;-$$nI)Dj(XW8X|=t>pv~u@gF?epy1H=DAn|}q)Zeg~k@JiEOP4(u4}sV*G`aZi z+_}^i29(ptmBxWaQ}&Pvd@VGw7%#rHaAB?%uO;UrdzqU@xzn9pybY8DLqc%2jueww-j|c=Rh%W!sKSVRSamig z>s^+GQMA}fgEFk#Qgm{>Tbgy)K?sGrB7L=56i~q_4b%6!G$1mp>cCP1VX)$P^ z7zzkOLEfwEUf3qwCK|MJ8#3190r&$8tHq|}A|;}UiqzZvn{pi1Z`FuDpNLm&B!x!+ zoN}*vm^pB>nm7@dZEjNSO#M>(Cg7K}JyAc_j#x~`uv2k08gLb_m z@)VxuIw@4~2wYH^&yt0U_k~9uK{w;bvtI`HaRqC-_7Zijnf@rr0B4cFvJeosF6ywQ zwi8*6N{J3D)K)en>`QK>o<|jaRK1F!W2LQ;r2Wo-YGW)XbN0kl(UEi&*7NIVEIK)U zUSAKG&)Cq!b3;vwyIekOxwxKgRl5OS^Cj0)|DRVlcHvs{U{h~Ebj&1kbBgp)FQZ_T ztWq_gm`QSw{L<}k95?c4A~~2^hiK`NJk8YWf>v9UL4~aw7-)v_4Kev|KP^*N)%Y<- zb!gFSJ>YD1W`EyL)_IqJ9Kvnz9t}rSaed5KCaS~x@%xBC5?K=POwL7!*?cFXhSM321*wNxez36SzN5>q)I3x$xf(P?=UyE#K0ZQ?-*SmyH!s$8uB zxIFvw>!TS5RYZg~LGvZO5~tFS%P6Rw<*Xiy=Yc>`5x)`1UVp7%T6Y&-)-AfL8vTPy zUjgu0Hq?Se@742#-xX>e?Ff%@GY}W z@2{BSUM7a-zw^m|h2Bo6A+0dQiC^yqQtQd%XXATCIgzPmdt&{NVm}nl#Muk(i~WZY za@p5(R%=r$%r2*~?lrj&-qlC0)xZWnddR)&jkfxKVKeQ6?5XO7q_lzUtH77%h{bBx zt({p)FDLBaor=Af=xA(Abc*G+DVu9|lIR;mcw>j=2S)fyB}bUH6RydhzxDniE;1R# zPM(;TYEA5(iJ8vcrMwjWsV~Wq)<8!tC{mI8qLNI9nEXS=z(PcG+7SKaOjZf;{YkeA zKK0wE?4P_*X5-(apv=_Y9?1Y0dU_*?JdF8TUVaSw51Z}Q^JUnH{!e8EgDBTiG$4bE z`8RC&^M@9-SC0LvCV9*&Arg4p+H*qUZ zliR`lG_NBpwaXY53dcw5+I(sBltoX?=Vy?=F}*yNKj^xx%bOJE`FAKC&%-nZge@gQ zqQe+&HGXdrS!@`-&%KiPWAs~;|*0PQT8Bsy~Mt)?B1~%=G)L(R}Pa=Xzjfb>_uiJ?sADDkfRJ;QRw(%W~#i z6W={W>F8|PI_xN0=u3{^x zkP0nj&ese5A_j-cfuy|*VBOD3a+kAffKU`TGp}2|WC{+f3ZJZRNZLGex&9r6$N6gk zp(U}hbI$x`*i$5ivmR8NQEYg4{!%Vf^3{$u@?eeAh|YtGoO&qo5&PrrW}DWX;-j^6 zbfI$PjPEmRQ_j;f=ff0kOSO36Y1W@Ozv_8+XV}NRHFuBt!2qb`-&#~*nadEG8#%mn z=XBwK$&c8_CpfDGFG1Eg1>3rMVgee`CH2lnKw`x7frO(6*>4d7fkV&Yr6%@->mvar z6q!jB)qJm|D>KPuqn&|oN@-8MjI3|zDn<4v@?F@ALR8IPzEqYcsKm;4Z@h?RfMlQ0 zyyQHhEMF^P9%ZI;W}CVs(z0!O!{3|J+%MYvhND45sBLKkIdKwC4|4w2jheIR*Fq# zqv>4Ni}zI0xDr&{m=}8<^mdqn1zb1Zbbij0W8`c^#worKaLp~TQ+-VTfC)tqD)DAC z`sjlIVh?k`O<#cEoA8>!?hL0M8=&I$$0|@EA{y-DB0^+gj$EY*phCI}c|L0sgO|RF zSTyL=k`;od`a`B+Vp_*~RFmZSI2B|Nk-{bunB9j)<<;@ffavWz2bLP(K0-Z{9v$Mv z3wL2}jZDQMxfz$mk_l=baLn`P?`B5&)B4OHqo83Ot^gG)p-9`wBB!%Bis4r}eVL+a z<#uzX4M@aGZAVbxCC9w4lWRRg+3cs=l%j?(u-jf>_OX6orADS8w0C z5IhUnlq~VVFE3t86EF2dGv+$BD-o;@Tz@}qEQdGxRsG|qkHJX@pj`^#fRNV+|+ZEh4b(J zS<3f{OhO-p7j)XDT-GUJk9lBo{<;|e7=BQ8#shp7Y&*)tZaY8-8O_4%Yo~TAT z;j*Dr=SB2me$FNE5gnR7B+haGwASZH2t|TIi$(mx=+YMEyq~r3Jq`J_0|i}5}1|$ z<9c$<}v{s?#$mP^BQ?sjZ>m~+}0iC#0WP=P7h zz{TJL+WN#(7lHD5!AL>ftEiPZ@or`Rp2s7uNB)#TKzc+lsZ{Q_x;g7wv{>Q367iKU))Q zcuKrlV=Tq6^kuv_<_2}wBB&TLPeV*nbr-b zt2mHTB=U@`_3^!SM(30`xbLnV9melO>{~<3axr_Ntpn#$7Iy~vvojAlj@km$6zh#8 z?BeIc`x9%^)~6n0q~EU)bjYI%o+mG!y*9<%^?D$9z4)e%^_rO>{LlIa7X6?4=f*Rf zXH+;J52T);w&yPkC=pGn;!3VqtIVzS)*7Gr_!Yds3=@qg_Y(BeCOd+=Gnb$J zx~vhFgsJPCjZN({Fh83~Zzf}orr_gv$p7^T+ygwm}q3?1<=u7D1BVh4`05ivZid4eiFRo?+1rA%HUdv67 zllss`#+Ox^cMWvEm9l!ZYThe5*!S*LP=(=tgCWg9TL$xaO)S`*(i$!fV)-!Xe00ND zyQrVd0;kGHeMx(XFlAGa{oq>39Hx1zxCh^5o%ZPXxL+|UyXPX$fE&F>A5h@7+(XZC zx5#vSS|#Cdx0*fw%cxa`(EeGBki#2H+7=|5nl;J$HmZ7ip=y9g=XdiB9 zU2#>+fCVZKZ~o}5aR7;lfu-MB5h>Y-%j+h0h>z&Qi826$-6A$O zJ1z4yE|!%GfY?)fnpRkj8>OTdS1kFShSV5c@$#=Wruc;L-s>@Dlx5J+B zUVKu$p-Gs%dC>HG|@=eTPA!L zH-5gQ-Wt&twGKDG$Yv0v?LXq{gHOn+8VSvswox|Qs2Z(2Gu0|U+dgpV>gr+Q6|H_A zr|Kmxz%lZ-j80ibkXpHaj6RufYb(InAh+!<>SCE3U(4RwV%Iqp33Laiwrq1E+p+eL zp;+_Yu}kgOk)Z@IxvJnyl)}dpzc@YY;|ERUxMGH0+F_54-$SETCVPpQc{Js0&w@+v zxc&EnhTg+xzhl}tu0~u;PpR>)lKR#sCdvi#P%2Ka1>a!dsTf3 z9p9iP1<8*54;V>XmO507GHBjNf2e;~m|%gSVPD!s;@L}Bj7qCMAOpzI=q#Rsbf*SC zFP`gDESBuAxG!ku0o4$uRqCQA5eAdWiA{Zb$wETowS7+~ti6F);#Rm%j&CBpX6RX2SzCAhxJ7@C(yUMXT70dmLIg@(ts(-aW17s` ztQ#6BZ|=hQUT5;dHBULRyxl{uJ@|bm?bo=LVcsei$Cm}X;*@jz*sy`0)?Wh4t`%3C zC6RsGd(8{IosNFdn6}4Ft_2Af54|r371odl^gYQ-Q(e8-!RO1bzP;vS#ah*C91sN| zTM8!U59Z3f7b{~wY@bj6yyM%dIHSITOx^FbrKV=)frE%?k<>8XF67yAbtc;Hj5jyR zo;Mb?L!UIt<73!vkD_T|A-!;Rewf~Fonb#}X(a#&Slm1)2d6~)1Y}md5F>dJb7M)cm(0KbXL)xlAjxa3#xxH!@>?cNvTXJ%Kjw0X z7!Lu5vr*c!dVDfeV3_>?iSb|l<*?h(V&h_pnhWZ^Ev#|y>ZOH+XgFElr-*#mZi?5c zM9QWUqzVHa6@e5%G-;wB%ov5?ks}2#C|k zo2-ihiDh-+$vN}Wrj7I7z6yY&`*ftEy`oY~9NtD=IC{0~>G(LZgKJs>$fdY{{i%-; zNstETC;^w#WN;@LoU3E)l(*w?J&Iv#uoeweXlOKfa=X^t{Bv$$u4Pr~{2pWvE?8Tf zS7tY|Ud%a{(NKVA9SWK<+qV>I-qdFdR#f1l{KSCWW|Q?JSlb+uiq2INpq!S};X4vX zJh~SURPi$Drf8sy_mg5-k$9(pa&o3r29o2J)X565Z41?sS|8=rS3JunR`g4>`O1}I zjk||iJv~so@v`EuX5;E5U$DkCpp4I@Z&<<<%@Q_nWlBSxLQ{kGJ6C^TU60Ff#)bOI z>Ms-@w0#dCcLYc5;}3dcEfNWZmNHo-SQ}{>MLZ+s2*)+WtR`M&T{!C=v=MvLR8+7E z>3o;CXawRevKc0BsWD(<4CDnx)uJ#O{>;zI2h@};Pa3Q8(DrBZBR(QdK<~^*e=sl{ z@a6PIWHGAa0$q>(3Wv$QijI!%LAXl33TR5&f1_?_L+Q&65O%e}{?j!!l!6@bxYfO+ zbGvqnAr-Tz?hr@@a&u_cFb-09a;Rq#;@L8WpPQXpT%6EN zYH-ESNOE;`nxpF)#P3&SlqQiEK!tV2GpoJh(??V zBii8BFi^jbZP54a#EB3{tNynSyrv66^cRHGqg&=xmS|ih0i3#SDeKvarqYF>VY-Ht z-UA<~)eO#dckOE(7k|!NF324pqX`uxI`OZI$8>iCbCQwEB9BM&SkhCbwie%X&2MTP2a?iCq{ov-Jhma)NRAQb2o{v zEJkMwXrFasQ8e#0HAUKGA`B+F-fo3iW&$eZRZCjBUf$xqoHf2N@IEj~=X&vSD6a0p z)p=zYbqV?7qE}~U5Pa$eZ-Q($j_YKqbSSH@Y_SD$;w}1#Or`vn#|n)HGPsN@T`bxv0)Ib~_IdH<{ zDrRJ|{w*$TuF0xTtLtn!&A_81g%0V23zt0OhHKZ8cPERryyLyr8b1+E;y$N+7(xZ5$2NF*N?rIGMCP{>jOHrIaadjG_HsGQ{ZpD*ZgJj`{n+6rr_Gc3F^RM0cu(7F8E3%tjOevmW0&7_ zPt8IrF6Fm7t+CfDd9XFxi}v`p$0z@PRK1YE!CiTkaug;erp;=NDUANjGQ-WTd=xga ztPF?&VsAbU!6D$N@Cun4mwL39ZlG4mQucIgta5iRl-N#SN=-|p9|*Qlmi87Ui&_0c zI~?7de!h&fE<-EHDRvvux((XXkP8adrRrMuB z^qT3U`h1y)P*HdORMKOG>)g1M_Ojo%ori8+60Pj+I|~7ajsVzi(G*ZZsIsp>_M(>q zTS8`#Qjz}6lVDpu2{lF{s2!Yb+x|U=-jfr_ z$0sgUU~dnpd)KAtU$!Sxp#3KY}rq)GJup&tBUI6tj+8+!t*#9u{u?@akx zlfOOlS|2bnUbh%JPy{*_++-0x)eT!sn$Nhy%<%H z=p|Rf)IP(ypOoThLf248CcR3338P4RlarPMOmEBl#(vZqr+#26@-|e4>CB&k6EO8M zma1P_?hQq0JwZ~Y4-TExCb=QsuyhsBdH=A778$Lt2)KuXulC*nY65?(>$JpRk_RW$ z4b_6D7W%%;;?+G}`*y2iPmeNQ@crMUh9)Nk-nWDaoAl~W z!@ZhRy66l^`=Lx-C@>+a$C#oxz%NPD;|~S?H641(My75uOpcC~g-zLi@*Bfe0Ld2L z8z7NP1`8=U1lhJrsk?lfvf*JDcqGu2iYkv)cqeS_!eG$gfrOmz76dH!j2LSAG>5*0 zfx+^}Ka{t>gYWMp?^JnFShyrxenW&;}N#|%m6nu(R(g}+!j;6u!W$n}?-f+Xx6G)kTT#FZ-I z|K^Jqk}H`=v&{uvN+3dt1Ey&?4cQWpCfEBkR0Dne>x9J|TWwR0)+tBzTgOZ6#^V+A z-qBDF*JD7r_%8d#Mer|7V<@ZnJ8hA{| zi)^GjK2U!4j#nq!xRGW75PF;0!u~Z07Nm5B7Rc!Sou7oIW5RlK1DNt~moG^IewP8@yWXPVsi zr&W|z_rFjs=GDqTnM;6HjdqlDVNHRrl=q|!jG)n`oGUe^?em0nTf$b8PmRsp{A;ql z3eIH$)T6G`pTau-V(n(0P*dr>thTipv|751vspjRB)&GhaMvqOU#AdZPQZK_5?*L> z0Ow_e+489C&IEQ$k$S*S=UJ|n#qyXGmpbOK@r$UEV+XA~ByWmgRk57FzVyPJStl8> zf1dcG3otpDk}YE&OYU|Kmo`RVb@uYhTw(0FIpjw>G2{7sauIS^N32OQ+--A*64 z-5&IHr3>LkF#h6oAeJ4MkKbOYD_MJY6H#u`yBGx(7aK3)R)4^JzKfFbT#IT=jq$$9}Xa?aL(GyQV8 zs&a)C@6O#?Udj?^skYO)UJdMw10irgth!Js!$N|F=g){0 zb2+TV{sh;cew=v^NFSqW4}3O3y!X%1!WOo|q6(_OmS#JCKw~KW$hAd%lW{|+Fu#I# zaj|dF+cM)74+Syhp#@DrZwJ%EQHF;%XF{2ZmXXI_%>Mu&=Z>|Y!JGoMV^`ukhd#u< zJCwy$P`Rk;qs3CP??~$|_-y`p<7RYXkV>te^?Z^{BI{bvSHB5_=c-;qo4F4B$0O*+ zNoLNY;X?}oAyXk%rMu?Q-Ev=mYjo+dG19=nRI8aQuu{XC`NA2#KZnK1QwV=fGRY%g zwAWT6!I}&z64w$sCzrqbz_*k@gIr+Pv@q-T?0fcKXV%5J@{C|z1l&QadJ z_2P{8(Ce-X%PI-s2UJW2Hv{`)!_S2xi1U4L2kprfzdpYznW%9^TYj>dB=1X!hwXo9 zdwPOpSCrpG#t9Ua`hWF9`(nd`-$m_;Sq;(+g9Hzhk~096r6_(6EhkuKPDeLUfbQ~2 z9xKL5cRrh&+ZJ@rno*N0O~r>YLa{Ml0-VyKPvi8vluOP-UxDYp z7T00!F(qMKd3{A*fXsdQ@lOL&9{zk*(m6<7?>MqwtDuc;&QQ-7UbyVj>v!38)2s$C z8g0IFdf>OH1Z?60=UIAiA-4Sv!6;b_-n9emAqQp68bTVH& zf+bMJP*O=othJdS>+Ki#0+RO#sMGxOI)Fh*-xN!>jspL);08oZd43Ywm7_JKdQee!Ad&jr>+C*`IBFjdT&mrm8fva*`<5 z`3$d8LL5o$N!D4a3=-eHINu5#ys3YMKQ%kyzv;Me8&|w|e?z*kqz=_YRsAcICh>n4 z;VDh}{Z)ily`k~%!(2{I(Rg71Up(b+F2Djk@KjQ%rQx4Q_3uXs{P-{8k<=91fBsp@ zN&ki#Tk!?p1vq{M4z6283g89G*56!He*-Q8i$Lu=9ec%w{duuK0U|etpl)$I;4ef+ zBSDRq5ixmn*OEvPXOpTHfw(L<0UGf73WkD+$9L8VZJB z%ZyIAw?812cH5J#W5=~fbQ|1@WEB;yFAv8cJH0yQ<`IPtq`FH0j879u_kr5U8@54I z;umh2S9(%OVE_?#H9g4g?e|cI9_jmLMFe$d3w2}ptgNirKNRUdOcvT2I5ydE$$k)$ z%2YMi7JQ-RLD1y2keL-CG|sl-XK9oNl7sNBv{I5P%)85+Ix(2HC>jsuQN>`lpkD-p054= zp;Rr)u!1zh{qdp?{;)a+*tqXZg9kfS*($HjF)X{f`;CVXuG=hgO37_i_-_5aCV1P`(^c<&1FBZOxsZOx_B-fb zD#S6Qr5?QS?gvKx_v}RlOjmqFpLIJ%lhvBZ&((M03L+O(*<&UbjhWs(aM_%!CrlQw zu-G@J;lpToCXG?3xz4vCOuA}Z2lO=mV_jZE1c6%hWn%_(m>B`U{0Uk``5rQ z?T9O>wN~RO2=Ho3y$$6{G`M2ngca9IV@*9E#j$a9N}SXO$twwCplWI6L>!;L&y7pP z)q5I2xKLZLcMzt#0O$&@S4ff^)!mEWfek%wb&>%Cd_s%)rohxDH^ z`x{+{6_mv~t9RjSs|J+(+Rxf6t?(l;KV0v6CtBOvs5P|o<|7on7b6lp9TLCsl?H+3 z$bo?Rr1JkfZ;B#^N!D>yzLU7lp#EK5{v%mt(mCRhXj9!fn~f>H==jgpZcEL+LhbR3za{V%azb zNAH9R&a8JGePaA=#1St?`;2#hTKCNdi_q^}d$ADL!?D2+jUyP!=2Omeec0-{ zy@}$UmD2tjSzmLfE(b^NmAk$B*CgXYnczkFy3^%myUdnfq7yj}F#?ZZ8H7KThbYpT zt}rBK1p;EGUtu*p0OVQEEJTZKEG77L3=GhU!doTqSuaHQt}=W9EGN2%aDZNS}4nxIK8aTD&sm488xW zjH6=9mVz+=|Kr&raiYhn)KpymqBjgp!@PS};%jLje!4Q~3$^V9M#>C#SC6X}@pMqg zPVa7U;^`Wh$n}P^w(FKq06rCDQ~fG{&S9#HYH;9V4y8q|>LVEOukl>*{*msM;6zn>d$H~hWL??1Jh{7#j53J>Mv1;2L{EAtX5E{3%@ z7s0+j!|xrOtYi|49G*^FmmY@6Q+_k!wzAL_HxHI#@IzI8PKd*5=>CBM8eXzlzMgQ1 z$YGy*qu@W16!!28?KwkHC~T?&Lrxp|xW<*+yd5KqO3e>D5E~s_vHY9fRgBl3Uh8W{ z_gsSy|4%WQ@U1Xng8P^@mg!G7LtMZKpDn|s7=$9w6Q20D1iS&(OD6VBsu;+SkW1h0 zGczHr4#u`5fXJIub?p$N}ej;{PBa3)vz)D5;+Yw z0Rb7b(hu6$p||Tsy6y+E+cCcS)GpZIx;}s21|C;-^)hHKC`^eQd^BkMsZ`*_9YWeC zQ0}UL^~VWP%FUVm_ma=J>Ijv_fEoPH$yBDN0SzbZvxuGZc~eQDc3v)byvB>{Y(O^a zI9wsEYwRq%3?js4h$QOwoaS5%X&=9o8Dg3<p2ws=On}VZ!KHdzi9Jkpwf) zrqi*10_PlB=2%*)<4D}W9PYmqI9j)Q{5wNjq9xeEWcRC%OFiL_-+l*Vr$5E_%3Qju z@3@mKVuYFJZ)26xdJSDeLep$94Gnm&Kaj%`&hGey*WC>QLN@o!&;VZsxLo_0$)OaI z;+Am)UpE?9dZeS!easET54A(hGpf(-O>Q)ort1tp)&0(%O0ANRVX;-#reR zbvu2~R|;`@%(sVcLJkQDDTT`8@hWZUButh{?r3zg4K_lyv)De=+Tkbn(oazi87K<) z7wTW8C@0kaL=!X>TKA|%r^=pfZMW_-YW>RTy7N=i)k)=*5_0jbT?rsQKLV~Y*cq)) zIGnQ}dz#dL43$_&UbzaY>~z#&h?0FrmC^Zo<4TsDDR##O5Jb5-GH;B4WV}*N9L2ER zCxdu53olSH72Sr}em+UL1X4>q4t`7>@!o2bLL@>w!blJyHrm2T?-)ul;rKo3UxcPy1H7>+1I%Kj+Q{i^ z2P*F-xGUL8YS)-xnmng6OP!jB2Jb;=xRkpXQ%HJn6~urvx%T(glYhKZyB|U1^+fi_ z#EIYP2C4JWfhcsc`1W>`+~)!^x(^!Nif!?L>BR}i>%U%S>bFeTSiSp*1C5#rCw1d- zZm3UC&^8^puFKD_4*|TqntqFV81TQR#-?uItNRC&wP-K3d(BRGxRf$bGOeaR><@L+ z`D(VN6}D$vk@KTfh{@Jpk2sn0o_QAdaU-5zfk`1*e<2joY>a- z(>m#21`q{H5!o+(z#|JKX4@Bz5LDaG_pqu=Uqof7{FLZX8u{5$UUO{ufXjNo!VSIy zU4Q~;>H%|2h*|YjY_(cEk=@ft9fINQy_~TP9>QZDVZ=(_BeD?(B`3pwM?a>#ZzBz{52nQdpq}~OFD0M9rqWD5B2)b zQr@sgfxND$5J^u^UN4`C9L<^|!IPGni>1cw?iYfHgQ#K(*CUrhRGv#4=r4T0#e^}_H5zE?NP!)?3#Od1(QP%I@Qf+_(9J%;dRu;44 z?XhG=ChFN=>vjP%EcUK&KT$eC>t#S{~ zulsrLW6aBB3!MVZB*`m&^;^&AuO*qI3LB^9F=7eCqs-Iaeth!;{^Li8e!-)*;nXux zkHC4PF<0SIFIv9qnqd54Lx1M-zMQth0V`e~zn@?u&`SB-1GjcxKD$y&)A+kVQWeQk9xFQcQQ=+bb!JsYSmq`x&K$s2O;9%Xx|kt;m} zgeK6wglHNT|0f1o2CXfks#mY{AuT-lAiK3{5(DMc7w&RLOL8$kmE;+_=FN~tu)MS)6lR`>v6!%@cvkrmuX?4=C!^2*)gH1 z{RIYL*{`n?Mm~?mj%nA@c?VWGd3$s71wN&WlQ?d^j&~A1OOL@6=vsf^mSyjNy;IhF<@tBJV>Bz|E}ZE^oN^wN3hqxvtz{5KBxybk#1J3R)9wCzf? zGT?zhS7&LAsvd9B0uQ+TYt6{D?=Ll9L5p`J^~jZ)4>+tO>X~(6&jFZJ&YX6nuNonP_NSAoi zPH<;%hXDo{+-G=`bMCq4ocHcu^>3=Enx5(I-Fxp|@_lQs)ewGMC|_arWj;R0m{u(1 zX8N)C#UAaf)74`Utd>(wC-Hg?m2**=8FT%|7ylUI5iVl?1@>7IAWoQ@+Hb_@4c|H= zwz_h(g`>o0b)2zWhaG)(mePP$ha0f~O}!p9rVF|$ttyZGn5D}DJ-@6#387^V3F}L} zZoURsR$iu7Ne z8%{)BU{{`=QakF&w+k4oCQt?>G!P%{R85rg)JHQwgOqg(r><4JxxB83mHgc?GXWl4 z+F1*z)Gl*tqu1I7-I!ye>pxdxX{7dYNM@U>{E1Y7^%0y>&Eduw^VpT1_}Zg&V%9u| zZIoC|8(yR7?~rCE?@p;tZYyQsC4L*%2MDU$y8tnlxh*J5`b-7E_9x;6ke8T3~J`0LvPW+EccJK*!nS%6*E!# z5Z}oCnTP5RewX;)c+@nf(RHccW9Np=8z)e*?@rA80f|VKVE&m841&}KaKoa^e9}1* zrAy9fvQ$N@B&y^I~`7jb$>9br+JjVv6qv!Z;>ouKz><`GmBFYi7-s z=RUEQF_?+E3>SxUA9jLLsyzudiCQ}Y!rU&N$)&tW=`qo^X9G9$4V+;t$i}@=bfAQo z@vJrLfHan$LCkD>=SrP7mR3>~FtKz$`L4Z=4};3W}b zoGm`bquFW+q35R85i%z$XM(j+{oF2>6kcB9k*1DV2W7CPzN0q=Nc64@COL*k<@Wdq zcfIE6vHx`P|0}YHIfmzD;{+hhjTKSkuC{zT{I9Lcw=TTfcgnSs#_g7hvFs^3>xi6N zK6soZaRZLD9#w3bGB|aW`>%hVk&kM4#laES~-Oq3Y}da7=}Ws+YtUDv`U>cq3vUXr0y@` zY{%U0ujVYY2C$|~(LN_i=3k%bcP;msAFG*>!|Tuiq7gSwLZ)6G|(56JYzZvTvu;5stRu6liWGGK&JJj6FzHvRo{M2 zeB`$y)1D@jEy5?cc5#(dEB3yddpqwCjjo);e{bYIK!XJeJLFth!s{vTzogX-6uG#j zot3;o8UsvaVT5~kkS~t0j%A9)i5M$^FT9hh`3UR*=QGuUMk&!fW?;t4u^BuBebc6j zU{2lC@_e^9$TkZLM*&XWO!zzFpe#YO08eG5pm- z;u6yv(f>1+rh^ghSXg@R7RH$U9rrw$%z>U&BaaKH9KdC6@aJjAT1W))6 zS~p&oT(OrjbD7Ui_F_>BFdm-Sl^D0)>1fZ zty%BrPVm|Ki7Z!}sX1})lkjfo^N%D5bVKBv)yxx5z1tlDR+3<8>7(IAG=_HnVAY;h zUU7PX_lEt=`^(ZVDNc+DajwC_&Ul{T~nTqMrx^Tt|5fMe2fXeAe9h-LdRUFCQRFYysH|-+dQu>p@36RKXpVlkLbfDhujzb z4HHc&Sy)^4Kh5c} zO9d|X|E9zJmm5zuZ>~f3*!62tf*zx9oLOn;_s{ZPgG_K1=I9&5Rz|PQ_ZBFhqTkr{ zfkR)>+@3>>A(#K#I6bp;xxU6gqy>(=S_w)xt>Hzr#J%_vcQ6F|{lj*aoG9_M$06nY zCKHCG$$d%lzn=Mxd;Z_v)329GsM3fEmEZqGa>=H#{cZWn;+Xw5VBg@U@LEQ~OW6M5 zK*ap~|0HIAKggf$D~$GeL{-qC-O^{pWG9JVBSY%%?5b+>QV@R8J1xDp$xh4U_W^&n zN5Sy4e_kG2yC#<;u3r&|3@hcRon<^CH?NvCi!QD|kWO4m+PY+TAWt@a_Xd(7p2VJ} zIuN^oc7^hXey1<^OR%70v}Ge)m^2`wrV5|U@whBc8RI~2)itmfwezuBbA2A0mtUKa z-{pRHnkE~$0n)S_qVD(q>!#U$e?zbI+RAESTY#`XJ}PK-;qg(@QvH@!e!bXAyM;)T zF9Oa*>(`a|+g}0}Ilgdi-q2}%;q&4O9@UUraCxXOJgN4-2oDsaGp^XbVgWGev7yS^ zSf!KrM=w$yDl5F`BQq2nReK7bjcETvcjD{oj`1=EI zfSRyB7xdwjDgFI2-hKKPiT_o?{k!pWu88_SIusRlX{rA8kL2Q)BVT&N1 zfBbywMFCC2Uh)h6 z&QaLjv92O%PM3SdN?mby|K;x~N&B4(>>pfp-#Fe{;4{A8ZpxqStJOD_ z|2b^UPmkLpQvb=WnW@8-+f*f}q~ zPPZ%mW9Re?`mHNKX z4JHZrWzr?qVA}nab!hnw%F>g^ZsP2EqUjy6<>PfNE0+G>BJzj*kI9^TfryQ=?I8(~ z6dmuYPX&%zt_YJ7bz3vz)~!LAL&x{~lqxdz**mxX;s)ORA|9Ay+p&u9U+m6Q<>H)Q z-w;W}r4DnAQY-(E|B!BPke5jLPADCCrpU>98#WeC^vT=lJAa|jP4&U)ju$Q0xya&u z(s8U%6I4X1@Bt_<(q0suifb?g?#wft`!J&fyRdAv={n$K$(_neZ){G`K0%bt5$Q1C z4iqJ3#X>BF_u9UldZ?zr%B;O#^YS(B5% z24fq}=_{}DhE-q#Q?_xQCC`Uopx(U8KWts>-%Os(-87WMYdQj@p}hNb*PKh}8~W&& zP**yH`S`?%{v$cY=Fs5Il&6^R;r#qRNj}Wc9Mm%s{~i=XfKr5IY#~p#?f(T#AHysg zP9eng8z*E{FW(M*^f|kAogHa#6CHCu%&vF8Y18Ir#_UQrUe^Zdcgj69hEef7bkrGV zj0A$H-wNUxhz7uGlwj(Hwh9?w?2AWWaXl&wWT5 zyPr~Pk{Hu$73C_olg~ad@~=cHZ}8m+ewTXqqs>$Q)v3RZd<{tn`x_q$GD~oxho$=g z3$k&&7>h?3m819N|7kRgKUoXJ|EFSt$s-g~QpIBvI)q~SMfC*bo+F5w;PG~jn_5%R z=64ci;>4&wXRfKpK!Z~-tr_Vk;SY8BAJKmIX%KM7X!S=aqdyPb#k@uWm0`#ul_FH# zf7LA6qgK^_DPCgMhr{&06mM!Jf`pXyF!~*N^)CbRfcno9f0LF-|C9KuMN;e?1R*9z zTSv?4#7ivB;fzYQJfYO$edeS!4;xKoO|rmkycL++0- zrj6pUy2SL!UR&|xP>R>av(KvJS^m3h>5;rxnq)4&$tpVUe6mD{m z;<&{`(44J7Q12ds6cgoodo~~!=uBYyzJ5Wq8SBl6W4$aFYR+#U-Jh`*7rY8XQ7?q2 z7eCy}A1G4b8FagQBjCMOG2g9;yGExDr6YiYV2dN=knH|F!Ds zf3#6nAu7;7QXpW6j{7mP$tR5&`YmB{x@j)u%=}(;cu>xkSv8%8LWC8b{#HILj@fJ< z)ba%RHCWD;u_@&TG=^4LQ6~6GVc4S{?^y(9*-q-J!Qh!-LTopa>qKuMH#Z1xrRl?D z#>!UzXv)fx%GM`I>-5#hh9pZ1xrUpqx&&WAIC&kUkl*Kt$uIIi5danT-1{A=AUfRh zTjYN4SzFy4vOfj`2Q1H6s| z&yF{Ay?uMLX?@4_=R^-yO#=&)99Uii4@KzoNWz=Pe=c!Jl#vU57mFBnaW^%v+*if| zs^B|XD^W|tJyO`8iftvBcKqBQy;gs{X4n?$EVDKOeWkMZ+AA!#G|b!Ctgw>uutuqT zXy1L3)9{+WyLB!N6L1lDYe7nhu3+P^!orjsd1Gu=v_yAfN{BvrFK2$HBEmAVeos&^CgYx;{e=>Nlxkhh$=@Awh8K>`)faTcrNPGD& z`k)#;RNaexzG2pr5hokV%}M zx@Bhn`%N^fACyo=)^SfyC_r-1p#RPDUY6CBYE70p0}O=RI#9N9uU3B?ssqjE)R>+x z)#H$3n0G&;{mL!)ye}pc5u}DdG@R(8x!BTGL5FKUt*!B~&~Q?`X2EkP_d!TR94Y;C zbc}12MQ_;M#rMxy5P}bp<~d&~20nPZOZOD9@!}WqXiVb_)xTh-eSQ7g>yRsvqtJw^ zSw<&U!EL*SS-ogm5t+3Zo(K}**bp8xbF8V=z^L33ETg?9w0YUNN zzh(-=wH9GvJO>*9KvNoUgk4!Y>Fa9Zhum0jG_{I_&P$$3*r^+pak>*`lgH5V$SopS z|J8bA|Iu2bpfVwG0s(%)wAWOT@ION~J}qnG-_3Jxi@p5;nZMtu>HZa=@$P;^e8vA# z!Rf}264-M0KDO~zs~PI`0S{tyOCHBXJ*ZkK@@dqFJ4;FL?$x*3DfX+KaYXRak`*c6 z-JaN#)@0iu@IyJzhAKCpn5J5Z^E0PMyHsAb|<_EYOZz@80hwl!dk6n}Yp zH!+UQTk0lArU=RB%jpd-^SqkD;Yk|j>Tkn+^4mSG1VLLaloIjby|Mol*^9(6LpR)H zc+sh;bIW?h#Uk6a6FP3A(*>;B)~KOkTbVSO@%!Jt=Cu9`Je%g2`RS*6%2FT?U?&#= ztnd!E>7+(x;KK5&_T1OYPr3-)U+8@|B(L;%rY=C0v_xA*d3#Lj?^s_8u87cX1Spy2 z;9s2obF1I3Wpx*1xBRIcc9(#^m`08R@&}UDv=(o6Q}w+>?Fcc@wPooHHvaG?e!0Xm zD(#O^>=ZcFUL&kEHMQwBQC`_zNGKh3bGJ@q6;D@#c9Dvrg5L;a2c5Tn z{Ia#XsiJ`wjM&|u{qe=0S{{4n!jCjy*JZZCXr7;3Y5E~2@#o;Af%3y@?bGkd&DM~z zeKpI&FpgEe83=$Vfda!2MK(U4sAXp~OCdZq^NHZL4(ze)Yb!=YVlH|HhvdE2wjxT{DU8ru3!dc;Dyn6QJT??Vi#7X?lSKRKfxcA($dR2AGKPl4# z3M*VY{NG#iIg0F6a=ARm!wH9^yylAfpslpt`xC*UwnpTi^CHm5;)G3M;n*qKMGkat z&Ys1|_wKC>y)ylJ{W6DISaSV}=gWGsIE;IJ4}y-UQ90d$BlyWz} zKQh6*uX374l`f_K+$)VL4a+I0kBALlC?|=GO+J|SwnyMxcZ8mm-bmu(G8XoqbrGv* z?vYfPGA1XI-ps>u9^pARnj2#LT&{d{PjQbjPw{D|-gh3YdA?gApWh_h$EF2vsqNKh z0zSm8IzIzWlb2{;E0sO|t{kBYD@K~zkSvqn_=)~q9{u6P*r#hAr(%(n_>CXt<(oge z*uru)8E&08XwmU6rss(fKuA?@ppN~MDw`)Rii5rgevJ~hPM?Q>l55_7wHyIh9B6BJ zB1MSVVS}axp!ORUv}i`1H14m z{e)p3>6o7iu~()Wio;JRhEADW_IfvMAV?^>XA9j*`YD}_m`v_UooS4=#>$Shgls-1 zCm>z?N3ULftmsVN>+bw9a_oLRFhObe&|z-@Up(!-w;pusQoPd%w8v6#6#x zNw9BhM}TI5_`poFdZ@!m@>Or?eA0Y~xm#-1 zv8Hdii=Y$2Q63VkK4D?Ty>@k=m!e}6(t5e^>0mUsypao`3?_wx4{^Gnen4!z4lasNg%W&}X4PqLgW>Nb~D?e{4k+ViM&ROK`E)iu8PBCmi$ z>Dw$}|7IzFAoDx$iOV9tIC0uB?5oGte7y>!rGFb_~!D7eC~(@tZ}cr%J0w`&LRjzdjoFn z*@hR0UQk4SS%y7PN$1b-GLkBly#I!6$gB1+a$Ajme2;{{{jOAmmJ4vI5Ijs7k zE(W54qHWr$zWP3=cOJG?IsiRYk2B9$sF1zr(~xvhug@0CL*Ot~p`(u}v^ZB^r8Y42 zXGwmHR9!W{49g6S>T{W$-`q>HoOBW@lsA6*%AFa5)X%R0L_wU5Ol~#=syylHhdi;~ zo@!F1GM$oaUj5v}O-rX3F&A4|LD;1A?x6#l?NG zgs1y!P*>fyPAD-dGKcCp!zUa!mv)0f6BfYA3F;Tu)|U-Lan%-=;t|x@lj-a-Q&e%+8zz>V=Qx}jt8v3Q>V`M3 z2i+%s_Rg8GdqE<$e*SQeqY$jLADbtpR%n?9@_nl3u`cd5wWbJp=mn(!VJ@-IwPgsj zx|L^j^UWPY<%|oQ@5H@_soH#1ERLfZ=G@}XsH(EqsgRr@&BPVj5YQoIJg`+ZAU{6q zI^R}S?{1goGvgDKEqitIumOZC82FhnwMy@%H~5~9P>iOEXVF@7^;h*R73T*E>^L(C zYTX672}c-~j=7TYWOboJ5eZk^n>Z@1SFGlL-Z_>Kek(ar-MH7767jnYc#41H!J9iI zwVp^e>RvoHLMj^H`e`Z^QQZx~0rk-o31_qqw5j+lcd#g9xB6)88@=(UISynh%Up|d{-4%5ur z&m~P|oBI)+U5KR@yVTDAtckAAT};@!;5q+>Ndj63uSUdGZ7Qr7&1F;Lj^#p6w0D%%6{3V?Xr`^6( z7MjrU`l3CFu&X#_8zMM~S1iul1Fn95*IwrVna`vOM04H!LiO{6lvb>L=Tn2A>#wF{ zzIqw7&_U+@kWG#vZ=*Hmp{2eZj0>s08Mh_jlyRaJzyXz{KyU+xxeB$K<=r>*^A%I53$1&BF7C zjMt&-4rmqyuoNb<@LpIzMyu^Ne$$WJUH z4lMeO-%hFUZNTm>OW!t;Ok^}dTlz0s>cxu#0S>)dZ<~#}ESa{|M4idvl#RQJFkgSM z$C?F_L&wjq)yA+M4H<8(<0f(M-#@b`gMAZe$G@J}g#8la?2N=S>MsVp8x5m~>$wrV z32?%y&!M`R71|cDU5KwWC#npz61kl$C5q2)}Zy$yyB>G#bM(Ob1@1Z4Miu< zN^OfVXY}f9NFEcZM=T#n{j;0OA`8TsM5bL^r6T-79g#w%RCilr?KVt#b0X$o2cwF^ zj5J20a_`x5qhVVAxi@;snFB)d*>9o4NRW1emq$fsET}scjB5M1{Ee;m}n&A|DO`E%fC$hrG9VzT2~j z_l;dPL67gd%26oL_3J};y-ekadg_U((M(UA^?JhMc_jnar2^1QLS&b4zO~@Qn!D6A zb2lPRR5@SF;W_!yiqrU2&!}G(lM_*|G~r0d|nY`rvlYDY|f5Q*a%W>z4#1>GS8l8XzXMSwAr|| zk^5`$XDDT6tch%4*{*?DYSY%E#)}cm9Em)#bf)PC5)ePd*tMwJ215d=J|0w{DtJa= zu_3l|;Ar$X2=!Y0-CXYD@}#S^FPEu46kM4tbsJ}tK`w4=00lRt^Pz{$s}tM2Z#5?V zv8}u8%~ql*KYcS)U51`|c~gz8z2d@YI;Rr({1N-4vFB_7Z>}c8Ff-2NjT#ybsRy-4 zbVf1T+!58Q*vizt$1ZMn+&PEfVZG)r@imee(=1UU5$m^!hnV?8#S$X35!(~A3LzUQ zf)8;8A&Bu4<;(N87sv65L>pf`cD2!*@?aUW;%CRjXP~W?@ z*cv}MD$o19BRnLIsvG@%Rejj{)a;m$aV72DI0nIOfXgl(Z^5Y9rg!rMo4a#+Kl`@r z7+}KOc;V&^T3y5u0QxnAStr{gmOHUJS~YxKILyQlJk5VH_~y!Jn5663p*6R8Ot#Uo z*6DU1PPAAPlCzu9K{?6nu~9{N;Nj%9^FH|9+q#kpcT>kz1@Q|}RjqeN#oLY7cB|R4 zDe9w4nt=LmGj3Ustkb38z*8gV)c(>_iX%K&f)2k{>V;ipKJB3^g{)dRxbE9nxPj=q*z8#Ooz08796%6{ViJd``)gdrEnkdsKz1^5{dPvw=y$~e%l>Epucf+&egqC> zpRD|z-m#}>8nq#J=q$@Bi(?aV9I^1Nf{JKUC9fbtcS2O;zGD#cBN&3jhqosg&rii7yuq8bUu4EZN(UliemsIEDIl+oaMavWeb= zy@mfuz>W-WT;sP((jU8`0Ud2=8Kg=#F@+0V8xBv zv7WORUb>BXW5u?ci92mJdQ(@&My*1DG!U=R!bY(7Zo;O9u61Q^RoGH-d7wr9z$$;X z%!P!CXAn~{pEG0*+I-36tLq5f)f;oy1TAYrFOqy(OR~Dz#;0mMM3Q2 zi(3aRn5t}ABb@2(g_WphL+kSw)vEVB9JT6z1&6zg2S+CvpqHde&EE84cl;R926Qg_ zRdy$~CZ6p~(nlq9+Fth5hQxOPMGz5;YZ(M>XRecEV+R>b+b1pft zhnUGczFk_5y7a;`9(bI;QVB_7oZ-7u+Q`d^Yur1vZ`yW_J3a6}n}1#uz|=(8n}U&i z23ft&(yureOU9KLc`qefuL6b_!jzhCjHm*3Umm3`C0#)Qi5E0aoP&~Es119MAxG;! z(5ctpi-5kydQ>d2D~DTRtj&#dCq$UFXeL$r?xlo0JY)49es%9)VN`BhzchJ4fZOyD zD0z(6KvkE;1iu$gmAa*S@Imm+2Z3?6#!$O8k?(e#vR)M?oeP;XOQ^NvS-nQa-&wR|V~NOfnxZK^Lpl`<9NBnI{G`fsC16k#yC?&< zR=1Dt)>&)J{QBsGiO1DL&9ZX#+vRU1;-sGlD+f?afq{?F984&UIap}SzMH`)lA4$I zG}j{9lZ3K63m-zwQ%4gj*}wOcQ+2N;$vLo)y#lkV--)oh-rEl)h+Y;hhzKsb#&CtW z?0I?lN9~Guurnd5ujItn9Xv^Al%(1_twO#@x)domP4V7S94*jgUv>O|v^s38cOgMk zo}tzpz_>~fYa-pwbeq!fMJ5bfg9u>Ls1jN=ugiQe>kpa&u`h3bEH}A0=#7AR*DQV^ zUm2`_ipkg&t69nt!GBo`n!Yg_-W5#hB&0sM?nvV!S%fXu1enZon5^G(f}iAk{P=|RV{q^}tkbq^Z#6p}QuzHcja@?{f?)iea{w^NPowAA6fn&wQ_^s~bR(;V>&0p6|q zPd<95(Yq(6@T|?4A3N~urn{ErLj3SSbeEbq>Na!{JuDU~1H#66~2(rLJ_c@D{pHwwINy!yh3&^@gvZbH<^Mjkq~}VU>Us-S@kCZ zUY0qCB`)1*7B0!DW;`gXpE&$FCYH*Fe#ktSt4oMcTwVK`AL3)#<+6d)%krEX0}+x9s?LmXO&Q)#XG$h~G*`KdWLW7}c_d^cqU~sj zcdht)-*%&L#+hMF?=S0|@ox=?BGo>8X2d>ToQvk1FxvG%dmewU>p_d2*kPA3{kEz4 zyN$@fgC|Cm`sqvbG?tGre!(%~Pxu&BR`Hq@?I3ZR`AxHLzTsD!8W^p0fOQ8P`i zktD-1Cq;JREmaItiBY6jR)9?*3JC zEXo*}k#VW3c*c;DTVH zVI>MDwEk<8v68j@7p|b7_G^+7qr&2v+mZtV{;7tX!JIb4+wFneBN*J816-g}xK8gPMC?r#(!vdx_~5@}UuTB|$Z zprw&cm_8C_SNmh3j6|YM$5S|y#$Cu%kl2WKyKJhClNJQI&?=W`3XdDc6tF zSwh?dbT;k#!EJ5q4!b&y0Hf_P@C*;1!u6_!6V`1M7~b-c1}Y(gPTkR(Od1yyJk}U- zE+zhYe3H;u^OY8dTVVtTdrch{z!4Jrmcd0$46c}3tn z--jrU%A<+X>cmZ=3qOlH-<<*Fn{{??b=H@sFAMf97fX(M&;ML&qwu)Rx2v=Ar6SmdN0WxxRH3Zq=9%^3aI zwpUpuiSOo}n+EJ*zBj!t)`d#-NlUBh&Og6Addc2vz)gr?!A2b#hjyGW{|^^dCMmviis)|FNae@0MliD zY^fpZ?GZR1v#RKl+R-~_Y7dd+mYFr6t}s#akw5SHR8|EE5+^Zcer)wpMx#gIuj-^t zn(ZV}0=L$lUP8pepy9-hNAjTaYon@d!pj{9oiTHb;$@5c*l9v9CN{Ju5rNuaY1Vl| z0`U-Sh;sEV!4;Wx6VF?EAO0#fxzT5%v%g=PRrZOR5dYf(WR`ujgOuakbNUyJo%AoB zG8|H5W)myJ)@|;X41?t?b8^f@0*;YfPq0spk3Hf#{RMsv4Q*dnoA(yRwZ!SZ*YHb@ zKrJ+LGaw+ORkB3~}ytuU@9BZjola|((_ z9a1gz=C$)n1xtQyS!fs$c^RT=xttxGLu&A~aqi@ZPdVlX5Qw~`-Duot0v z?^G;9)30bCe)1v|&ojrT1IU{Iu!$}B@GbR8Pd*e>T!j2xJ@5qwlHAP45Sd8)4@ZN` z{pAB=WA1x8Ur4;O|2ZERG@&jWWQ3|-5QOf?;7~w%+VBGesZJRoQDzh(5&9kwne!zv zH6pno4kZ*1G2n=QpY=*hlREU8&fr>t#8vt$BgrMidym*<8M7=9(V12_g%0=l%f1dq>Mz&mTI;tc$U9&KITi_w#-Z*nQgdWN8Td!H44gc4q zq_?LN-MlJMAvw$rOUKFjpbyE(Ld7hFZbBq^+x5SMdZf5`TY#nO75AvE7#BSE4MjOF#bCNIX%avm+8C3LH&l zQ#C7xcrL!bao<-|x?O)lRBW4Em>8HdklQ6*q*X9#C;dG5zuhS!)fd$dGWZ4cuTg{e zE!*LQ3p$&szWt%!aOFncYrbHcA2bMHcu*@b`oh_f6Hus?<;Ridl@hDQ<1s4$m$9} zRf|pQyv62f+CL{%^U+-rXF`=8(Pqp$XyH(c2PRv}`O33B z0ihIu+)MW~(U#r1oNoq*XrnZ}#U=weOE&%48qeo=B+-@GQeq9x3>*T9YhS)R1v%I1 zDyXWOpRJPHEakDp#HX$8Z8IP<|2aL4FKTLQ->52XR?d_*<~#)R9eJ0Ga&A?-&!)4B zlJ#Fyw)cFdhi-%#BeFrvW$V-&Tx9}PH?s0=Ep!R*&BR;}C9Zh$A}RxY6`ukEVLW`v z4jVr0I1kqqSS@!dX!IhI4F$E9orMALO;H?6dy;!{p0$CK=(o(ZVj^PK2gStK4A)+a z67JX&ed3oKH{Wo<6Cg{$~5?PNyZQWat9B!mpe5vEP$7c#Y zuB_(iTUGm;LaA+VBqrXG;H>Wj+Afa7bo$8;ok=pfIudlY?uUWVl(Q6o%r?ov#gzmY zE&bg7tCL1Ay%W`PCGWL(xn(xPB7$UnS39g?1!5Dmg%k6Bo0Y*8MBSk+Bh3Mx04rKg0R`{_&$?YRaf= zpj5hGS+05Vk&Pa980+BTNEczMk9Xjg&X`s<$3yGL!`>qMbo{bRvt^;FR7Ab8p1@<{ zh3l>AO+@CgpR^f}qZ?{UnVLEND;X=L>rg+al&sk5tIJ`(EMj28dM813u+mN`5w(l4x+;74;A&P;ZOcWPa@cy` zjK@wi%M25SG_h5k-tqA<^F@|>ykfPKYfmg44Vq2K{(A*!Wz8{mf3+KaYzl>roUymC z^>f_sRaM_lFYL*5Q&Uk;!{3ZYyuH|+$ejhYxNW~JmZsrhK#bdGG7QWp6YypE^!epz znrraZt0E99S|w``i0KFZvM9aTqqHyzYgpsz&6x{L^33Y5B}>wujSremN6$hZ@walt z0FSbJYXWb6#;gu!P{*kQdeX)D*~8x30&|K1<%QRq%eDbtt(bAjt*JZhmzmYu1qkEK zQNKP%LB^SYY^Kqv)l6@yUXzwF4bRJrA$>=FD%aRs&mTD^+Clf^xmbO^i6fah(;|N2 zl6tbA(In27_Z-NX$(}Rp-#0V_^FO8)2V%c@H2$f$L~nG@V$9bzwny9bN*laQKy0s7 z`YnU=67)8;nlDpdHc9yrtI7p)&wQ!!tfpzOyFuxy3WP3exfHiU@d}k_*XkB^dqFo`oL& z@%xT7)lja3QFiHaGym{QsC$$SF9{Y33hE{VtbN4>8oXiQKpGmc|5lPsCg1F$D;L3D zo*|NxiEn7xkM0qYhWyt1j)S$nMARD}EmOs{_yq6MpNgj|wWK63L-<2U2}*d_`u5?c zq|BG0$d^ZN3)Uh{N+i0Pa^%CqO6Hx5_*?h*y~KqCJdRQe)}C#}xoODco}%P>*JjaE z+=P6Bj{aCy%(Ynu>nza4+CNAep{L~NCVRm0=Yn&9D znZS(gmasQi0oQKgojsug^x|vGXH_M)DB*#j5gG}O#IBD4ue!R%IwkxjRQDbmxKd^s zO6+dAfftBRH^#?D1Ljt_6yWm-X48k=ZjqYVDqH2f13LSD%XI-oC0eXEy7fVk`#+dP zm^qNxFl(>`M*`!Df%L=;la!!vn0Ql1po5UUktHo(4nCE|-ic&xmSZ zfH9-1ZYAB6 zZ(93Q*hQ8`Tr_QWLq4!8;5?-(9j|=I1d^mq81!)Rxh2iDaz*v-{8Q_FpILk+u}14> zJFzeRMghshcj#4slehymlyk|@n^(8+1cGVHilU6ec_^`?)5-DqDJ^e_BIgJ2~&+LY3l8d zNxyOaI*mYuM^(@3HD<@|(ip^ei#0jsdFQG(a0MyZs^o3wLBx(trPbL<_+=>q>G}R{ zV@2dcog(5wC;ma%H{$pJMGkoDzec3OqM+~@1=)@b`w!8usMFlTU9r7`+VC+dK)jIk z!*Iz3;OuIF@Lt%SHT&M{k-g?kO@|j2kTyjf(`frb7g!Sn5frS1CCVv2Fv!;eg1QN5 z=*Ln!wkiSEu-S44Js5c@&bC+}^?UBum!=2mpl9uYqWHjh#Sdt+>h?D$;5vuZ+GK#Y zlj-8R!ST~ReRLAYM6n`0DnxQ87q#@-AEG7byrMd*JFO2dRK^DYx`N+sC?7)y6a@rU!1oXm;aA67 z%Z3;eL&VrumNYkGKn!w`4{Ft=K7Nx#HU3u&(#5-h#XSF!tM2len$fTEOZ1w7!IF7Q zxIogbG52=Ufw3uFfiW}^jH1;PoX6#w58XK%ilt&kI|IRc!$zWspq)i&sJy||;$DC+ zUBHFp&&Ey5tkdki&fj3B992xI@;EBEbv6S;K^~B_jUal1gq}A{6Z)y zCCdpJ>$=~2Ln;o7VFkq`r}LPJG~rItI1ol;Ul!b%TotNCk2ZbudN|Dpe!29uu#i}* z6jg3w#=&Zj^=P1!3Tb?=Fi?pvxkNjEP<4o$x!THr6}bo#i%AT|Po=3@rXpX{``No2 zrEu1$!uS7l_0@4vEn)v40xBQ~NG_n#-O`{)NQ;C>NGUB#!%{0PEuBkBr*tk2l1q0k zuyp6b0`Ge7``&whzw_t$%sDe>X3jIu^ZkCGndh!)^trp8v^nOZ1q&44bkAKR!7k!b zfQQ+Gtxv)%0l^7^;T>WwKQgzKuyb5SR5`OUNMU&z0@M`}tU8*Jf~D8vhM=Ps;4iK$ zI#=-5yP#!a_MA^o4`ibTEGcs!z_b$Ah@YJ`x zS)d+h4wIK%eavqWdo3pEp+xeZ76XmRnnCeB`(=x3LGc<`LI8@K7@n3AW{*~{{ZrT1rqi}G<5_mq1yH}@s<_d>T9?!+=$mR+?h9;psV?GPe1G6khnriEQ7)_@U5@FQs|3v8 z;z7?oGEgm0HcNi}6pl+FI?{=^)GTYbG!|Dj7vGnzVYnLeC1obzJ;vq8N-t%Tx2%Y| zrrq}(I_MB{XpP;d&9f?X68xlC{_G^$mUq9y>%;Bs!H46j+)Yk#&EVippU&7$O|2c` zzoWEyz2ni6yI6;0Zjq&8_K2U;loHh31@md}RJh;!@CpFuZO9|t)C@bTrv>%)c&&fp zU$^TFz1l=S`RL#x@<2i6MyH#sZ;QX7o)2sm!hcUf3>vXBa@IVV^CQ1spU+MVKrsWasp zlHg_XNlslAhgC@#P^{(0g7wOqY~0I(RGZldihiZNti#_6y&uYyO>b`XZN31Dt36#j z#RKKd^|qX+D(9!fF_f%uyw!8@8o6*aP+H=4j?~gspKnpSG?DDv{G?!f@|eQ+=?bvra&a@ntkmI1u& zv=2n2Y92EEGB-VKu{CqeRm|q0L97w%4h2;?U-wdz1=Aq`I+b*sE40Z-#D`{EK|h+7 zvUQzPnx@0g?7lYj76g7H8VEXyapg%B?;j7PuI<*nxQDsheisblC7lC7y;N9H67Dfl zAU+LGzVB2h@$=pRHupQ??^waC!3BkrfBao^a4&e!D@ZCJTUm?2F$88ke`v+__NPJL zx-(a3c7D!#@2&!=9O0Xf>|8MzVa4QKy{kQkb!XU*61>`K46K7oKSZ{EBPvh)^>Og$ z8mS!%KivfLkMj%3$gH|^{4&`uV*+PkAAw58PD2M%96xL*afP)XoaxH1X3q4fwTr0a zXIir9PRb!Tl=8Z;%IqB@UkwWo@6+`rd$fF>fqKr?oOiO8#Ijj5!@6~ zY*5X6Qw{T77#Or-3No)XU#Ya%?Xba@>X^M6g}SinI8ZD_`}zZA*LYhLmC0Lp8kQ$( zn)}=2zK<=$xdNnYQHcSKC!3=UArwP89aX@X;AT@MwMd(9Pd2BS;!)t+j)bvWc&?B; zi4LIV(e2>%kKc{ODCQs^W67VkA7QMrkqsVCgB~@EN{b9^?}20pt=`z3Q!!~*+x8@T zk%*2on~p`VU_a{LHNT>Q>LjbSdmWsN|E9POsCMRQBro~m2s(Nq_~VG=I@7*+Y*!49 zU)n~$w`=8IEgjOkZ6hq@j&~3U>FgAnTaIcxns~wJ*z9{5*5bPP8Wwd8z&QW64jIy7*5dirnUky=4x|%UL&DGtVnN8-}Yp*^vAW z6HF1Y-B-!b2<0}g@ZgFRI$007A$1D>GDp{Ad6_=qHh>Hk>?Nbx5h|Ay^rcQ}l~a-sTo?Ve3{@Hj|r^lDqoVgbw`_ z$~^VX=f1Uu8nrcizM}SCeQW1>)tPJrq_O>AFB)0sn$#2R9cCKvx>M$A*;8YXCvU5F!fg#S!V z+9mt#>;bB|#X|p8o@6Uk$XMMsRcFlZM63&7+~tv6)P3qNv*z1O+r zlP%e>IiKntmXl!x0fdW*0F@7DPu#G`hn^EN)e~ ze+=U#;F)L)!wD3aEE9QO!)#v9fxB@y-+6%aLYye0I424wk^{U9#XBWd6YO=Ho#1~B zOri51Z&g||^o$hjb%zDsg~`SoH>wT6>aWX8z{0c0II9QdYAE$lz~TEL%y`xy)x6kP z7%T7QJWT^iwCFWdxs<0=X<}C>I$1aRvc2H0$x*3w)(Nv_w#mZv+Q4ZZ`ItZ5JI%|l zG{Z~!L0Sz!m3F=(*xj$bI+VO^@kaxQQP15#sVA`pe}{U&|KQV?5C^OTA%eCa=nm^Hq_(*2oY`nQxW_BlC}}3=1|3 zQ2}|0NzJxW%BJ&@B*b1aG0kpp@~?C)N9cNN=|iC|PzK(1dpoCK>mJ`bG7UZuTz;J!r6v=A3)XRl`e^nTQ2OK--I_y zdAEjTkA!*F@MEs~7xOl7Pj#wntWp-pgw=%#$6y*IuGQPy6JWcXs*&a{TUuIG!0w{g-drWX^d{;t zKEYz^WLQz1xQ?sB-!5g`j@4%P3vfIdhWV~UPZP z-Hz5nH$xlncu7HV`ibNic)pLdl39H((C#A9xaw4@b`LVC3Qef}klw9->O908c1gBC z>3PD_FnJ*kUh?94hujg~R@@)7PsYUR8hKo<{FO{7rLi;Sn`-Z1R&IEgBNlta?7P&R z_1#>DxceFGhILwl1=tzc)aCS!`VBkigZMD+ze9-~9mXDUB=JY)C`p++M-cG9>EdL9 zd|nneymOzQPp_uqSUE8i9od#Ts-n7i5s8Eu?N_LQOnnGWy*r&7!yikK9EMW1)7R!4 zAJvY5z7}}-OT_?%!?T-cI4L0KjlTA{Md^ebY>w*uCOLH?zX~ljL$=Gh7iKkW6p5e( zc$dd#+zR|yZX`2Vgcr?7jHyW>3`{4_Q;D=QZ%Cw+cUn59yEK(xo94*#cb=BlON1cK zL(beGaCn|s{xYS?f&AQPqwyMgy2N3^<*$RJmM50c`QnX2ZdSPjr5by(x82CMB6ju_n$v_WAk%Yn5iOob({MtnspsQftT?3e)pBI9w6ZaNAbGGh%qw z@YD9+(r+xOik>(|_PJpIhJ=nlLJlr)HW`C765%3Jo-+(Wc)!;ucRrm zwUIrkNIUaG-gxEf(VpHs`Y7xz2+{u`$e6OQp zC+9^o+Y))%$)GtASXVc`HqG(rQep}p`Z#n(zc2-;-rrKeiIK^WVdX%)&Eib)nXwMnbm&jaQ{5B1XcN`og+)s_x`<8^S&SE?#Hf3oeul@2CPvKh$?oTqN zg`vypY1iz&4CyhuRST_+tBvqH(wjxaVAd7gzp?NW_*{J@1>Y`?%}c%Yi^Lim7f;kP z826d!GczwKyQ;M8`eo_`AI#c?!Qq#$8B4+ne}!qgg-JmZ`-%-+ z(OvM2MG~{4O@adG87dScpWUz15gW8*q84PH=wQe`^t)gkxB>AnG_t-(03DQeyJbb) zT#)XA*Xi8VJR`-3RW^!0B}6o)6{JO;bcI4fJmf#$oxR9rhAcO*0v$C{rH>=gc~c1~ z3W8X)##MDz0;a(ISKn7gxxmgR#1jU#w^NmrqMrA$v61KVvLogC)8@3CS07Uw?Yc@C zlN}|y7j+a^tsZW=8Rm^;k8K^Ep2FvV2%g>UD8WLoCFW+kX31y#)-2u=fVuLKazocI zgatvKrEa*!W}#JwP06Ky&Fp~A;E;WZagCOEVO*D4sU3<%t2zoeShNNDoEOVg1h4vr zcw0eksHyqX>71#i`Valug)eiysdBs4t9;b^%UQQV#|YAPOaiRHtMJKBGFJYabT*OJ<7vS0sBXcm-5t%8Dyy<;iq0lU&rEHE%cak@0d%j@j_}&9Mv`2=%m-LdeDH;X!1%(~iObhv&V4yQU8;B7$A2PUh|nS7NV{l{)P? zKXgCcw7-4H#LLL*%-a+DC*B{bZxY zs~N(#Wp(56VZ^e6L=h8(z=k-}zThSh3R6Lw*eN6pp=Bq$7G$hk3pyuG+TV<|g80)(>M~XD75a zr~#-0EF&u|+Hx-NI)VW29TWq9jt)@%T=NC`FV&YBmD!oB!U3ijFMldjvhcY|^una} zR}jl!5a^h2^J-y`{(_0PlQ+KUh|F5Q0Hu|0dy6LDN2=x39@9?4D$>cPIPCY#s5+U4 zOFkU1C9rdYHDBKwe!IOz>g(&5>X#}p@9g|ulHaUx;p|hJ(TOtx|(HEm0!k~sF;|vetFY`r@SE!s_kCtLsDg0tNGa9SGSNxcd7}DxeK` zqbnzFwUIj`cG`vL6bEum_tk-Mr>6~WF9V-h=hLc(nymr?^pxn0*l!gD6OMpADj(#J zPf-bp`HRwb+AE0;u2HE6PTcwv9Ce=*@F|HpLdup9t5%xI91luy%{Y zD6j7ZVxC7;WIvp(q^QBjw53YF`U>zHlvnfv)8}|CALWc9-B8YXB551?3taDR+1}c2 zp^7-bKS{~RVpA|HbW7IOKgU4gI1Q+|??Ju2{R8E*STqmddp{CV`rELsMe4wGAkb)7 z4bz{w2h~Wh;x1zmbrO|jQ~k5fpvge>VS$pq>z+5)7f;d?{R?LsqjdRQ4o^~nIu$PD zy)!RgMXJ3eJ^EUx#>Z_mR=Ydn22ATlUhv^xdp8v;yWnN?l9j-c*H?Rx+Q> z5+L?*14r0ifmL^0_NXroCzCZpDi}k|iFg&hZaFT5>yNQf_{H}?yV8$GguoS2{hw-? zS{_glQuT~=z;`)sxkio?!jt;`>KhiYrt!UDk%AQ~(>8-Xa^Rk)%GGrk2e?eQshoq-#s?6R{>?9v*I_80W5zhU(J63ucQDi1HR3fWkh)yCay@7W*aSKyWZU%}`431MSQ zh1oTZB;P~PTB`r$s?o1mtqNxAoxh*J<)`WMPwCI8FaJF8e?pnR?&I;3l99gU{JY}6 zYtQu=liET23u(-4ph36&eE7Z3br2#Mguu`w7`Q3+9aXJzXn zb9(Tv{q*!8<`(Y6uA-F3{I+ljeS~_Dhfy)?ThAx6)_RtOdVSX~#Ra?bNv~fe-0_ub z&?}eH%90nR{X29racs702}-Zs!;($Gi>H|d;!|z7#alMFwm612O;M(xWsMfj8zAlAk4YbM z?JTpLR{SJ%_S)C}J>W#6_X`qSkIJ(hwWLNfp`fuxm~^`HXOGlQAHO!7xT3kf9bCc{ zPWpm*dS7tAqcsAjrc7tBrUR{bdSM~4sk%=$O4?EZY5cln!{IuInOb-_EfQj?ihz9_ zL%Mg_z8}Q#g~Lb8{eES+2PRJ>{5yArnN=C+ioChxHdewhsRPKWQ;&{L?+fholn@dy zP0O#I)&wZ0TyTw1H4m=Rpx$oI6Ws(3?)FhA+9cAB+nn1fwM`$IWvP@;fV8Y=$DZFEANrAnFycOhavAIP7Mx@<1s0|DRNtpw9mu45J~$?Q}y z;S zSL1vS$r}^id%552PPZSPm(H~Ze>WC)`(TQi@T{7~IGy;|V!Fg{5_mGaEa!&UH9k58 z4a}3s!lG)A?TVSVs1bZ$7aIpG&zW2WxhmDHRFQL~r_;!63lSTLqz|DsXzXhzMMDpT zaPy83g3dt^-Nyc_zp1;w=C(7tByU0V{S31aR~ zeK7sfDXh9hG3;b@vyb^BbdNOt@$KcyebqM$%&2;a7;i7%k*5HYG4+6H^@nck_3O5a zV!4c8^_Y#a7*#snPos9(CIP%-Y*u(D-jc7UGajNFQCcM#NrhGxyJ8(3^TWT2X^v3k+ylG;66 z=UtCmyIbR<7zyDucf0=Fz520K!K;_1kalt%Ke*SrV%E$%L z7YSKEZAl%zw>8aBH9IH^bN2cRIQ`2`eh-1Xm6StgiJ(74%Ly}rvb%1C^H9(oPEM+- zlY!gth^!ibOj9^O`Io}>!kR0e`!JmJ&mEk67`dm2QBrCVv2wF+GqfoKM|}M(=alXI z-N)gIBwe8lErrU(OeUaq$8Qj|l14P`D}@aiy=n_9VVTRL;Msaz;(cC5)ua0Q^_xag z_P76->~{2le@t=irA2lM2Q0+Yf-iU_1HXqvgCmqRjE}h@#(un>y2x}k0idda?7`i0mK-X`9W zKztV)HP-Deym$?pxwwGmqQN|?Ghfg3%UbxfXv9o)Ot-1|W@@%BlL(}^+mHWgnRHzv zs!9L!|&%2rw%BJ>9Wb|%n^n89u6Ezv0WoZAQSbz8TkfnM9@E2t*fLK=x zfsj6B+vNc>pKP;_H)lKV`c21?Z)m0Xzbn;#zD@1YwlijF*z?qV`cjjHal=a!OL?Xx z7f!;+zU7z$Npm>gwT?T}l3nyRQdot;gaOgl#&9D5k@)4eMfHHTu!`a?ADOiqt{&dlLdCzr7Xh;>ueq> zK9jus|N92RjSWR@vu-5ye2E?57#-wyF!j=MFYW8NQ>jwQ{wQRa$FY#^4e(JdbKmZN zESfYaZ|VuBML?>goj>4s`eaPt_7EC!1Q}vNnlphW_*^z$1mmB}CU_(|FLA;@+tD?2 zy**a@&-e%RHQc0mkuN8g1I%ZFxDVe{M6~{7uXCM$2Zu4KD8!m1W^I<Vj|YcV+%%0UJpH$(2P+NuEgVR7tJnXzM}7uw X%Xb{{VgDe5fxZ-ERb@)0jeP$Hyj%aW diff --git a/freedv/branches/1.2/freedv-dev/credits.txt b/freedv/branches/1.2/freedv-dev/credits.txt deleted file mode 100644 index 431e399c..00000000 --- a/freedv/branches/1.2/freedv-dev/credits.txt +++ /dev/null @@ -1,13 +0,0 @@ -Credits (code or ideas borrowed from): -============================================== -Dave Witten and David Rowe (obviously) -Mel Whitten K0PFX (material and moral support) -Bruce Perens (cheerleader, promotion and publicity) -Mooneer Salem KG6AOV(Mac OSX Patch) -Soeren Straarup OZ2DAK (FreeBSD Port) -Don Mak -Steve Nance (K5FR) -Joel Stanley (Hamlib prototyping) and Mark Jessop (Mac OSX) -James Ahlstrom (Quisk) -FLDIGI -All the folks on the digital voice google group... diff --git a/freedv/branches/1.2/freedv-dev/db/current b/freedv/branches/1.2/freedv-dev/db/current deleted file mode 100644 index d00491fd..00000000 --- a/freedv/branches/1.2/freedv-dev/db/current +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/freedv/branches/1.2/freedv-dev/db/format b/freedv/branches/1.2/freedv-dev/db/format deleted file mode 100644 index db06890e..00000000 --- a/freedv/branches/1.2/freedv-dev/db/format +++ /dev/null @@ -1,2 +0,0 @@ -4 -layout sharded 1000 diff --git a/freedv/branches/1.2/freedv-dev/db/fs-type b/freedv/branches/1.2/freedv-dev/db/fs-type deleted file mode 100644 index 4fdd9531..00000000 --- a/freedv/branches/1.2/freedv-dev/db/fs-type +++ /dev/null @@ -1 +0,0 @@ -fsfs diff --git a/freedv/branches/1.2/freedv-dev/db/fsfs.conf b/freedv/branches/1.2/freedv-dev/db/fsfs.conf deleted file mode 100644 index cc08cebb..00000000 --- a/freedv/branches/1.2/freedv-dev/db/fsfs.conf +++ /dev/null @@ -1,38 +0,0 @@ -### This file controls the configuration of the FSFS filesystem. - -[memcached-servers] -### These options name memcached servers used to cache internal FSFS -### data. See http://www.danga.com/memcached/ for more information on -### memcached. To use memcached with FSFS, run one or more memcached -### servers, and specify each of them as an option like so: -# first-server = 127.0.0.1:11211 -# remote-memcached = mymemcached.corp.example.com:11212 -### The option name is ignored; the value is of the form HOST:PORT. -### memcached servers can be shared between multiple repositories; -### however, if you do this, you *must* ensure that repositories have -### distinct UUIDs and paths, or else cached data from one repository -### might be used by another accidentally. Note also that memcached has -### no authentication for reads or writes, so you must ensure that your -### memcached servers are only accessible by trusted users. - -[caches] -### When a cache-related error occurs, normally Subversion ignores it -### and continues, logging an error if the server is appropriately -### configured (and ignoring it with file:// access). To make -### Subversion never ignore cache errors, uncomment this line. -# fail-stop = true - -[rep-sharing] -### To conserve space, the filesystem can optionally avoid storing -### duplicate representations. This comes at a slight cost in -### performance, as maintaining a database of shared representations can -### increase commit times. The space savings are dependent upon the size -### of the repository, the number of objects it contains and the amount of -### duplication between them, usually a function of the branching and -### merging process. -### -### The following parameter enables rep-sharing in the repository. It can -### be switched on and off at will, but for best space-saving results -### should be enabled consistently over the life of the repository. -### rep-sharing is enabled by default. -# enable-rep-sharing = true diff --git a/freedv/branches/1.2/freedv-dev/db/min-unpacked-rev b/freedv/branches/1.2/freedv-dev/db/min-unpacked-rev deleted file mode 100644 index 573541ac..00000000 --- a/freedv/branches/1.2/freedv-dev/db/min-unpacked-rev +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/freedv/branches/1.2/freedv-dev/db/rep-cache.db b/freedv/branches/1.2/freedv-dev/db/rep-cache.db deleted file mode 100644 index 63c6f0b8a5181c3954b89f8a6fb505c09e81c4e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmWFz^vNtqRY=P(%1ta$FlJz3U}R))P*7lCU|#F<%m)i%A($C- zAIbAF|6yQa`p&@go%tjdHKRhKAwcgCX!K`f7nhf3Y|1T3Ov*_uN-c;_PE5`~FqoZ# zTpdGP6+#@Hd|Vaa@(LOX3JMvC#Tg1At`Q*$e*Qol>f@sj5aj9W7!;}C?HZ{AR8f># zmRX#cpQqsI7vk#f8U$AelFUy_D^4xJDpj0Wm5Nm&wW1&~FC{f49;*tVp_+zFY~rr+ zj0~ATWfjGRIlx>UpIBOw59Y_iJrHjQXM*xD3phi=ay7kUVbs3S5Eu=C0Sy6OknPB| j{D8V<)bh~~7!3h>h5#4HveEoc&mbSQYcvD~O$Y!0OWInE diff --git a/freedv/branches/1.2/freedv-dev/db/revprops/0/0 b/freedv/branches/1.2/freedv-dev/db/revprops/0/0 deleted file mode 100644 index d0b90dee..00000000 --- a/freedv/branches/1.2/freedv-dev/db/revprops/0/0 +++ /dev/null @@ -1,5 +0,0 @@ -K 8 -svn:date -V 27 -2012-08-21T18:27:59.389906Z -END diff --git a/freedv/branches/1.2/freedv-dev/db/revprops/0/1 b/freedv/branches/1.2/freedv-dev/db/revprops/0/1 deleted file mode 100644 index 0af71a2e..00000000 --- a/freedv/branches/1.2/freedv-dev/db/revprops/0/1 +++ /dev/null @@ -1,13 +0,0 @@ -K 10 -svn:author -V 9 -OFA-Staff -K 8 -svn:date -V 27 -2012-08-21T18:28:08.741468Z -K 7 -svn:log -V 25 -Imported folder structure -END diff --git a/freedv/branches/1.2/freedv-dev/db/revs/0/0 b/freedv/branches/1.2/freedv-dev/db/revs/0/0 deleted file mode 100644 index 10f5c45f..00000000 --- a/freedv/branches/1.2/freedv-dev/db/revs/0/0 +++ /dev/null @@ -1,11 +0,0 @@ -PLAIN -END -ENDREP -id: 0.0.r0/17 -type: dir -count: 0 -text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e -cpath: / - - -17 107 diff --git a/freedv/branches/1.2/freedv-dev/db/revs/0/1 b/freedv/branches/1.2/freedv-dev/db/revs/0/1 deleted file mode 100644 index fd802a9f..00000000 --- a/freedv/branches/1.2/freedv-dev/db/revs/0/1 +++ /dev/null @@ -1,49 +0,0 @@ -id: 3-1.0.r1/0 -type: dir -count: 0 -cpath: /tags -copyroot: 0 / - -id: 0-1.0.r1/62 -type: dir -count: 0 -cpath: /trunk -copyroot: 0 / - -id: 2-1.0.r1/126 -type: dir -count: 0 -cpath: /branches -copyroot: 0 / - -PLAIN -K 8 -branches -V 16 -dir 2-1.0.r1/126 -K 4 -tags -V 14 -dir 3-1.0.r1/0 -K 5 -trunk -V 15 -dir 0-1.0.r1/62 -END -ENDREP -id: 0.0.r1/306 -type: dir -pred: 0.0.r0/17 -count: 1 -text: 1 194 99 99 7b6cc14dddba4e09be5255b475d1a0a8 -cpath: / -copyroot: 0 / - -_0.0.t0-0 add-dir false false /trunk - -_2.0.t0-0 add-dir false false /branches - -_3.0.t0-0 add-dir false false /tags - - -306 431 diff --git a/freedv/branches/1.2/freedv-dev/db/transactions/.gitignore b/freedv/branches/1.2/freedv-dev/db/transactions/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/freedv-dev/db/txn-current b/freedv/branches/1.2/freedv-dev/db/txn-current deleted file mode 100644 index d00491fd..00000000 --- a/freedv/branches/1.2/freedv-dev/db/txn-current +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/freedv/branches/1.2/freedv-dev/db/txn-current-lock b/freedv/branches/1.2/freedv-dev/db/txn-current-lock deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/freedv-dev/db/txn-protorevs/.gitignore b/freedv/branches/1.2/freedv-dev/db/txn-protorevs/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/freedv-dev/db/uuid b/freedv/branches/1.2/freedv-dev/db/uuid deleted file mode 100644 index 0f362976..00000000 --- a/freedv/branches/1.2/freedv-dev/db/uuid +++ /dev/null @@ -1 +0,0 @@ -a56d66ce-6468-4744-9be7-52ce95ca47a4 diff --git a/freedv/branches/1.2/freedv-dev/db/write-lock b/freedv/branches/1.2/freedv-dev/db/write-lock deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/branches/1.2/freedv-dev/debian/changelog b/freedv/branches/1.2/freedv-dev/debian/changelog deleted file mode 100644 index ddfe80b5..00000000 --- a/freedv/branches/1.2/freedv-dev/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -freedv (1.0-150830) unstable; urgency=low - - * Subversion snapshot of tag 1.0. - - -- Stuart Longland Sun, 30 Aug 2015 09:01:13 +1000 diff --git a/freedv/branches/1.2/freedv-dev/debian/compat b/freedv/branches/1.2/freedv-dev/debian/compat deleted file mode 100644 index ec635144..00000000 --- a/freedv/branches/1.2/freedv-dev/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/freedv/branches/1.2/freedv-dev/debian/control b/freedv/branches/1.2/freedv-dev/debian/control deleted file mode 100644 index d1472b25..00000000 --- a/freedv/branches/1.2/freedv-dev/debian/control +++ /dev/null @@ -1,19 +0,0 @@ -Source: fdmdv2 -Section: main -Priority: optional -Maintainer: Stuart Longland -Build-Depends: debhelper (>= 9), cmake, libcodec2-dev, libgtk2.0-dev, - libhamlib-dev, libsamplerate-dev, libasound2-dev, libao-dev, libgsm1-dev, - portaudio19-dev, libsox-dev, libsndfile1-dev, libwxgtk3.0-dev -Standards-Version: 3.9.5 -Homepage: http://www.freedv.org -#Vcs-Git: git://anonscm.debian.org/collab-maint/freedv.git -#Vcs-Browser: http://anonscm.debian.org/?p=collab-maint/freedv.git;a=summary - -Package: freedv -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libcodec2 -Description: FreeDV: Open-Source Digital Voice modem - FreeDV is a digital voice modem that can transmit voice-quality - audio digitally over HF radio links in as little as 1.25kHz - bandwidth in varying conditions. diff --git a/freedv/branches/1.2/freedv-dev/debian/copyright b/freedv/branches/1.2/freedv-dev/debian/copyright deleted file mode 100644 index b55a293b..00000000 --- a/freedv/branches/1.2/freedv-dev/debian/copyright +++ /dev/null @@ -1,38 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: freedv -Source: - -Files: * -Copyright: - -License: - - - . - - -# If you want to use GPL v2 or later for the /debian/* files use -# the following clauses, or change it to suit. Delete these two lines -Files: debian/* -Copyright: 2015 unknown -License: GPL-2+ - This package 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 package 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, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". - -# Please also look if there are files or directories which have a -# different copyright/license attached and list them here. -# Please avoid to pick license terms that are more restrictive than the -# packaged work, as it may make Debian's contributions unacceptable upstream. diff --git a/freedv/branches/1.2/freedv-dev/debian/docs b/freedv/branches/1.2/freedv-dev/debian/docs deleted file mode 100644 index acfbcb33..00000000 --- a/freedv/branches/1.2/freedv-dev/debian/docs +++ /dev/null @@ -1,3 +0,0 @@ -credits.txt -README.txt -README.txt diff --git a/freedv/branches/1.2/freedv-dev/debian/format b/freedv/branches/1.2/freedv-dev/debian/format deleted file mode 100644 index 163aaf8d..00000000 --- a/freedv/branches/1.2/freedv-dev/debian/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/freedv/branches/1.2/freedv-dev/debian/rules b/freedv/branches/1.2/freedv-dev/debian/rules deleted file mode 100755 index ad892150..00000000 --- a/freedv/branches/1.2/freedv-dev/debian/rules +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/make -f -# See debhelper(7) (uncomment to enable) -# output every command that modifies files on the build system. -#DH_VERBOSE = 1 - -# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* -DPKG_EXPORT_BUILDFLAGS = 1 -include /usr/share/dpkg/default.mk - -# see FEATURE AREAS in dpkg-buildflags(1) -#export DEB_BUILD_MAINT_OPTIONS = hardening=+all - -# see ENVIRONMENT in dpkg-buildflags(1) -# package maintainers to append CFLAGS -#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic -# package maintainers to append LDFLAGS -#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed - - -# main packaging script based on dh7 syntax -%: - dh $@ - -# debmake generated override targets -# This is example for Cmake (See http://bugs.debian.org/641051 ) -override_dh_auto_configure: - dh_auto_configure -- \ - -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) \ - -DUSE_STATIC_CODEC2=FALSE \ - -DUSE_STATIC_SPEEXDSP=FALSE diff --git a/freedv/branches/1.2/freedv-dev/script/spot.sh b/freedv/branches/1.2/freedv-dev/script/spot.sh deleted file mode 100644 index cb1309a2..00000000 --- a/freedv/branches/1.2/freedv-dev/script/spot.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# -# spot.sh -# David Rowe Sep 2015 -# - -# Demo script for "spotting" based on FreeDV txt string. Posts a -# date-stamped text file to a web server. Called from FreeDV GUI -# program when a callsign is received in the txt msg. - - -# Q: how to remove repeated spots, or those close in time? -# -# Set up automated lftp login: -# -# $ lftp ftp://username@server -# Password: -# lftp username@server:~> set bmk:save-passwords true -# lftp username@server:~> bookmark add yourserver -# lftp username@server:~> bookmark list -# lftp username@server:~> quit - -SPOTFILE=/home/david/tmp/freedvspot.html -FTPSERVER=ftp.rowetel.com - -echo `date -u` " " $1 "
" >> $SPOTFILE -tail -n 25 $SPOTFILE > /tmp/spot.tmp1 -mv /tmp/spot.tmp1 $SPOTFILE -lftp -e "cd www;put $SPOTFILE;quit" $FTPSERVER diff --git a/freedv/branches/1.2/freedv-dev/src/CMakeLists.txt b/freedv/branches/1.2/freedv-dev/src/CMakeLists.txt deleted file mode 100644 index ef0798b3..00000000 --- a/freedv/branches/1.2/freedv-dev/src/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -set(FREEDV_SOURCES - dlg_audiooptions.cpp - dlg_filter.cpp - dlg_options.cpp - dlg_ptt.cpp - dlg_plugin.cpp - fdmdv2_main.cpp - fdmdv2_pa_wrapper.cpp - fdmdv2_plot.cpp - fdmdv2_plot_scalar.cpp - fdmdv2_plot_scatter.cpp - fdmdv2_plot_spectrum.cpp - fdmdv2_plot_waterfall.cpp - hamlib.cpp - serialport.cpp - topFrame.cpp - sox_biquad.c - comp.h - dlg_audiooptions.h - dlg_filter.h - dlg_options.h - dlg_ptt.h - fdmdv2_defines.h - fdmdv2_main.h - fdmdv2_pa_wrapper.h - fdmdv2_plot.h - fdmdv2_plot_scalar.h - fdmdv2_plot_scatter.h - fdmdv2_plot_spectrum.h - fdmdv2_plot_waterfall.h - hamlib.h - sox_biquad.h - sox/band.h - sox/biquad.c - sox/biquads.c - sox/biquad.h - sox/effects.c - sox/effects.h - sox/effects_i.c - sox/formats_i.c - sox/libsox.c - sox/sox.h - sox/sox_i.h - sox/soxomp.h - sox/util.h - sox/xmalloc.h - sox/xmalloc.c - topFrame.h - version.h -) - -# WIN32 is needed for Windows GUI apps and is ignored for UNIX like systems. -add_executable(freedv WIN32 ${FREEDV_SOURCES} ${RES_FILES}) -target_link_libraries(freedv ${FREEDV_LINK_LIBS}) -include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) -if(FREEDV_STATIC_DEPS) - add_dependencies(freedv ${FREEDV_STATIC_DEPS}) -endif(FREEDV_STATIC_DEPS) -install(TARGETS freedv - RUNTIME DESTINATION bin) - -# Custom commands to build OSX images. -if(APPLE) - add_custom_command( - TARGET freedv - POST_BUILD - COMMAND mkdir ARGS -p FreeDV.app/Contents/MacOS - COMMAND mkdir ARGS -p FreeDV.app/Contents/Resources/English.lproj - COMMAND cp ARGS ${CMAKE_CURRENT_SOURCE_DIR}/info.plist FreeDV.app/Contents - COMMAND cp ARGS ${CMAKE_CURRENT_SOURCE_DIR}/freedv.icns FreeDV.app/Contents/Resources - COMMAND echo ARGS -n "APPL????" > FreeDV.app/Contents/PkgInfo - COMMAND cp ARGS freedv FreeDV.app/Contents/MacOS/FreeDV - COMMAND dylibbundler ARGS -od -b -x FreeDV.app/Contents/MacOS/FreeDV -d FreeDV.app/Contents/libs -p @executable_path/../libs/ - COMMAND mkdir dist_tmp - COMMAND cp -r FreeDV.app dist_tmp - COMMAND hdiutil create -srcfolder dist_tmp/ -volname FreeDV -format UDZO ./FreeDV.dmg - COMMAND rm -rf dist_tmp - ) -endif(APPLE) diff --git a/freedv/branches/1.2/freedv-dev/src/Makefile.win32 b/freedv/branches/1.2/freedv-dev/src/Makefile.win32 deleted file mode 100644 index 932e8518..00000000 --- a/freedv/branches/1.2/freedv-dev/src/Makefile.win32 +++ /dev/null @@ -1,52 +0,0 @@ -# src/Makefile.win32 -# David Rowe 26 Oct 2012 -# -# Makefile for Win32 on msys/Mingw to help David R get up to speed -# -# $ make -f Makefile.Win32 - -CODEC2_PATH=$(HOME)/codec2-dev -INCLUDE_PATH=/usr/local/include - -WX_CONFIG=wx-config -WX_CPPFLAGS = $(shell $(WX_CONFIG) --cxxflags) -D__WXDEBUG__ -WX_LIBS = $(shell $(WX_CONFIG) --libs core, base, aui, adv, net) -SVN_REVISION=$(shell svnversion) -CODEC2_INC=$(CODEC2_PATH)/src -CODEC2_LIB=$(CODEC2_PATH)/build_win32/src/ - -CPP_FLAGS = -D_NO_AUTOTOOLS_ -I$(INCLUDE_PATH) $(WX_CPPFLAGS) -I$(CODEC2_INC) -I../extern/include -I. -g -Wall -DSVN_REVISION=\"$(SVN_REVISION)\" -LIBS = $(WX_LIBS) -L$(CODEC2_LIB) -lcodec2 -lm -lportaudiocpp -lportaudio -lpthread -lsndfile -lsamplerate -lhamlib -lsox -lspeexdsp - -OBJS = topFrame.o \ -fdmdv2_main.o \ -fdmdv2_plot.o \ -fdmdv2_plot_scalar.o \ -fdmdv2_plot_scatter.o \ -fdmdv2_plot_spectrum.o \ -fdmdv2_plot_waterfall.o \ -fdmdv2_pa_wrapper.o \ -dlg_audiooptions.o \ -dlg_ptt.o \ -dlg_options.o \ -dlg_filter.o \ -sox_biquad.o \ -hamlib.o \ -../../codec2-dev/src/golay23.o - -HDRS = version.h dlg_audiooptions.h dlg_ptt.h dlg_filter.h fdmdv2_main.h fdmdv2_defines.h fdmdv2_plot.h fdmdv2_plot_scalar.h fdmdv2_plot_waterfall.h fdmdv2_plot_scatter.h fdmdv2_plot_spectrum.h fdmdv2_pa_wrapper.h topFrame.h dlg_audiooptions.h topFrame.h varicode.h ../../codec2-dev/src/golay23.h hamlib.h - -all: freedv - -freedv: $(OBJS) - g++ -o freedv $(OBJS) $(CPP_FLAGS) $(LIBS) - -%.o: %.cpp $(HDRS) Makefile.win32 - g++ $(CPP_FLAGS) -c $< -o $@ - -%.o: %.c $(HDRS) Makefile.win32 - gcc $(CPP_FLAGS) -c $< -o $@ - -clean: - rm -f *.o fdmdv2 - diff --git a/freedv/branches/1.2/freedv-dev/src/afreedvplugin.c b/freedv/branches/1.2/freedv-dev/src/afreedvplugin.c deleted file mode 100644 index 5fdc424e..00000000 --- a/freedv/branches/1.2/freedv-dev/src/afreedvplugin.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - afreedvplugin.c - David Rowe Feb 2016 - - Sample FreeDV plugin - - TODO: - [ ] plugin to call back to functions - [ ] ability to list .so's/DLLs and scan - [ ] where do we put plugins? - [ ] Windows build and test environment - - linux .so: - $ gcc -Wall -fPIC -c afreedvplugin.c - $ gcc -shared -Wl,-soname,afreedvplugin.so -o afreedvplugin.so afreedvplugin.o - win32 .dll: - $ i686-w64-mingw32-gcc -c afreedvplugin.c - $ i686-w64-mingw32-gcc -shared -o afreedvplugin.dll afreedvplugin.o -Wl,--out-implib,afreedvplugin_dll.a - -*/ - -#include -#include -#include - -#ifdef _WIN32_ -#define DLL __declspec(dllexport) -#else -#define DLL -#endif - - -#ifdef LATER -/* functions plugin can call - not sure how to link to these */ - -int plugin_alert(char string[]); -int plugin_get_persistant(char name[], char value[]); -int plugin_set_persistant(char name[], char value[]); -#endif -static int (*plugin_get_persistant)(char name[], char value[]); - -struct APLUGIN_STATES { - int symbol_rate; - int num_tones; - int counter; -}; - -/* plugin functions called by host, we need to write these */ - -void DLL plugin_name(char name[]) { - - sprintf(name, "aFreeDVplugIn"); -} - -/* - Text fields will be created for nparams, using the names - in *param_names[]. These fields we be saved to persistent - storage as name/param_names[0], name/param_names[1] .... -*/ - -void DLL *plugin_open(char *param_names[], - int *nparams, - int (*aplugin_get_persistant)(char *, char *)) -{ - struct APLUGIN_STATES *states; - - /* set up function ptrs */ - - plugin_get_persistant = aplugin_get_persistant; - - /* tell host how many persistent parameters we have and their names */ - - strcpy(param_names[0], "SymbolRate"); - strcpy(param_names[1], "NumTones"); - *nparams = 2; - - /* init local states */ - - states = (struct APLUGIN_STATES *)malloc(sizeof(struct APLUGIN_STATES)); - if (states == NULL) { - // TODO: plugin_alert("Problem starting plugin!"); - return NULL; - } - states->counter = 0; - - return (void*)states; -} - -void DLL plugin_close(void *states) { - free(states); -} - -void DLL plugin_start(void *s) { - struct APLUGIN_STATES *states = (struct APLUGIN_STATES*)s; - char txt[80]; - - fprintf(stderr, "\nplugin_start\n"); - - (plugin_get_persistant)("SymbolRate",txt); - states->symbol_rate = atoi(txt); - - (plugin_get_persistant)("NumTones",txt); - states->num_tones = atoi(txt); - - fprintf(stderr, "symbol_rate: %d num_tones: %d\n", states->symbol_rate, states->num_tones); -} - -void DLL plugin_stop(void *states) { - fprintf(stderr, "\nplugin_stop\n"); -} - -void DLL plugin_rx_samples(void *s, short samples[], int n) { - struct APLUGIN_STATES *states = (struct APLUGIN_STATES*)s; - //fprintf(stderr, "Got n=%d samples!\n", n); - //fprintf(stderr, "samples[0] = %d samples[%d-1] = %d counter = %d\n", samples[0], n, samples[n-1], states->counter++); -} - diff --git a/freedv/branches/1.2/freedv-dev/src/comp.h b/freedv/branches/1.2/freedv-dev/src/comp.h deleted file mode 100644 index a3a1bd9b..00000000 --- a/freedv/branches/1.2/freedv-dev/src/comp.h +++ /dev/null @@ -1,39 +0,0 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: comp.h - AUTHOR......: David Rowe - DATE CREATED: 24/08/09 - - Complex number definition. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2009 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 . -*/ - -#ifndef __COMP__ -#define __COMP__ - -/* Complex number */ - -typedef struct -{ - float real; - float imag; -} COMP; - -#endif diff --git a/freedv/branches/1.2/freedv-dev/src/dlg_audiooptions.cpp b/freedv/branches/1.2/freedv-dev/src/dlg_audiooptions.cpp deleted file mode 100644 index 4e8cd981..00000000 --- a/freedv/branches/1.2/freedv-dev/src/dlg_audiooptions.cpp +++ /dev/null @@ -1,1264 +0,0 @@ -//========================================================================= -// Name: AudioOptsDialog.cpp -// Purpose: Implements an Audio options selection dialog. -// -// Authors: David Rowe, David Witten -// License: -// -// All rights reserved. -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================= -#include "fdmdv2_main.h" -#include "dlg_audiooptions.h" - -// constants for test waveform plots - -#define TEST_WAVEFORM_X 180 -#define TEST_WAVEFORM_Y 180 -#define TEST_WAVEFORM_PLOT_TIME 2.0 -#define TEST_WAVEFORM_PLOT_FS 400 -#define TEST_BUF_SIZE 1024 -#define TEST_FS 48000.0 -#define TEST_DT 0.1 // time between plot updates in seconds -#define TEST_WAVEFORM_PLOT_BUF ((int)(DT*400)) - -void AudioOptsDialog::Pa_Init(void) -{ - m_isPaInitialized = false; - - if((pa_err = Pa_Initialize()) == paNoError) - { - m_isPaInitialized = true; - } - else - { - wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK); - return; - } -} - - -void AudioOptsDialog::buildTestControls(PlotScalar **plotScalar, wxButton **btnTest, - wxPanel *parentPanel, wxBoxSizer *bSizer, wxString buttonLabel) -{ - wxBoxSizer* bSizer1 = new wxBoxSizer(wxVERTICAL); - - wxPanel *panel = new wxPanel(parentPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); - *plotScalar = new PlotScalar((wxFrame*) panel, 1, TEST_WAVEFORM_PLOT_TIME, 1.0/TEST_WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "", 1); - (*plotScalar)->SetClientSize(wxSize(TEST_WAVEFORM_X,TEST_WAVEFORM_Y)); - bSizer1->Add(panel, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 8); - - *btnTest = new wxButton(parentPanel, wxID_ANY, buttonLabel, wxDefaultPosition, wxDefaultSize); - bSizer1->Add(*btnTest, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 0); - - bSizer->Add(bSizer1, 0, wxALIGN_CENTER_HORIZONTAL |wxALIGN_CENTER_VERTICAL ); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// AudioOptsDialog() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -AudioOptsDialog::AudioOptsDialog(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - //this->SetSizeHints(wxSize(850, 600), wxDefaultSize); - fprintf(stderr, "pos %d %d\n", pos.x, pos.y); - Pa_Init(); - - wxBoxSizer* mainSizer; - mainSizer = new wxBoxSizer(wxVERTICAL); - m_panel1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer4; - bSizer4 = new wxBoxSizer(wxVERTICAL); - m_notebook1 = new wxNotebook(m_panel1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM); - m_panelRx = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer20; - bSizer20 = new wxBoxSizer(wxVERTICAL); - wxGridSizer* gSizer4; - gSizer4 = new wxGridSizer(2, 1, 0, 0); - - // Rx In ----------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer2; - sbSizer2 = new wxStaticBoxSizer(new wxStaticBox(m_panelRx, wxID_ANY, _("From Radio")), wxHORIZONTAL); - - wxBoxSizer* bSizer811a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlRxInDevices = new wxListCtrl(m_panelRx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer811a->Add(m_listCtrlRxInDevices, 1, wxALL|wxEXPAND, 1); - - wxBoxSizer* bSizer811; - bSizer811 = new wxBoxSizer(wxHORIZONTAL); - m_staticText51 = new wxStaticText(m_panelRx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText51->Wrap(-1); - bSizer811->Add(m_staticText51, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_textCtrlRxIn = new wxTextCtrl(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer811->Add(m_textCtrlRxIn, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText6 = new wxStaticText(m_panelRx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText6->Wrap(-1); - bSizer811->Add(m_staticText6, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateRxIn = new wxComboBox(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer811->Add(m_cbSampleRateRxIn, 0, wxALIGN_CENTER_VERTICAL|wxALL, 1); - - bSizer811a->Add(bSizer811, 0, wxEXPAND, 5); - - sbSizer2->Add(bSizer811a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarRxIn, &m_btnRxInTest, m_panelRx, sbSizer2, _("Rec 2s")); - - gSizer4->Add(sbSizer2, 1, wxEXPAND, 5); - - // Rx Out ----------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer3; - sbSizer3 = new wxStaticBoxSizer(new wxStaticBox(m_panelRx, wxID_ANY, _("To Speaker/Headphones")), wxHORIZONTAL); - - wxBoxSizer* bSizer81a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlRxOutDevices = new wxListCtrl(m_panelRx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer81a->Add(m_listCtrlRxOutDevices, 1, wxALL|wxEXPAND, 1); - - wxBoxSizer* bSizer81; - bSizer81 = new wxBoxSizer(wxHORIZONTAL); - m_staticText9 = new wxStaticText(m_panelRx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText9->Wrap(-1); - bSizer81->Add(m_staticText9, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_textCtrlRxOut = new wxTextCtrl(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer81->Add(m_textCtrlRxOut, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText10 = new wxStaticText(m_panelRx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText10->Wrap(-1); - bSizer81->Add(m_staticText10, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateRxOut = new wxComboBox(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer81->Add(m_cbSampleRateRxOut, 0, wxALIGN_CENTER_VERTICAL|wxALL, 1); - - bSizer81a->Add(bSizer81, 0, wxEXPAND, 5); - - sbSizer3->Add(bSizer81a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarRxOut, &m_btnRxOutTest, m_panelRx, sbSizer3, _("Play 2s")); - - gSizer4->Add(sbSizer3, 1, wxEXPAND, 2); - bSizer20->Add(gSizer4, 1, wxEXPAND, 1); - m_panelRx->SetSizer(bSizer20); - m_panelRx->Layout(); - bSizer20->Fit(m_panelRx); - m_notebook1->AddPage(m_panelRx, _("Receive"), true); - - // Tx Tab ------------------------------------------------------------------------------- - - m_panelTx = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer18; - bSizer18 = new wxBoxSizer(wxVERTICAL); - wxGridSizer* gSizer2; - gSizer2 = new wxGridSizer(2, 1, 0, 0); - - // Tx In ---------------------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer22; - sbSizer22 = new wxStaticBoxSizer(new wxStaticBox(m_panelTx, wxID_ANY, _("From Microphone")), wxHORIZONTAL); - - wxBoxSizer* bSizer83a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlTxInDevices = new wxListCtrl(m_panelTx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer83a->Add(m_listCtrlTxInDevices, 1, wxALL|wxEXPAND, 1); - wxBoxSizer* bSizer83; - bSizer83 = new wxBoxSizer(wxHORIZONTAL); - m_staticText12 = new wxStaticText(m_panelTx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText12->Wrap(-1); - bSizer83->Add(m_staticText12, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_textCtrlTxIn = new wxTextCtrl(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer83->Add(m_textCtrlTxIn, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText11 = new wxStaticText(m_panelTx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText11->Wrap(-1); - bSizer83->Add(m_staticText11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateTxIn = new wxComboBox(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer83->Add(m_cbSampleRateTxIn, 0, wxALL, 1); - - bSizer83a->Add(bSizer83, 0, wxEXPAND, 5); - - sbSizer22->Add(bSizer83a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarTxIn, &m_btnTxInTest, m_panelTx, sbSizer22, _("Rec 2s")); - - gSizer2->Add(sbSizer22, 1, wxEXPAND, 5); - - // Tx Out ---------------------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer21; - sbSizer21 = new wxStaticBoxSizer(new wxStaticBox(m_panelTx, wxID_ANY, _("To Radio")), wxHORIZONTAL); - - wxBoxSizer* bSizer82a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlTxOutDevices = new wxListCtrl(m_panelTx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer82a->Add(m_listCtrlTxOutDevices, 1, wxALL|wxEXPAND, 2); - wxBoxSizer* bSizer82; - bSizer82 = new wxBoxSizer(wxHORIZONTAL); - m_staticText81 = new wxStaticText(m_panelTx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText81->Wrap(-1); - bSizer82->Add(m_staticText81, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_textCtrlTxOut = new wxTextCtrl(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer82->Add(m_textCtrlTxOut, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText71 = new wxStaticText(m_panelTx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText71->Wrap(-1); - bSizer82->Add(m_staticText71, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateTxOut = new wxComboBox(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer82->Add(m_cbSampleRateTxOut, 0, wxALL, 1); - - bSizer82a->Add(bSizer82, 0, wxEXPAND, 5); - - sbSizer21->Add(bSizer82a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarTxOut, &m_btnTxOutTest, m_panelTx, sbSizer21, _("Play 2s")); - - gSizer2->Add(sbSizer21, 1, wxEXPAND, 5); - bSizer18->Add(gSizer2, 1, wxEXPAND, 1); - m_panelTx->SetSizer(bSizer18); - m_panelTx->Layout(); - bSizer18->Fit(m_panelTx); - m_notebook1->AddPage(m_panelTx, _("Transmit"), false); - - // API Tab ------------------------------------------------------------------- - - m_panelAPI = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer12; - bSizer12 = new wxBoxSizer(wxHORIZONTAL); - wxGridSizer* gSizer31; - gSizer31 = new wxGridSizer(2, 1, 0, 0); - wxStaticBoxSizer* sbSizer1; - sbSizer1 = new wxStaticBoxSizer(new wxStaticBox(m_panelAPI, wxID_ANY, _("PortAudio")), wxVERTICAL); - - wxGridSizer* gSizer3; - gSizer3 = new wxGridSizer(4, 2, 0, 0); - - m_staticText7 = new wxStaticText(m_panelAPI, wxID_ANY, _("PortAudio Version String:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText7->Wrap(-1); - gSizer3->Add(m_staticText7, 1, wxALIGN_RIGHT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - m_textStringVer = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - gSizer3->Add(m_textStringVer, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText8 = new wxStaticText(m_panelAPI, wxID_ANY, _("PortAudio Int Version:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText8->Wrap(-1); - gSizer3->Add(m_staticText8, 1, wxALIGN_RIGHT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - m_textIntVer = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - gSizer3->Add(m_textIntVer, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText5 = new wxStaticText(m_panelAPI, wxID_ANY, _("Device Count:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText5->Wrap(-1); - gSizer3->Add(m_staticText5, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 10); - m_textCDevCount = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - gSizer3->Add(m_textCDevCount, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText4 = new wxStaticText(m_panelAPI, wxID_ANY, _("API Count:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText4->Wrap(-1); - gSizer3->Add(m_staticText4, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 10); - m_textAPICount = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - m_textAPICount->SetMaxSize(wxSize(45,-1)); - gSizer3->Add(m_textAPICount, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - sbSizer1->Add(gSizer3, 1, wxEXPAND, 2); - gSizer31->Add(sbSizer1, 1, wxEXPAND, 2); - wxStaticBoxSizer* sbSizer6; - sbSizer6 = new wxStaticBoxSizer(new wxStaticBox(m_panelAPI, wxID_ANY, _("Other")), wxVERTICAL); - gSizer31->Add(sbSizer6, 1, wxEXPAND, 5); - bSizer12->Add(gSizer31, 1, wxEXPAND, 5); - m_panelAPI->SetSizer(bSizer12); - m_panelAPI->Layout(); - bSizer12->Fit(m_panelAPI); - m_notebook1->AddPage(m_panelAPI, _("API Info"), false); - bSizer4->Add(m_notebook1, 1, wxEXPAND | wxALL, 0); - m_panel1->SetSizer(bSizer4); - m_panel1->Layout(); - bSizer4->Fit(m_panel1); - mainSizer->Add(m_panel1, 1, wxEXPAND | wxALL, 1); - - wxBoxSizer* bSizer6; - bSizer6 = new wxBoxSizer(wxHORIZONTAL); - m_btnRefresh = new wxButton(this, wxID_ANY, _("Refresh"), wxDefaultPosition, wxDefaultSize, 0); - bSizer6->Add(m_btnRefresh, 0, wxALIGN_CENTER|wxALL, 2); - - m_sdbSizer1 = new wxStdDialogButtonSizer(); - - m_sdbSizer1OK = new wxButton(this, wxID_OK); - m_sdbSizer1->AddButton(m_sdbSizer1OK); - - m_sdbSizer1Cancel = new wxButton(this, wxID_CANCEL); - m_sdbSizer1->AddButton(m_sdbSizer1Cancel); - - m_sdbSizer1Apply = new wxButton(this, wxID_APPLY); - m_sdbSizer1->AddButton(m_sdbSizer1Apply); - - m_sdbSizer1->Realize(); - - bSizer6->Add(m_sdbSizer1, 1, wxALIGN_CENTER_VERTICAL, 2); - mainSizer->Add(bSizer6, 0, wxEXPAND, 2); - this->SetSizer(mainSizer); - this->Layout(); - this->Centre(wxBOTH); -// this->Centre(wxBOTH); - - m_notebook1->SetSelection(0); - - showAPIInfo(); - m_RxInDevices.m_listDevices = m_listCtrlRxInDevices; - m_RxInDevices.direction = AUDIO_IN; - m_RxInDevices.m_textDevice = m_textCtrlRxIn; - m_RxInDevices.m_cbSampleRate = m_cbSampleRateRxIn; - - m_RxOutDevices.m_listDevices = m_listCtrlRxOutDevices; - m_RxOutDevices.direction = AUDIO_OUT; - m_RxOutDevices.m_textDevice = m_textCtrlRxOut; - m_RxOutDevices.m_cbSampleRate = m_cbSampleRateRxOut; - - m_TxInDevices.m_listDevices = m_listCtrlTxInDevices; - m_TxInDevices.direction = AUDIO_IN; - m_TxInDevices.m_textDevice = m_textCtrlTxIn; - m_TxInDevices.m_cbSampleRate = m_cbSampleRateTxIn; - - m_TxOutDevices.m_listDevices = m_listCtrlTxOutDevices; - m_TxOutDevices.direction = AUDIO_OUT; - m_TxOutDevices.m_textDevice = m_textCtrlTxOut; - m_TxOutDevices.m_cbSampleRate = m_cbSampleRateTxOut; - - populateParams(m_RxInDevices); - populateParams(m_RxOutDevices); - populateParams(m_TxInDevices); - populateParams(m_TxOutDevices); - - m_listCtrlRxInDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnRxInDeviceSelect ), NULL, this ); - m_listCtrlRxOutDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnRxOutDeviceSelect ), NULL, this ); - m_listCtrlTxInDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnTxInDeviceSelect ), NULL, this ); - m_listCtrlTxOutDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnTxOutDeviceSelect ), NULL, this ); - - // wire up test buttons - m_btnRxInTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxInTest ), NULL, this ); - m_btnRxOutTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxOutTest ), NULL, this ); - m_btnTxInTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxInTest ), NULL, this ); - m_btnTxOutTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxOutTest ), NULL, this ); - - m_btnRefresh->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRefreshClick ), NULL, this ); - m_sdbSizer1Apply->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnApplyAudioParameters ), NULL, this ); - m_sdbSizer1Cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnCancelAudioParameters ), NULL, this ); - m_sdbSizer1OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnOkAudioParameters ), NULL, this ); -/* - void OnClose( wxCloseEvent& event ) { event.Skip(); } - void OnHibernate( wxActivateEvent& event ) { event.Skip(); } - void OnIconize( wxIconizeEvent& event ) { event.Skip(); } - void OnInitDialog( wxInitDialogEvent& event ) { event.Skip(); } -*/ -// this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(AudioOptsDialog::OnClose)); - this->Connect(wxEVT_HIBERNATE, wxActivateEventHandler(AudioOptsDialog::OnHibernate)); - this->Connect(wxEVT_ICONIZE, wxIconizeEventHandler(AudioOptsDialog::OnIconize)); - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(AudioOptsDialog::OnInitDialog)); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// ~AudioOptsDialog() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -AudioOptsDialog::~AudioOptsDialog() -{ - Pa_Terminate(); - - // Disconnect Events - this->Disconnect(wxEVT_HIBERNATE, wxActivateEventHandler(AudioOptsDialog::OnHibernate)); - this->Disconnect(wxEVT_ICONIZE, wxIconizeEventHandler(AudioOptsDialog::OnIconize)); - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(AudioOptsDialog::OnInitDialog)); - - m_listCtrlRxInDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnRxInDeviceSelect), NULL, this); - m_listCtrlRxOutDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnRxOutDeviceSelect), NULL, this); - m_listCtrlTxInDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnTxInDeviceSelect), NULL, this); - m_listCtrlTxOutDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnTxOutDeviceSelect), NULL, this); - - m_btnRxInTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxInTest ), NULL, this ); - m_btnRxOutTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxOutTest ), NULL, this ); - m_btnTxInTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxInTest ), NULL, this ); - m_btnTxOutTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxOutTest ), NULL, this ); - - m_btnRefresh->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnRefreshClick), NULL, this); - m_sdbSizer1Apply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnApplyAudioParameters), NULL, this); - m_sdbSizer1Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnCancelAudioParameters), NULL, this); - m_sdbSizer1OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnOkAudioParameters), NULL, this); - -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnInitDialog( wxInitDialogEvent& event ) -{ - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -// -// helper function to look up name of devNum, and if it exists write -// name to textCtrl. Used to trap dissapearing devices. -//------------------------------------------------------------------------- -int AudioOptsDialog::setTextCtrlIfDevNumValid(wxTextCtrl *textCtrl, wxListCtrl *listCtrl, int devNum) -{ - int i, aDevNum, found_devNum; - - // ignore last list entry as it is the "none" entry - - found_devNum = 0; - for(i=0; iGetItemCount()-1; i++) { - aDevNum = wxAtoi(listCtrl->GetItemText(i, 1)); - //printf("aDevNum: %d devNum: %d\n", aDevNum, devNum); - if (aDevNum == devNum) { - found_devNum = 1; - textCtrl->SetValue(listCtrl->GetItemText(i, 0) + " (" + wxString::Format(wxT("%i"),devNum) + ")"); - printf("setting focus of %d\n", i); - listCtrl->SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED); - } - } - - if (found_devNum) - return devNum; - else { - textCtrl->SetValue("none"); - return -1; - } -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -int AudioOptsDialog::ExchangeData(int inout) -{ - if(inout == EXCHANGE_DATA_IN) - { - // Map sound card device numbers to tx/rx device numbers depending - // on number of sound cards in use - - printf("EXCHANGE_DATA_IN:\n"); - printf(" g_nSoundCards: %d\n", g_nSoundCards); - printf(" g_soundCard1InDeviceNum: %d\n", g_soundCard1InDeviceNum); - printf(" g_soundCard1OutDeviceNum: %d\n", g_soundCard1OutDeviceNum); - printf(" g_soundCard1SampleRate: %d\n", g_soundCard1SampleRate); - printf(" g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - printf(" g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - printf(" g_soundCard2SampleRate: %d\n", g_soundCard2SampleRate); - - if (g_nSoundCards == 0) { - m_textCtrlRxIn ->SetValue("none"); rxInAudioDeviceNum = -1; - m_textCtrlRxOut->SetValue("none"); rxOutAudioDeviceNum = -1; - m_textCtrlTxIn ->SetValue("none"); txInAudioDeviceNum = -1; - m_textCtrlTxOut->SetValue("none"); txOutAudioDeviceNum = -1; - } - - if (g_nSoundCards == 1) { - rxInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxIn, - m_listCtrlRxInDevices, - g_soundCard1InDeviceNum); - - rxOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxOut, - m_listCtrlRxOutDevices, - g_soundCard1OutDeviceNum); - - if ((rxInAudioDeviceNum != -1) && (rxInAudioDeviceNum != -1)) { - m_cbSampleRateRxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - m_cbSampleRateRxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - } - - m_textCtrlTxIn ->SetValue("none"); txInAudioDeviceNum = -1; - m_textCtrlTxOut->SetValue("none"); txOutAudioDeviceNum = -1; - } - - if (g_nSoundCards == 2) { - - rxInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxIn, - m_listCtrlRxInDevices, - g_soundCard1InDeviceNum); - - rxOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxOut, - m_listCtrlRxOutDevices, - g_soundCard2OutDeviceNum); - - txInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlTxIn, - m_listCtrlTxInDevices, - g_soundCard2InDeviceNum); - - txOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlTxOut, - m_listCtrlTxOutDevices, - g_soundCard1OutDeviceNum); - - if ((rxInAudioDeviceNum != -1) && (txOutAudioDeviceNum != -1)) { - m_cbSampleRateRxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - m_cbSampleRateTxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - } - - if ((txInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1)) { - m_cbSampleRateTxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard2SampleRate)); - m_cbSampleRateRxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard2SampleRate)); - } - } - printf(" rxInAudioDeviceNum: %d\n rxOutAudioDeviceNum: %d\n txInAudioDeviceNum: %d\n txOutAudioDeviceNum: %d\n", - rxInAudioDeviceNum, rxOutAudioDeviceNum, txInAudioDeviceNum, txOutAudioDeviceNum); - } - - if(inout == EXCHANGE_DATA_OUT) - { - int valid_one_card_config = 0; - int valid_two_card_config = 0; - wxString sampleRate1, sampleRate2; - - printf("EXCHANGE_DATA_OUT:\n"); - printf(" rxInAudioDeviceNum: %d\n rxOutAudioDeviceNum: %d\n txInAudioDeviceNum: %d\n txOutAudioDeviceNum: %d\n", - rxInAudioDeviceNum, rxOutAudioDeviceNum, txInAudioDeviceNum, txOutAudioDeviceNum); - - // --------------------------------------------------------------- - // check we have a valid 1 or 2 sound card configuration - // --------------------------------------------------------------- - - // one sound card config, tx device numbers should be set to -1 - - if ((rxInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1) && - (txInAudioDeviceNum == -1) && (txOutAudioDeviceNum == -1)) { - - valid_one_card_config = 1; - - // in and out sample rate must be the same, as there is one callback - - sampleRate1 = m_cbSampleRateRxIn->GetValue(); - if (!sampleRate1.IsSameAs(m_cbSampleRateRxOut->GetValue())) { - wxMessageBox(wxT("With a single sound card the Sample Rate of " - "From Radio and To Speaker/Headphones must be the same."), wxT(""), wxOK); - return -1; - } - } - - // two card configuration - - if ((rxInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1) && - (txInAudioDeviceNum != -1) && (txOutAudioDeviceNum != -1)) { - - valid_two_card_config = 1; - - // Check we haven't doubled up on sound devices - - if (rxInAudioDeviceNum == txInAudioDeviceNum) { - wxMessageBox(wxT("You must use different devices for From Radio and From Microphone"), wxT(""), wxOK); - return -1; - } - - if (rxOutAudioDeviceNum == txOutAudioDeviceNum) { - wxMessageBox(wxT("You must use different devices for To Radio and To Speaker/Headphones"), wxT(""), wxOK); - return -1; - } - - // Check sample rates for callback 1 devices are the same, - // as input and output are handled synchronously by one - // portaudio callback - - sampleRate1 = m_cbSampleRateRxIn->GetValue(); - if (!sampleRate1.IsSameAs(m_cbSampleRateTxOut->GetValue())) { - wxMessageBox(wxT("With two sound cards the Sample Rate " - "of From Radio and To Radio must be the same."), wxT(""), wxOK); - return -1; - } - - // check sample rate for callback 2 devices is the same - - sampleRate2 = m_cbSampleRateTxIn->GetValue(); - if (!sampleRate2.IsSameAs(m_cbSampleRateRxOut->GetValue())) { - wxMessageBox(wxT("With two sound cards the Sample Rate of " - "From Microphone and To Speaker/Headphones must be the same."), wxT(""), wxOK); - return -1; - } - - } - - printf(" valid_one_card_config: %d valid_two_card_config: %d\n", valid_one_card_config, valid_two_card_config); - - if (!valid_one_card_config && !valid_two_card_config) { - wxMessageBox(wxT("Invalid one or two sound card configuration"), wxT(""), wxOK); - return -1; - } - - // --------------------------------------------------------------- - // Map Rx/TX device numbers to sound card device numbers used - // in callbacks. Portaudio uses one callback per sound card so - // we have to be soundcard oriented at run time rather than - // Tx/Rx oriented as in this dialog. - // --------------------------------------------------------------- - g_nSoundCards = 0; - g_soundCard1InDeviceNum = g_soundCard1OutDeviceNum = g_soundCard2InDeviceNum = g_soundCard2OutDeviceNum = -1; - - if (valid_one_card_config) { - - // Only callback 1 used - - g_nSoundCards = 1; - g_soundCard1InDeviceNum = rxInAudioDeviceNum; - g_soundCard1OutDeviceNum = rxOutAudioDeviceNum; - g_soundCard1SampleRate = wxAtoi(sampleRate1); - } - - if (valid_two_card_config) { - g_nSoundCards = 2; - g_soundCard1InDeviceNum = rxInAudioDeviceNum; - g_soundCard1OutDeviceNum = txOutAudioDeviceNum; - g_soundCard1SampleRate = wxAtoi(sampleRate1); - g_soundCard2InDeviceNum = txInAudioDeviceNum; - g_soundCard2OutDeviceNum = rxOutAudioDeviceNum; - g_soundCard2SampleRate = wxAtoi(sampleRate2); - } - - printf(" g_nSoundCards: %d\n", g_nSoundCards); - printf(" g_soundCard1InDeviceNum: %d\n", g_soundCard1InDeviceNum); - printf(" g_soundCard1OutDeviceNum: %d\n", g_soundCard1OutDeviceNum); - printf(" g_soundCard1SampleRate: %d\n", g_soundCard1SampleRate); - printf(" g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - printf(" g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - printf(" g_soundCard2SampleRate: %d\n", g_soundCard2SampleRate); - - wxConfigBase *pConfig = wxConfigBase::Get(); - pConfig->Write(wxT("/Audio/soundCard1InDeviceNum"), g_soundCard1InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1OutDeviceNum"), g_soundCard1OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1SampleRate"), g_soundCard1SampleRate ); - - pConfig->Write(wxT("/Audio/soundCard2InDeviceNum"), g_soundCard2InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2OutDeviceNum"), g_soundCard2OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2SampleRate"), g_soundCard2SampleRate ); - - pConfig->Flush(); - delete wxConfigBase::Set((wxConfigBase *) NULL); - } - - return 0; -} - -//------------------------------------------------------------------------- -// buildListOfSupportedSampleRates() -//------------------------------------------------------------------------- -int AudioOptsDialog:: buildListOfSupportedSampleRates(wxComboBox *cbSampleRate, int devNum, int in_out) -{ - // every sound device has a different list of supported sample rates, so - // we work out which ones are supported and populate the list ctrl - - static double standardSampleRates[] = - { - 8000.0, 9600.0, - 11025.0, 12000.0, - 16000.0, 22050.0, - 24000.0, 32000.0, - 44100.0, 48000.0, - 88200.0, 96000.0, - 192000.0, -1 // negative terminated list - }; - - const PaDeviceInfo *deviceInfo; - PaStreamParameters inputParameters, outputParameters; - PaError err; - wxString str; - int i, numSampleRates; - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - printf("Pa_GetDeviceInfo(%d) failed!\n", devNum); - cbSampleRate->Clear(); - return 0; - } - - inputParameters.device = devNum; - inputParameters.channelCount = deviceInfo->maxInputChannels; - inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = 0; - inputParameters.hostApiSpecificStreamInfo = NULL; - - outputParameters.device = devNum; - outputParameters.channelCount = deviceInfo->maxOutputChannels; - outputParameters.sampleFormat = paInt16; - outputParameters.suggestedLatency = 0; - outputParameters.hostApiSpecificStreamInfo = NULL; - - cbSampleRate->Clear(); - //printf("devNum %d supports: ", devNum); - numSampleRates = 0; - for(i = 0; standardSampleRates[i] > 0; i++) - { - if (in_out == AUDIO_IN) - err = Pa_IsFormatSupported(&inputParameters, NULL, standardSampleRates[i]); - else - err = Pa_IsFormatSupported(NULL, &outputParameters, standardSampleRates[i]); - - if( err == paFormatIsSupported ) { - str.Printf("%i", (int)standardSampleRates[i]); - cbSampleRate->AppendString(str); - printf("%i ", (int)standardSampleRates[i]); - numSampleRates++; - } - } - printf("\n"); - - return numSampleRates; -} - -//------------------------------------------------------------------------- -// showAPIInfo() -//------------------------------------------------------------------------- -void AudioOptsDialog::showAPIInfo() -{ - wxString strval; - int apiVersion; - int apiCount = 0; - int numDevices = 0; - - strval = Pa_GetVersionText(); - m_textStringVer->SetLabel(strval); - - apiVersion = Pa_GetVersion(); - strval.Printf(wxT("%d"), apiVersion); - m_textIntVer->SetLabel(strval); - - apiCount = Pa_GetHostApiCount(); - strval.Printf(wxT("%d"), apiCount); - m_textAPICount->SetLabel(strval); - - numDevices = Pa_GetDeviceCount(); - strval.Printf(wxT("%d"), numDevices); - m_textCDevCount->SetLabel(strval); -} - -//------------------------------------------------------------------------- -// populateParams() -//------------------------------------------------------------------------- -void AudioOptsDialog::populateParams(AudioInfoDisplay ai) -{ - const PaDeviceInfo *deviceInfo = NULL; - wxListCtrl* ctrl = ai.m_listDevices; - int in_out = ai.direction; - long idx; - int numDevices; - wxListItem listItem; - wxString buf; - int devn; - int col = 0; - - numDevices = Pa_GetDeviceCount(); - - if(ctrl->GetColumnCount() > 0) - { - ctrl->ClearAll(); - } - - listItem.SetAlign(wxLIST_FORMAT_LEFT); - listItem.SetText(wxT("Device")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 300); - - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("ID")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 45); - - listItem.SetAlign(wxLIST_FORMAT_LEFT); - listItem.SetText(wxT("API")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - - if(in_out == AUDIO_IN) - { - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Default Sample Rate")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 160); - } - else if(in_out == AUDIO_OUT) - { - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Default Sample Rate")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 160); - } - - #ifdef LATENCY - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Min Latency")); - ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Max Latency")); - ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - #endif - - for(devn = 0; devn < numDevices; devn++) - { - buf.Printf(wxT("")); - deviceInfo = Pa_GetDeviceInfo(devn); - if( ((in_out == AUDIO_IN) && (deviceInfo->maxInputChannels > 0)) || - ((in_out == AUDIO_OUT) && (deviceInfo->maxOutputChannels > 0))) - { - col = 0; - buf.Printf(wxT("%s"), deviceInfo->name); - idx = ctrl->InsertItem(ctrl->GetItemCount(), buf); - col++; - - buf.Printf(wxT("%d"), devn); - ctrl->SetItem(idx, col++, buf); - - buf.Printf(wxT("%s"), Pa_GetHostApiInfo(deviceInfo->hostApi)->name); - ctrl->SetItem(idx, col++, buf); - - buf.Printf(wxT("%i"), (int)deviceInfo->defaultSampleRate); - ctrl->SetItem(idx, col++, buf); - - #ifdef LATENCY - if (in_out == AUDIO_IN) - buf.Printf(wxT("%8.4f"), deviceInfo->defaultLowInputLatency); - else - buf.Printf(wxT("%8.4f"), deviceInfo->defaultLowOutputLatency); - ctrl->SetItem(idx, col++, buf); - - if (in_out == AUDIO_IN) - buf.Printf(wxT("%8.4f"), deviceInfo->defaultHighInputLatency); - else - buf.Printf(wxT("%8.4f"), deviceInfo->defaultHighOutputLatency); - ctrl->SetItem(idx, col++, buf); - #endif - } - } - - // add "none" option at end - - buf.Printf(wxT("%s"), "none"); - idx = ctrl->InsertItem(ctrl->GetItemCount(), buf); -} - -//------------------------------------------------------------------------- -// OnDeviceSelect() -// -// helper function to set up "Device:" and "Sample Rate:" fields when -// we click on a line in the list of devices box -//------------------------------------------------------------------------- -void AudioOptsDialog::OnDeviceSelect(wxComboBox *cbSampleRate, - wxTextCtrl *textCtrl, - int *devNum, - wxListCtrl *listCtrlDevices, - int index, - int in_out) -{ - - wxString devName = listCtrlDevices->GetItemText(index, 0); - if (devName.IsSameAs("none")) { - *devNum = -1; - textCtrl->SetValue("none"); - } - else { - *devNum = wxAtoi(listCtrlDevices->GetItemText(index, 1)); - textCtrl->SetValue(devName + " (" + wxString::Format(wxT("%i"),*devNum) + ")"); - - int numSampleRates = buildListOfSupportedSampleRates(cbSampleRate, *devNum, in_out); - if (numSampleRates) { - wxString defSampleRate = listCtrlDevices->GetItemText(index, 3); - cbSampleRate->SetValue(defSampleRate); - } - else { - cbSampleRate->SetValue("None"); - } - } -} - -//------------------------------------------------------------------------- -// OnRxInDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxInDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateRxIn, - m_textCtrlRxIn, - &rxInAudioDeviceNum, - m_listCtrlRxInDevices, - evt.GetIndex(), - AUDIO_IN); -} - -//------------------------------------------------------------------------- -// OnRxOutDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxOutDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateRxOut, - m_textCtrlRxOut, - &rxOutAudioDeviceNum, - m_listCtrlRxOutDevices, - evt.GetIndex(), - AUDIO_OUT); -} - -//------------------------------------------------------------------------- -// OnTxInDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxInDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateTxIn, - m_textCtrlTxIn, - &txInAudioDeviceNum, - m_listCtrlTxInDevices, - evt.GetIndex(), - AUDIO_IN); -} - -//------------------------------------------------------------------------- -// OnTxOutDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxOutDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateTxOut, - m_textCtrlTxOut, - &txOutAudioDeviceNum, - m_listCtrlTxOutDevices, - evt.GetIndex(), - AUDIO_OUT); -} - -//------------------------------------------------------------------------- -// plotDeviceInputForAFewSecs() -// -// opens a record device and plots the input speech for a few seconds. This is "modal" using -// synchronous portaudio functions, so the GUI will not respond until after test sample has been -// taken -//------------------------------------------------------------------------- -void AudioOptsDialog::plotDeviceInputForAFewSecs(int devNum, PlotScalar *plotScalar) { - PaStreamParameters inputParameters; - const PaDeviceInfo *deviceInfo = NULL; - PaStream *stream = NULL; - PaError err; - short in48k_stereo_short[2*TEST_BUF_SIZE]; - short in48k_short[TEST_BUF_SIZE]; - short in8k_short[TEST_BUF_SIZE]; - int numDevices, nBufs, i, j, src_error,inputChannels; - float t; - SRC_STATE *src; - FIFO *fifo; - - // a basic sanity check - numDevices = Pa_GetDeviceCount(); - if (devNum >= numDevices) - return; - if (devNum < 0) - return; - printf("devNum %d\n", devNum); - - fifo = fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL); - src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL); - - // work out how many input channels this device supports. - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card "), wxT("Error"), wxOK); - return; - } - if (deviceInfo->maxInputChannels == 1) - inputChannels = 1; - else - inputChannels = 2; - - // open device - - inputParameters.device = devNum; - inputParameters.channelCount = inputChannels; - inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - nBufs = TEST_WAVEFORM_PLOT_TIME*TEST_FS/TEST_BUF_SIZE; - printf("inputChannels: %d nBufs %d\n", inputChannels, nBufs); - - err = Pa_OpenStream( - &stream, - &inputParameters, - NULL, - TEST_FS, - TEST_BUF_SIZE, - paClipOff, - NULL, // no callback, use blocking API - NULL ); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't initialise sound device."), wxT("Error"), wxOK); - return; - } - - err = Pa_StartStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't start sound device."), wxT("Error"), wxOK); - return; - } - - for(i=0, t=0.0; i TEST_DT) { - t -= TEST_DT; - short plotSamples[TEST_WAVEFORM_PLOT_BUF]; - if (fifo_read(fifo, plotSamples, TEST_WAVEFORM_PLOT_BUF)) - memset(plotSamples, 0, TEST_WAVEFORM_PLOT_BUF*sizeof(short)); - plotScalar->add_new_short_samples(0, plotSamples, TEST_WAVEFORM_PLOT_BUF, 32767); - plotScalar->Refresh(); - } - } - - err = Pa_StopStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't stop sound device."), wxT("Error"), wxOK); - return; - } - Pa_CloseStream(stream); - - fifo_destroy(fifo); - src_delete(src); -} - -//------------------------------------------------------------------------- -// plotDeviceOutputForAFewSecs() -// -// opens a play device and plays a tone for a few seconds. This is "modal" using -// synchronous portaudio functions, so the GUI will not respond until after test sample has been -// taken. Also plots a pretty picture like the record versions -//------------------------------------------------------------------------- -void AudioOptsDialog::plotDeviceOutputForAFewSecs(int devNum, PlotScalar *plotScalar) { - PaStreamParameters outputParameters; - const PaDeviceInfo *deviceInfo = NULL; - PaStream *stream = NULL; - PaError err; - short out48k_stereo_short[2*TEST_BUF_SIZE]; - short out48k_short[TEST_BUF_SIZE]; - short out8k_short[TEST_BUF_SIZE]; - int numDevices, nBufs, i, j, src_error, n, outputChannels; - float t; - SRC_STATE *src; - FIFO *fifo; - - // a basic sanity check - numDevices = Pa_GetDeviceCount(); - if (devNum >= numDevices) - return; - if (devNum < 0) - return; - - fifo = fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL); - src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL); - - // work out how many output channels this device supports. - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card "), wxT("Error"), wxOK); - return; - } - if (deviceInfo->maxOutputChannels == 1) - outputChannels = 1; - else - outputChannels = 2; - - printf("outputChannels: %d\n", outputChannels); - - outputParameters.device = devNum; - outputParameters.channelCount = outputChannels; - outputParameters.sampleFormat = paInt16; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - nBufs = TEST_WAVEFORM_PLOT_TIME*TEST_FS/TEST_BUF_SIZE; - - err = Pa_OpenStream( - &stream, - NULL, - &outputParameters, - TEST_FS, - TEST_BUF_SIZE, - paClipOff, - NULL, // no callback, use blocking API - NULL ); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't initialise sound device."), wxT("Error"), wxOK); - return; - } - - err = Pa_StartStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't start sound device."), wxT("Error"), wxOK); - return; - } - - for(i=0, t=0.0, n=0; i TEST_DT) { - t -= TEST_DT; - short plotSamples[TEST_WAVEFORM_PLOT_BUF]; - if (fifo_read(fifo, plotSamples, TEST_WAVEFORM_PLOT_BUF)) - memset(plotSamples, 0, TEST_WAVEFORM_PLOT_BUF*sizeof(short)); - plotScalar->add_new_short_samples(0, plotSamples, TEST_WAVEFORM_PLOT_BUF, 32767); - plotScalar->Refresh(); - } - } - - err = Pa_StopStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't stop sound device."), wxT("Error"), wxOK); - return; - } - Pa_CloseStream(stream); - - fifo_destroy(fifo); - src_delete(src); -} - -//------------------------------------------------------------------------- -// OnRxInTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxInTest(wxCommandEvent& event) -{ - plotDeviceInputForAFewSecs(rxInAudioDeviceNum, m_plotScalarRxIn); -} - -//------------------------------------------------------------------------- -// OnRxOutTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxOutTest(wxCommandEvent& event) -{ - plotDeviceOutputForAFewSecs(rxOutAudioDeviceNum, m_plotScalarRxOut); -} - -//------------------------------------------------------------------------- -// OnTxInTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxInTest(wxCommandEvent& event) -{ - plotDeviceInputForAFewSecs(txInAudioDeviceNum, m_plotScalarTxIn); -} - -//------------------------------------------------------------------------- -// OnTxOutTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxOutTest(wxCommandEvent& event) -{ - plotDeviceOutputForAFewSecs(txOutAudioDeviceNum, m_plotScalarTxOut); -} - -//------------------------------------------------------------------------- -// OnRefreshClick() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRefreshClick(wxCommandEvent& event) -{ - // restart portaudio, to re-sample available devices - - Pa_Terminate(); - Pa_Init(); - - m_notebook1->SetSelection(0); - showAPIInfo(); - populateParams(m_RxInDevices); - populateParams(m_RxOutDevices); - populateParams(m_TxInDevices); - populateParams(m_TxOutDevices); - - // some devices may have dissapeared, so possibily change sound - // card config - - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// OnApplyAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnApplyAudioParameters(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } -} - -//------------------------------------------------------------------------- -// OnCancelAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnCancelAudioParameters(wxCommandEvent& event) -{ - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } - EndModal(wxCANCEL); -} - -//------------------------------------------------------------------------- -// OnOkAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnOkAudioParameters(wxCommandEvent& event) -{ - int status = ExchangeData(EXCHANGE_DATA_OUT); - - // We only accept OK if config sucessful - - printf("status: %d m_isPaInitialized: %d\n", status, m_isPaInitialized); - if (status == 0) { - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - printf("terminated OK\n"); - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } - EndModal(wxOK); - } - -} diff --git a/freedv/branches/1.2/freedv-dev/src/dlg_audiooptions.h b/freedv/branches/1.2/freedv-dev/src/dlg_audiooptions.h deleted file mode 100644 index 5aa6741d..00000000 --- a/freedv/branches/1.2/freedv-dev/src/dlg_audiooptions.h +++ /dev/null @@ -1,176 +0,0 @@ -//========================================================================= -// Name: AudioInfoDisplay.h -// Purpose: Declares simple wxWidgets application with GUI -// created using wxFormBuilder. -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================= -#ifndef __AudioOptsDialog__ -#define __AudioOptsDialog__ - -#include "fdmdv2_main.h" - -#define ID_AUDIO_OPTIONS 1000 -#define AUDIO_IN 0 -#define AUDIO_OUT 1 - -#include "portaudio.h" -#ifdef WIN32 -#if PA_USE_ASIO -#include "pa_asio.h" -#endif -#endif -#include "codec2_fifo.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// AudioInfoDisplay -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class AudioInfoDisplay -{ - public: - wxListCtrl* m_listDevices; - int direction; - wxTextCtrl* m_textDevice; - wxComboBox* m_cbSampleRate; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class AudioOptsDialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class AudioOptsDialog : public wxDialog -{ - private: - - protected: - PaError pa_err; - bool m_isPaInitialized; - - int rxInAudioDeviceNum; - int rxOutAudioDeviceNum; - int txInAudioDeviceNum; - int txOutAudioDeviceNum; - - void buildTestControls(PlotScalar **plotScalar, wxButton **btnTest, - wxPanel *parentPanel, wxBoxSizer *bSizer, wxString buttonLabel); - void plotDeviceInputForAFewSecs(int devNum, PlotScalar *plotScalar); - void plotDeviceOutputForAFewSecs(int devNum, PlotScalar *plotScalar); - - int buildListOfSupportedSampleRates(wxComboBox *cbSampleRate, int devNum, int in_out); - void populateParams(AudioInfoDisplay); - void showAPIInfo(); - int setTextCtrlIfDevNumValid(wxTextCtrl *textCtrl, wxListCtrl *listCtrl, int devNum); - void Pa_Init(void); - void OnDeviceSelect(wxComboBox *cbSampleRate, - wxTextCtrl *textCtrl, - int *devNum, - wxListCtrl *listCtrlDevices, - int index, - int in_out); - - AudioInfoDisplay m_RxInDevices; - AudioInfoDisplay m_RxOutDevices; - AudioInfoDisplay m_TxInDevices; - AudioInfoDisplay m_TxOutDevices; - wxPanel* m_panel1; - wxNotebook* m_notebook1; - - wxPanel* m_panelRx; - - wxListCtrl* m_listCtrlRxInDevices; - wxStaticText* m_staticText51; - wxTextCtrl* m_textCtrlRxIn; - wxStaticText* m_staticText6; - wxComboBox* m_cbSampleRateRxIn; - - wxButton* m_btnRxInTest; - PlotScalar* m_plotScalarRxIn; - - wxListCtrl* m_listCtrlRxOutDevices; - wxStaticText* m_staticText9; - wxTextCtrl* m_textCtrlRxOut; - wxStaticText* m_staticText10; - wxComboBox* m_cbSampleRateRxOut; - - wxButton* m_btnRxOutTest; - PlotScalar* m_plotScalarRxOut; - - wxPanel* m_panelTx; - - wxListCtrl* m_listCtrlTxInDevices; - wxStaticText* m_staticText12; - wxTextCtrl* m_textCtrlTxIn; - wxStaticText* m_staticText11; - wxComboBox* m_cbSampleRateTxIn; - - wxButton* m_btnTxInTest; - PlotScalar* m_plotScalarTxIn; - - wxListCtrl* m_listCtrlTxOutDevices; - wxStaticText* m_staticText81; - wxTextCtrl* m_textCtrlTxOut; - wxStaticText* m_staticText71; - wxComboBox* m_cbSampleRateTxOut; - - wxButton* m_btnTxOutTest; - PlotScalar* m_plotScalarTxOut; - - wxPanel* m_panelAPI; - - wxStaticText* m_staticText7; - wxStaticText* m_textStringVer; - wxStaticText* m_staticText8; - wxStaticText* m_textIntVer; - wxStaticText* m_staticText5; - wxStaticText* m_textCDevCount; - wxStaticText* m_staticText4; - wxStaticText* m_textAPICount; - wxButton* m_btnRefresh; - wxStdDialogButtonSizer* m_sdbSizer1; - wxButton* m_sdbSizer1OK; - wxButton* m_sdbSizer1Apply; - wxButton* m_sdbSizer1Cancel; - - // Virtual event handlers, overide them in your derived class - //virtual void OnActivateApp( wxActivateEvent& event ) { event.Skip(); } -// virtual void OnCloseFrame( wxCloseEvent& event ) { event.Skip(); } - - void OnRxInDeviceSelect( wxListEvent& event ); - - void OnRxInTest( wxCommandEvent& event ); - void OnRxOutTest( wxCommandEvent& event ); - void OnTxInTest( wxCommandEvent& event ); - void OnTxOutTest( wxCommandEvent& event ); - - void OnRxOutDeviceSelect( wxListEvent& event ); - void OnTxInDeviceSelect( wxListEvent& event ); - void OnTxOutDeviceSelect( wxListEvent& event ); - void OnRefreshClick( wxCommandEvent& event ); - void OnApplyAudioParameters( wxCommandEvent& event ); - void OnCancelAudioParameters( wxCommandEvent& event ); - void OnOkAudioParameters( wxCommandEvent& event ); - // Virtual event handlers, overide them in your derived class - void OnClose( wxCloseEvent& event ) { event.Skip(); } - void OnHibernate( wxActivateEvent& event ) { event.Skip(); } - void OnIconize( wxIconizeEvent& event ) { event.Skip(); } - void OnInitDialog( wxInitDialogEvent& event ); - - public: - - AudioOptsDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Audio Config"), const wxPoint& pos = wxPoint(1,1), const wxSize& size = wxSize( 800, 650 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~AudioOptsDialog(); - int ExchangeData(int inout); -}; -#endif //__AudioOptsDialog__ diff --git a/freedv/branches/1.2/freedv-dev/src/dlg_filter.cpp b/freedv/branches/1.2/freedv-dev/src/dlg_filter.cpp deleted file mode 100644 index 5a5294a9..00000000 --- a/freedv/branches/1.2/freedv-dev/src/dlg_filter.cpp +++ /dev/null @@ -1,785 +0,0 @@ -//========================================================================== -// Name: dlg_filter.cpp -// Purpose: Dialog for controlling Codec audio filtering -// Date: Nov 25 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "dlg_filter.h" - -#define SLIDER_MAX 100 -#define SLIDER_LENGTH 100 - -#define FILTER_MIN_MAG_DB -20.0 -#define FILTER_MAX_MAG_DB 20.0 - -#define MAX_FREQ_BASS 600.00 -#define MAX_FREQ_TREBLE 3900.00 -#define MAX_FREQ_DEF 3000.00 - -#define MIN_GAIN -20 -#define MAX_GAIN 20 - -#define MAX_LOG10_Q 1.0 -#define MIN_LOG10_Q -1.0 - -// DFT parameters - -#define IMP_AMP 2000.0 // amplitude of impulse -#define NIMP 50 // number of samples in impulse response -#define F_STEP_DFT 10.0 // frequency steps to sample spectrum -#define F_MAG_N (int)(MAX_F_HZ/F_STEP_DFT) // number of frequency steps - -extern struct freedv *g_pfreedv; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class FilterDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -FilterDlg::FilterDlg(wxWindow* parent, bool running, bool *newMicInFilter, bool *newSpkOutFilter, - wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - m_running = running; - m_newMicInFilter = newMicInFilter; - m_newSpkOutFilter = newSpkOutFilter; - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* bSizer30; - bSizer30 = new wxBoxSizer(wxVERTICAL); - - // LPC Post Filter -------------------------------------------------------- - - wxStaticBoxSizer* lpcpfs = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("LPC Post Filter")), wxHORIZONTAL); - - wxBoxSizer* left = new wxBoxSizer(wxVERTICAL); - - m_codec2LPCPostFilterEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - left->Add(m_codec2LPCPostFilterEnable); - - m_codec2LPCPostFilterBassBoost = new wxCheckBox(this, wxID_ANY, _("0-1 kHz 3dB Boost"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - left->Add(m_codec2LPCPostFilterBassBoost); - lpcpfs->Add(left, 0, wxALL, 5); - - newLPCPFControl(&m_codec2LPCPostFilterBeta, &m_staticTextBeta, lpcpfs, "Beta"); - newLPCPFControl(&m_codec2LPCPostFilterGamma, &m_staticTextGamma, lpcpfs, "Gamma"); - - m_LPCPostFilterDefault = new wxButton(this, wxID_ANY, wxT("Default")); - lpcpfs->Add(m_LPCPostFilterDefault, 0, wxALL|wxALIGN_CENTRE_HORIZONTAL|wxALIGN_CENTRE_VERTICAL, 5); - - bSizer30->Add(lpcpfs, 0, wxALL, 0); - - // Speex pre-processor -------------------------------------------------- - - wxStaticBoxSizer* sbSizer_speexpp; - wxStaticBox *sb_speexpp = new wxStaticBox(this, wxID_ANY, _("Speex Mic Audio Pre-Processor")); - sbSizer_speexpp = new wxStaticBoxSizer(sb_speexpp, wxVERTICAL); - - m_ckboxSpeexpp = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_speexpp->SetToolTip(_("Enable noise supression, dereverberation, AGC of mic signal")); - sbSizer_speexpp->Add(m_ckboxSpeexpp, wxALIGN_LEFT, 2); - - bSizer30->Add(sbSizer_speexpp, 0, wxALL, 0); - - // EQ Filters ----------------------------------------------------------- - - wxStaticBoxSizer* eqMicInSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Mic In Equaliser")), wxVERTICAL); - wxBoxSizer* eqMicInSizer1 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* eqMicInSizer2 = new wxBoxSizer(wxHORIZONTAL); - - m_MicInBass = newEQ(eqMicInSizer1, "Bass" , MAX_FREQ_BASS, disableQ); - m_MicInTreble = newEQ(eqMicInSizer1, "Treble", MAX_FREQ_TREBLE, disableQ); - eqMicInSizer->Add(eqMicInSizer1); - - m_MicInEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - eqMicInSizer2->Add(m_MicInEnable,0,wxALIGN_CENTRE_VERTICAL|wxRIGHT,10); - m_MicInMid = newEQ(eqMicInSizer2, "Mid" , MAX_FREQ_DEF, enableQ); - m_MicInDefault = new wxButton(this, wxID_ANY, wxT("Default")); - eqMicInSizer2->Add(m_MicInDefault,0,wxALIGN_CENTRE_VERTICAL|wxLEFT,20); - eqMicInSizer->Add(eqMicInSizer2); - - wxStaticBoxSizer* eqSpkOutSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Speaker Out Equaliser")), wxVERTICAL); - wxBoxSizer* eqSpkOutSizer1 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* eqSpkOutSizer2 = new wxBoxSizer(wxHORIZONTAL); - - m_SpkOutBass = newEQ(eqSpkOutSizer1, "Bass" , MAX_FREQ_BASS, disableQ); - m_SpkOutTreble = newEQ(eqSpkOutSizer1, "Treble", MAX_FREQ_TREBLE, disableQ); - eqSpkOutSizer->Add(eqSpkOutSizer1); - - m_SpkOutEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - eqSpkOutSizer2->Add(m_SpkOutEnable,0,wxALIGN_CENTRE_VERTICAL|wxRIGHT,10); - m_SpkOutMid = newEQ(eqSpkOutSizer2, "Mid" , MAX_FREQ_DEF, enableQ); - m_SpkOutDefault = new wxButton(this, wxID_ANY, wxT("Default")); - eqSpkOutSizer2->Add(m_SpkOutDefault,0,wxALIGN_CENTRE_VERTICAL|wxLEFT,20); - eqSpkOutSizer->Add(eqSpkOutSizer2); - - bSizer30->Add(eqMicInSizer, 0, wxALL, 0); - bSizer30->Add(eqSpkOutSizer, 0, wxALL, 0); - - // Storgage for spectrum magnitude plots ------------------------------------ - - m_MicInMagdB = new float[F_MAG_N]; - for(int i=0; iSetFont(wxFont(8, 70, 90, 90, false, wxEmptyString)); - - bSizer30->Add(m_auiNotebook, 0, wxEXPAND|wxALL, 3); - - m_MicInFreqRespPlot = new PlotSpectrum((wxFrame*) m_auiNotebook, m_MicInMagdB, F_MAG_N, FILTER_MIN_MAG_DB, FILTER_MAX_MAG_DB); - m_auiNotebook->AddPage(m_MicInFreqRespPlot, _("Microphone In Equaliser")); - - m_SpkOutFreqRespPlot = new PlotSpectrum((wxFrame*)m_auiNotebook, m_SpkOutMagdB, F_MAG_N, FILTER_MIN_MAG_DB, FILTER_MAX_MAG_DB); - m_auiNotebook->AddPage(m_SpkOutFreqRespPlot, _("Speaker Out Equaliser")); - - // OK - Cancel buttons at the bottom -------------------------- - - wxBoxSizer* bSizer31 = new wxBoxSizer(wxHORIZONTAL); - - m_sdbSizer5OK = new wxButton(this, wxID_OK); - bSizer31->Add(m_sdbSizer5OK, 0, wxALL, 2); - - m_sdbSizer5Cancel = new wxButton(this, wxID_CANCEL); - bSizer31->Add(m_sdbSizer5Cancel, 0, wxALL, 2); - - bSizer30->Add(bSizer31, 0, wxALIGN_RIGHT|wxALL, 0); - - this->SetSizer(bSizer30); - this->Layout(); - - this->Centre(wxBOTH); - - // Connect Events ------------------------------------------------------- - - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(FilterDlg::OnInitDialog)); - - m_codec2LPCPostFilterEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnEnable), NULL, this); - m_codec2LPCPostFilterBassBoost->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnBassBoost), NULL, this); - m_codec2LPCPostFilterBeta->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnBetaScroll), NULL, this); - m_codec2LPCPostFilterGamma->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnGammaScroll), NULL, this); - m_LPCPostFilterDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnLPCPostFilterDefault), NULL, this); - - m_ckboxSpeexpp->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpeexppEnable), NULL, this); - - m_MicInBass.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassFreqScroll), NULL, this); - m_MicInBass.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassGainScroll), NULL, this); - m_MicInTreble.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleFreqScroll), NULL, this); - m_MicInTreble.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleGainScroll), NULL, this); - m_MicInMid.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidFreqScroll), NULL, this); - m_MicInMid.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidGainScroll), NULL, this); - m_MicInMid.sliderQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidQScroll), NULL, this); - m_MicInEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnMicInEnable), NULL, this); - m_MicInDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnMicInDefault), NULL, this); - - m_SpkOutBass.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassFreqScroll), NULL, this); - m_SpkOutBass.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassGainScroll), NULL, this); - m_SpkOutTreble.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleFreqScroll), NULL, this); - m_SpkOutTreble.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleGainScroll), NULL, this); - m_SpkOutMid.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidFreqScroll), NULL, this); - m_SpkOutMid.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidGainScroll), NULL, this); - m_SpkOutMid.sliderQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidQScroll), NULL, this); - m_SpkOutEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpkOutEnable), NULL, this); - m_SpkOutDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnSpkOutDefault), NULL, this); - - m_sdbSizer5Cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnCancel), NULL, this); - m_sdbSizer5OK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnOK), NULL, this); - -} - -//------------------------------------------------------------------------- -// ~FilterDlg() -//------------------------------------------------------------------------- -FilterDlg::~FilterDlg() -{ - delete m_MicInMagdB; - delete m_SpkOutMagdB; - - // Disconnect Events - - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(FilterDlg::OnInitDialog)); - - m_codec2LPCPostFilterEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnEnable), NULL, this); - m_codec2LPCPostFilterBassBoost->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnBassBoost), NULL, this); - m_codec2LPCPostFilterBeta->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnBetaScroll), NULL, this); - m_codec2LPCPostFilterGamma->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnGammaScroll), NULL, this); - m_LPCPostFilterDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnLPCPostFilterDefault), NULL, this); - - m_MicInBass.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassFreqScroll), NULL, this); - m_MicInBass.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassGainScroll), NULL, this); - m_MicInTreble.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleFreqScroll), NULL, this); - m_MicInTreble.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleGainScroll), NULL, this); - m_MicInMid.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidFreqScroll), NULL, this); - m_MicInMid.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidGainScroll), NULL, this); - m_MicInMid.sliderQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidQScroll), NULL, this); - m_MicInEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnMicInEnable), NULL, this); - m_MicInDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnMicInDefault), NULL, this); - - m_SpkOutBass.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassFreqScroll), NULL, this); - m_SpkOutBass.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassGainScroll), NULL, this); - m_SpkOutTreble.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleFreqScroll), NULL, this); - m_SpkOutTreble.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleGainScroll), NULL, this); - m_SpkOutMid.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidFreqScroll), NULL, this); - m_SpkOutMid.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidGainScroll), NULL, this); - m_SpkOutMid.sliderQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidQScroll), NULL, this); - m_SpkOutEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpkOutEnable), NULL, this); - m_SpkOutDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnSpkOutDefault), NULL, this); - - m_sdbSizer5Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnCancel), NULL, this); - m_sdbSizer5OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnOK), NULL, this); -} - -void FilterDlg::newLPCPFControl(wxSlider **slider, wxStaticText **stValue, wxSizer *s, wxString controlName) -{ - wxBoxSizer *bs = new wxBoxSizer(wxHORIZONTAL); - - wxStaticText* st = new wxStaticText(this, wxID_ANY, controlName, wxDefaultPosition, wxSize(70,-1), wxALIGN_RIGHT); - bs->Add(st, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 2); - - *slider = new wxSlider(this, wxID_ANY, 0, 0, SLIDER_MAX, wxDefaultPosition, wxSize(SLIDER_LENGTH,wxDefaultCoord)); - bs->Add(*slider, 1, wxALIGN_CENTER_VERTICAL|wxALL, 2); - - *stValue = new wxStaticText(this, wxID_ANY, wxT("0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - bs->Add(*stValue, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL, 2); - - s->Add(bs, 0); -} - -void FilterDlg::newEQControl(wxSlider** slider, wxStaticText** value, wxStaticBoxSizer *bs, wxString controlName) -{ - wxStaticText* label = new wxStaticText(this, wxID_ANY, controlName, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); - bs->Add(label, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 0); - - *slider = new wxSlider(this, wxID_ANY, 0, 0, SLIDER_MAX, wxDefaultPosition, wxSize(SLIDER_LENGTH,wxDefaultCoord)); - bs->Add(*slider, 1, wxALIGN_CENTER_VERTICAL|wxALL, 0); - - *value = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(40,-1), wxALIGN_LEFT); - bs->Add(*value, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxRIGHT, 5); -} - -EQ FilterDlg::newEQ(wxSizer *bs, wxString eqName, float maxFreqHz, bool enableQ) -{ - EQ eq; - - wxStaticBoxSizer *bsEQ = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, eqName), wxHORIZONTAL); - - newEQControl(&eq.sliderFreq, &eq.valueFreq, bsEQ, "Freq"); - eq.maxFreqHz = maxFreqHz; - eq.sliderFreqId = eq.sliderFreq->GetId(); - - newEQControl(&eq.sliderGain, &eq.valueGain, bsEQ, "Gain"); - if (enableQ) - newEQControl(&eq.sliderQ, &eq.valueQ, bsEQ, "Q"); - else - eq.sliderQ = NULL; - - bs->Add(bsEQ); - - return eq; -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void FilterDlg::ExchangeData(int inout, bool storePersistent) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - if(inout == EXCHANGE_DATA_IN) - { - // LPC Post filter - - m_codec2LPCPostFilterEnable->SetValue(wxGetApp().m_codec2LPCPostFilterEnable); - m_codec2LPCPostFilterBassBoost->SetValue(wxGetApp().m_codec2LPCPostFilterBassBoost); - m_beta = wxGetApp().m_codec2LPCPostFilterBeta; setBeta(); - m_gamma = wxGetApp().m_codec2LPCPostFilterGamma; setGamma(); - - // Speex Pre-Processor - - m_ckboxSpeexpp->SetValue(wxGetApp().m_speexpp_enable); - - // Mic In Equaliser - - m_MicInBass.freqHz = wxGetApp().m_MicInBassFreqHz; - m_MicInBass.freqHz = limit(m_MicInBass.freqHz, 1.0, MAX_FREQ_BASS); - setFreq(&m_MicInBass); - m_MicInBass.gaindB = wxGetApp().m_MicInBassGaindB; - m_MicInBass.gaindB = limit(m_MicInBass.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInBass); - - m_MicInTreble.freqHz = wxGetApp().m_MicInTrebleFreqHz; - m_MicInTreble.freqHz = limit(m_MicInTreble.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_MicInTreble); - m_MicInTreble.gaindB = wxGetApp().m_MicInTrebleGaindB; - m_MicInTreble.gaindB = limit(m_MicInTreble.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInTreble); - - m_MicInMid.freqHz = wxGetApp().m_MicInMidFreqHz; - m_MicInMid.freqHz = limit(m_MicInMid.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_MicInMid); - m_MicInMid.gaindB = wxGetApp().m_MicInMidGaindB; - m_MicInMid.gaindB = limit(m_MicInMid.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInMid); - m_MicInMid.Q = wxGetApp().m_MicInMidQ; - m_MicInMid.Q = limit(m_MicInMid.Q, pow(10.0,MIN_LOG10_Q), pow(10.0, MAX_LOG10_Q)); - setQ(&m_MicInMid); - - m_MicInEnable->SetValue(wxGetApp().m_MicInEQEnable); - - plotMicInFilterSpectrum(); - - // Spk Out Equaliser - - m_SpkOutBass.freqHz = wxGetApp().m_SpkOutBassFreqHz; - m_SpkOutBass.freqHz = limit(m_SpkOutBass.freqHz, 1.0, MAX_FREQ_BASS); - setFreq(&m_SpkOutBass); - m_SpkOutBass.gaindB = wxGetApp().m_SpkOutBassGaindB; - m_SpkOutBass.gaindB = limit(m_SpkOutBass.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutBass); - - m_SpkOutTreble.freqHz = wxGetApp().m_SpkOutTrebleFreqHz; - m_SpkOutTreble.freqHz = limit(m_SpkOutTreble.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_SpkOutTreble); - m_SpkOutTreble.gaindB = wxGetApp().m_SpkOutTrebleGaindB; - m_SpkOutTreble.gaindB = limit(m_SpkOutTreble.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutTreble); - - m_SpkOutMid.freqHz = wxGetApp().m_SpkOutMidFreqHz; - m_SpkOutMid.freqHz = limit(m_SpkOutMid.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_SpkOutMid); - m_SpkOutMid.gaindB = wxGetApp().m_SpkOutMidGaindB; - m_SpkOutMid.gaindB = limit(m_SpkOutMid.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutMid); - m_SpkOutMid.Q = wxGetApp().m_SpkOutMidQ; - m_SpkOutMid.Q = limit(m_SpkOutMid.Q, pow(10.0,MIN_LOG10_Q), pow(10.0, MAX_LOG10_Q)); - setQ(&m_SpkOutMid); - - m_SpkOutEnable->SetValue(wxGetApp().m_SpkOutEQEnable); - - plotSpkOutFilterSpectrum(); - } - if(inout == EXCHANGE_DATA_OUT) - { - // LPC Post filter - - wxGetApp().m_codec2LPCPostFilterEnable = m_codec2LPCPostFilterEnable->GetValue(); - wxGetApp().m_codec2LPCPostFilterBassBoost = m_codec2LPCPostFilterBassBoost->GetValue(); - wxGetApp().m_codec2LPCPostFilterBeta = m_beta; - wxGetApp().m_codec2LPCPostFilterGamma = m_gamma; - - // Speex Pre-Processor - - wxGetApp().m_speexpp_enable = m_ckboxSpeexpp->GetValue(); - - // Mic In Equaliser - - wxGetApp().m_MicInBassFreqHz = m_MicInBass.freqHz; - wxGetApp().m_MicInBassGaindB = m_MicInBass.gaindB; - - wxGetApp().m_MicInTrebleFreqHz = m_MicInTreble.freqHz; - wxGetApp().m_MicInTrebleGaindB = m_MicInTreble.gaindB; - - wxGetApp().m_MicInMidFreqHz = m_MicInMid.freqHz; - wxGetApp().m_MicInMidGaindB = m_MicInMid.gaindB; - wxGetApp().m_MicInMidQ = m_MicInMid.Q; - - // Spk Out Equaliser - - wxGetApp().m_SpkOutBassFreqHz = m_SpkOutBass.freqHz; - wxGetApp().m_SpkOutBassGaindB = m_SpkOutBass.gaindB; - - wxGetApp().m_SpkOutTrebleFreqHz = m_SpkOutTreble.freqHz; - wxGetApp().m_SpkOutTrebleGaindB = m_SpkOutTreble.gaindB; - - wxGetApp().m_SpkOutMidFreqHz = m_SpkOutMid.freqHz; - wxGetApp().m_SpkOutMidGaindB = m_SpkOutMid.gaindB; - wxGetApp().m_SpkOutMidQ = m_SpkOutMid.Q; - - if (storePersistent) { - pConfig->Write(wxT("/Filter/codec2LPCPostFilterEnable"), wxGetApp().m_codec2LPCPostFilterEnable); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterBassBoost"), wxGetApp().m_codec2LPCPostFilterBassBoost); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterBeta"), (int)(m_beta*100.0)); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterGamma"), (int)(m_gamma*100.0)); - - pConfig->Write(wxT("/Filter/speexpp_enable"), wxGetApp().m_speexpp_enable); - - pConfig->Write(wxT("/Filter/MicInBassFreqHz"), (int)m_MicInBass.freqHz); - pConfig->Write(wxT("/Filter/MicInBassGaindB"), (int)(10.0*m_MicInBass.gaindB)); - pConfig->Write(wxT("/Filter/MicInTrebleFreqHz"), (int)m_MicInTreble.freqHz); - pConfig->Write(wxT("/Filter/MicInTrebleGaindB"), (int)(10.0*m_MicInTreble.gaindB)); - pConfig->Write(wxT("/Filter/MicInMidFreqHz"), (int)m_MicInMid.freqHz); - pConfig->Write(wxT("/Filter/MicInMidGaindB"), (int)(10.0*m_MicInMid.gaindB)); - pConfig->Write(wxT("/Filter/MicInMidQ"), (int)(100.0*m_MicInMid.Q)); - - pConfig->Write(wxT("/Filter/SpkOutBassFreqHz"), (int)m_SpkOutBass.freqHz); - pConfig->Write(wxT("/Filter/SpkOutBassGaindB"), (int)(10.0*m_SpkOutBass.gaindB)); - pConfig->Write(wxT("/Filter/SpkOutTrebleFreqHz"), (int)m_SpkOutTreble.freqHz); - pConfig->Write(wxT("/Filter/SpkOutTrebleGaindB"), (int)(10.0*m_SpkOutTreble.gaindB)); - pConfig->Write(wxT("/Filter/SpkOutMidQ"), (int)(100.0*m_SpkOutMid.Q)); - pConfig->Write(wxT("/Filter/SpkOutMidFreqHz"), (int)m_SpkOutMid.freqHz); - pConfig->Write(wxT("/Filter/SpkOutMidGaindB"), (int)(10.0*m_SpkOutMid.gaindB)); - - pConfig->Flush(); - } - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -float FilterDlg::limit(float value, float min, float max) { - if (value < min) return min; - if (value > max) return max; - return value; -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void FilterDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnDefault() -//------------------------------------------------------------------------- - -void FilterDlg::OnLPCPostFilterDefault(wxCommandEvent& event) -{ - m_beta = CODEC2_LPC_PF_BETA; setBeta(); - m_gamma = CODEC2_LPC_PF_GAMMA; setGamma(); - m_codec2LPCPostFilterEnable->SetValue(true); - m_codec2LPCPostFilterBassBoost->SetValue(true); -} - -void FilterDlg::OnMicInDefault(wxCommandEvent& event) -{ - m_MicInBass.freqHz = 100.0; - m_MicInBass.gaindB = 0.0; - setFreq(&m_MicInBass); setGain(&m_MicInBass); - - m_MicInTreble.freqHz = 3000.0; - m_MicInTreble.gaindB = 0.0; - setFreq(&m_MicInTreble); setGain(&m_MicInTreble); - - m_MicInMid.freqHz = 1500.0; - m_MicInMid.gaindB = 0.0; - m_MicInMid.Q = 1.0; - setFreq(&m_MicInMid); setGain(&m_MicInMid); setQ(&m_MicInMid); - - plotMicInFilterSpectrum(); -} - -void FilterDlg::OnSpkOutDefault(wxCommandEvent& event) -{ - m_SpkOutBass.freqHz = 100.0; - m_SpkOutBass.gaindB = 0.0; - setFreq(&m_SpkOutBass); setGain(&m_SpkOutBass); - - m_SpkOutTreble.freqHz = 3000.0; - m_SpkOutTreble.gaindB = 0.0; - setFreq(&m_SpkOutTreble); setGain(&m_SpkOutTreble); - - m_SpkOutMid.freqHz = 1500.0; - m_SpkOutMid.gaindB = 0.0; - m_SpkOutMid.Q = 1.0; - setFreq(&m_SpkOutMid); setGain(&m_SpkOutMid); setQ(&m_SpkOutMid); - - plotSpkOutFilterSpectrum(); -} - -//------------------------------------------------------------------------- -// OnOK() -//------------------------------------------------------------------------- -void FilterDlg::OnOK(wxCommandEvent& event) -{ - //printf("FilterDlg::OnOK\n"); - ExchangeData(EXCHANGE_DATA_OUT, true); - this->EndModal(wxID_OK); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void FilterDlg::OnClose(wxCloseEvent& event) -{ - this->EndModal(wxID_OK); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void FilterDlg::OnInitDialog(wxInitDialogEvent& event) -{ - //printf("FilterDlg::OnInitDialog\n"); - ExchangeData(EXCHANGE_DATA_IN, false); - //printf("m_beta: %f\n", m_beta); -} - -void FilterDlg::setBeta(void) { - wxString buf; - buf.Printf(wxT("%3.2f"), m_beta); - m_staticTextBeta->SetLabel(buf); - int slider = (int)(m_beta*SLIDER_MAX + 0.5); - m_codec2LPCPostFilterBeta->SetValue(slider); -} - -void FilterDlg::setCodec2(void) { - if (m_running) { - codec2_set_lpc_post_filter(freedv_get_codec2(g_pfreedv), - m_codec2LPCPostFilterEnable->GetValue(), - m_codec2LPCPostFilterBassBoost->GetValue(), - m_beta, m_gamma); - } -} - -void FilterDlg::setGamma(void) { - wxString buf; - buf.Printf(wxT("%3.2f"), m_gamma); - m_staticTextGamma->SetLabel(buf); - int slider = (int)(m_gamma*SLIDER_MAX + 0.5); - m_codec2LPCPostFilterGamma->SetValue(slider); -} - -void FilterDlg::OnEnable(wxScrollEvent& event) { - setCodec2(); -} - -void FilterDlg::OnBassBoost(wxScrollEvent& event) { - setCodec2(); -} - -void FilterDlg::OnBetaScroll(wxScrollEvent& event) { - m_beta = (float)m_codec2LPCPostFilterBeta->GetValue()/SLIDER_MAX; - setBeta(); - setCodec2(); -} - -void FilterDlg::OnGammaScroll(wxScrollEvent& event) { - m_gamma = (float)m_codec2LPCPostFilterGamma->GetValue()/SLIDER_MAX; - setGamma(); - setCodec2(); -} - -// immediately change enable flags rather using ExchangeData() so we can switch on and off at run time - -void FilterDlg::OnSpeexppEnable(wxScrollEvent& event) { - wxGetApp().m_speexpp_enable = m_ckboxSpeexpp->GetValue(); -} - -void FilterDlg::OnMicInEnable(wxScrollEvent& event) { - wxGetApp().m_MicInEQEnable = m_MicInEnable->GetValue(); -} - -void FilterDlg::OnSpkOutEnable(wxScrollEvent& event) { - wxGetApp().m_SpkOutEQEnable = m_SpkOutEnable->GetValue(); - //printf("wxGetApp().m_SpkOutEQEnable: %d\n", wxGetApp().m_SpkOutEQEnable); -} - -void FilterDlg::setFreq(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%3.0f"), eq->freqHz); - eq->valueFreq->SetLabel(buf); - int slider = (int)((eq->freqHz/eq->maxFreqHz)*SLIDER_MAX + 0.5); - eq->sliderFreq->SetValue(slider); -} - -void FilterDlg::sliderToFreq(EQ *eq, bool micIn) -{ - eq->freqHz = ((float)eq->sliderFreq->GetValue()/SLIDER_MAX)*eq->maxFreqHz; - if (eq->freqHz < 1.0) eq->freqHz = 1.0; // sox doesn't like 0 Hz; - setFreq(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } -} - -void FilterDlg::setGain(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%3.1f"), eq->gaindB); - eq->valueGain->SetLabel(buf); - int slider = (int)(((eq->gaindB-MIN_GAIN)/(MAX_GAIN-MIN_GAIN))*SLIDER_MAX + 0.5); - eq->sliderGain->SetValue(slider); -} - -void FilterDlg::sliderToGain(EQ *eq, bool micIn) -{ - float range = MAX_GAIN-MIN_GAIN; - - eq->gaindB = MIN_GAIN + range*((float)eq->sliderGain->GetValue()/SLIDER_MAX); - //printf("gaindB: %f\n", eq->gaindB); - setGain(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } - -} - -void FilterDlg::setQ(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%2.1f"), eq->Q); - eq->valueQ->SetLabel(buf); - - float log10_range = MAX_LOG10_Q - MIN_LOG10_Q; - - int slider = (int)(((log10(eq->Q+1E-6)-MIN_LOG10_Q)/log10_range)*SLIDER_MAX + 0.5); - eq->sliderQ->SetValue(slider); -} - -void FilterDlg::sliderToQ(EQ *eq, bool micIn) -{ - float log10_range = MAX_LOG10_Q - MIN_LOG10_Q; - - float sliderNorm = (float)eq->sliderQ->GetValue()/SLIDER_MAX; - float log10Q = MIN_LOG10_Q + sliderNorm*(log10_range); - eq->Q = pow(10.0, log10Q); - //printf("log10Q: %f eq->Q: %f\n", log10Q, eq->Q); - setQ(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } -} - -void FilterDlg::plotMicInFilterSpectrum(void) { - plotFilterSpectrum(&m_MicInBass, &m_MicInMid, &m_MicInTreble, m_MicInFreqRespPlot, m_MicInMagdB); -} - -void FilterDlg::plotSpkOutFilterSpectrum(void) { - plotFilterSpectrum(&m_SpkOutBass, &m_SpkOutMid, &m_SpkOutTreble, m_SpkOutFreqRespPlot, m_SpkOutMagdB); -} - -void FilterDlg::adjRunTimeMicInFilter(void) { - // signal an adjustment in running filter coeffs - - if (m_running) { - ExchangeData(EXCHANGE_DATA_OUT, false); - *m_newMicInFilter = true; - } -} - -void FilterDlg::adjRunTimeSpkOutFilter(void) { - // signal an adjustment in running filter coeffs - - if (m_running) { - ExchangeData(EXCHANGE_DATA_OUT, false); - *m_newSpkOutFilter = true; - } -} - - -void FilterDlg::plotFilterSpectrum(EQ *eqBass, EQ *eqMid, EQ *eqTreble, PlotSpectrum* freqRespPlot, float *magdB) { - char *argBass[10]; - char *argTreble[10]; - char *argMid[10]; - char argstorage[10][80]; - float magBass[F_MAG_N]; - float magTreble[F_MAG_N]; - float magMid[F_MAG_N]; - int i; - - for(i=0; i<10; i++) { - argBass[i] = &argstorage[i][0]; - argTreble[i] = &argstorage[i][0]; - argMid[i] = &argstorage[i][0]; - } - sprintf(argBass[0], "bass"); - sprintf(argBass[1], "%f", eqBass->gaindB+1E-6); - sprintf(argBass[2], "%f", eqBass->freqHz); - - calcFilterSpectrum(magBass, 2, argBass); - - sprintf(argTreble[0], "treble"); - sprintf(argTreble[1], "%f", eqTreble->gaindB+1E-6); - sprintf(argTreble[2], "%f", eqTreble->freqHz); - - calcFilterSpectrum(magTreble, 2, argTreble); - - sprintf(argTreble[0], "equalizer"); - sprintf(argTreble[1], "%f", eqMid->freqHz); - sprintf(argTreble[2], "%f", eqMid->Q); - sprintf(argTreble[3], "%f", eqMid->gaindB+1E-6); - - calcFilterSpectrum(magMid, 3, argMid); - - for(i=0; im_newdata = true; - freqRespPlot->Refresh(); -} - -void FilterDlg::calcFilterSpectrum(float magdB[], int argc, char *argv[]) { - void *sbq; - short in[NIMP]; - short out[NIMP]; - COMP X[F_MAG_N]; - float f, w; - int i, k; - - // find impulse response ----------------------------------- - - for(i=0; i. -// -//========================================================================== - -#ifndef __FILTER_DIALOG__ -#define __FILTER_DIALOG__ - -#include "fdmdv2_main.h" - -enum {disableQ = false, enableQ = true}; - -typedef struct { - wxSlider *sliderFreq; - wxStaticText *valueFreq; - wxSlider *sliderGain; - wxStaticText *valueGain; - wxSlider *sliderQ; - wxStaticText *valueQ; - - int sliderFreqId; - int sliderGainId; - int sliderQId; - - float freqHz; - float gaindB; - float Q; - - float maxFreqHz; -} EQ; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class FilterDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class FilterDlg : public wxDialog -{ - public: - FilterDlg( wxWindow* parent, bool running, bool *newMicInFilter, bool *newSpkOutFilter, - wxWindowID id = wxID_ANY, const wxString& title = _("Filter"), - const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 800, 630 ), - long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~FilterDlg(); - - void ExchangeData(int inout, bool storePersistent); - - protected: - // Handlers for events. - void OnCancel(wxCommandEvent& event); - void OnOK(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void OnInitDialog(wxInitDialogEvent& event); - void OnLPCPostFilterDefault(wxCommandEvent& event); - - void OnBetaScroll(wxScrollEvent& event); - void OnGammaScroll(wxScrollEvent& event); - void OnEnable(wxScrollEvent& event); - void OnBassBoost(wxScrollEvent& event); - - void OnSpeexppEnable(wxScrollEvent& event); - - void OnMicInBassFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInBass, true); } - void OnMicInBassGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInBass, true); } - void OnMicInTrebleFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInTreble, true); } - void OnMicInTrebleGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInTreble, true); } - void OnMicInMidFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInMid, true); } - void OnMicInMidGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInMid, true); } - void OnMicInMidQScroll(wxScrollEvent& event) { sliderToQ(&m_MicInMid, true); } - void OnMicInEnable(wxScrollEvent& event); - void OnMicInDefault(wxCommandEvent& event); - - void OnSpkOutBassFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutBass, false); } - void OnSpkOutBassGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutBass, false); } - void OnSpkOutTrebleFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutTreble, false); } - void OnSpkOutTrebleGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutTreble, false); } - void OnSpkOutMidFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutMid, false); } - void OnSpkOutMidGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutMid, false); } - void OnSpkOutMidQScroll(wxScrollEvent& event) { sliderToQ(&m_SpkOutMid, false); } - void OnSpkOutEnable(wxScrollEvent& event); - void OnSpkOutDefault(wxCommandEvent& event); - - wxStaticText* m_staticText8; - wxCheckBox* m_codec2LPCPostFilterEnable; - wxStaticText* m_staticText9; - wxCheckBox* m_codec2LPCPostFilterBassBoost; - wxStaticText* m_staticText91; - wxSlider* m_codec2LPCPostFilterBeta; - wxStaticText* m_staticTextBeta; - wxStaticText* m_staticText911; - wxSlider* m_codec2LPCPostFilterGamma; - wxStaticText* m_staticTextGamma; - wxButton* m_LPCPostFilterDefault; - - wxCheckBox* m_ckboxSpeexpp; - - wxStdDialogButtonSizer* m_sdbSizer5; - wxButton* m_sdbSizer5OK; - wxButton* m_sdbSizer5Cancel; - PlotSpectrum* m_MicInFreqRespPlot; - PlotSpectrum* m_SpkOutFreqRespPlot; - - wxCheckBox* m_MicInEnable; - wxButton* m_MicInDefault; - wxCheckBox* m_SpkOutEnable; - wxButton* m_SpkOutDefault; - - float *m_MicInMagdB; - float *m_SpkOutMagdB; - - private: - bool m_running; - float m_beta; - float m_gamma; - - void setBeta(void); // sets slider and static text from m_beta - void setGamma(void); // sets slider and static text from m_gamma - void setCodec2(void); - - void newEQControl(wxSlider** slider, wxStaticText** value, wxStaticBoxSizer *bs, wxString controlName); - EQ newEQ(wxSizer *bs, wxString eqName, float maxFreqHz, bool enableQ); - void newLPCPFControl(wxSlider **slider, wxStaticText **stValue, wxSizer *sbs, wxString controlName); - wxAuiNotebook *m_auiNotebook; - void setFreq(EQ *eq); - void setGain(EQ *eq); - void setQ(EQ *eq); - void sliderToFreq(EQ *eq, bool micIn); - void sliderToGain(EQ *eq, bool micIn); - void sliderToQ(EQ *eq, bool micIn); - void plotFilterSpectrum(EQ *eqBass, EQ *eqMid, EQ* eqTreble, PlotSpectrum* freqRespPlot, float *magdB); - void calcFilterSpectrum(float magdB[], int arc, char *argv[]); - void plotMicInFilterSpectrum(void); - void plotSpkOutFilterSpectrum(void); - void adjRunTimeMicInFilter(void); - void adjRunTimeSpkOutFilter(void); - - EQ m_MicInBass; - EQ m_MicInMid; - EQ m_MicInTreble; - - EQ m_SpkOutBass; - EQ m_SpkOutMid; - EQ m_SpkOutTreble; - - float limit(float value, float min, float max); - - bool *m_newMicInFilter; - bool *m_newSpkOutFilter; - -}; - -#endif // __FILTER_DIALOG__ diff --git a/freedv/branches/1.2/freedv-dev/src/dlg_options.cpp b/freedv/branches/1.2/freedv-dev/src/dlg_options.cpp deleted file mode 100644 index 79b6c87f..00000000 --- a/freedv/branches/1.2/freedv-dev/src/dlg_options.cpp +++ /dev/null @@ -1,616 +0,0 @@ -//========================================================================== -// Name: dlg_options.cpp -// Purpose: Dialog for controlling misc FreeDV options -// Date: May 24 2013 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "dlg_options.h" - -extern bool g_modal; -extern struct freedv *g_pfreedv; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class OptionsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -OptionsDlg::OptionsDlg(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* bSizer30; - bSizer30 = new wxBoxSizer(wxVERTICAL); - - //------------------------------ - // Txt Msg Text Box - //------------------------------ - - wxStaticBoxSizer* sbSizer_callSign; - wxStaticBox *sb_textMsg = new wxStaticBox(this, wxID_ANY, _("Txt Msg")); - sbSizer_callSign = new wxStaticBoxSizer(sb_textMsg, wxVERTICAL); - - m_txtCtrlCallSign = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - m_txtCtrlCallSign->SetToolTip(_("Txt Msg you can send along with Voice")); - sbSizer_callSign->Add(m_txtCtrlCallSign, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 3); - - bSizer30->Add(sbSizer_callSign,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //---------------------------------------------------------------------- - // Voice Keyer - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer28a = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Voice Keyer")), wxHORIZONTAL); - - wxStaticText *m_staticText28b = new wxStaticText(this, wxID_ANY, _("Wave File: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28b, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtCtrlVoiceKeyerWaveFile = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300,-1), 0); - m_txtCtrlVoiceKeyerWaveFile->SetToolTip(_("Wave file to play for Voice Keyer")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerWaveFile, 0, 0, 5); - - m_buttonChooseVoiceKeyerWaveFile = new wxButton(this, wxID_APPLY, _("Choose"), wxDefaultPosition, wxSize(-1,-1), 0); - staticBoxSizer28a->Add(m_buttonChooseVoiceKeyerWaveFile, 0, wxALIGN_CENTER_VERTICAL, 5); - - wxStaticText *m_staticText28c = new wxStaticText(this, wxID_ANY, _(" Rx Pause: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28c, 0, wxALIGN_CENTER_VERTICAL , 5); - m_txtCtrlVoiceKeyerRxPause = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0); - m_txtCtrlVoiceKeyerRxPause->SetToolTip(_("How long to wait in Rx mode before repeat")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerRxPause, 0, 0, 5); - - wxStaticText *m_staticText28d = new wxStaticText(this, wxID_ANY, _(" Repeats: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28d, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtCtrlVoiceKeyerRepeats = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0); - m_txtCtrlVoiceKeyerRepeats->SetToolTip(_("How long to wait in Rx mode before repeat")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerRepeats, 0, 0, 5); - - bSizer30->Add(staticBoxSizer28a,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - -#ifdef __WXMSW__ - //------------------------------ - // debug console, for WIndows build make console pop up for debug messages - //------------------------------ - - wxStaticBoxSizer* sbSizer_console; - wxStaticBox *sb_console = new wxStaticBox(this, wxID_ANY, _("Debug")); - sbSizer_console = new wxStaticBoxSizer(sb_console, wxHORIZONTAL); - - m_ckboxDebugConsole = new wxCheckBox(this, wxID_ANY, _("Show Console"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_console->Add(m_ckboxDebugConsole, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_console,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); -#endif - - //------------------------------ - // FreeDV 700 Options - //------------------------------ - - wxStaticBoxSizer* sbSizer_freedv700; - wxStaticBox *sb_freedv700 = new wxStaticBox(this, wxID_ANY, _("FreeDV 700 Options")); - sbSizer_freedv700 = new wxStaticBoxSizer(sb_freedv700, wxHORIZONTAL); - - m_ckboxFreeDV700txClip = new wxCheckBox(this, wxID_ANY, _("Clipping"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_freedv700->Add(m_ckboxFreeDV700txClip, 0, wxALIGN_LEFT, 0); - m_ckboxFreeDV700Combine = new wxCheckBox(this, wxID_ANY, _("Diversity Combine for plots"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_freedv700->Add(m_ckboxFreeDV700Combine, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_freedv700, 0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Half/Full duplex selection - //------------------------------ - - wxStaticBox *sb_duplex = new wxStaticBox(this, wxID_ANY, _("Half/Full Duplex Operation")); - wxStaticBoxSizer* sbSizer_duplex = new wxStaticBoxSizer(sb_duplex, wxHORIZONTAL); - m_ckHalfDuplex = new wxCheckBox(this, wxID_ANY, _("Half Duplex"), wxDefaultPosition, wxSize(-1,-1), 0); - sbSizer_duplex->Add(m_ckHalfDuplex, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5); - bSizer30->Add(sbSizer_duplex,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Test Frames/Channel simulation check box - //------------------------------ - - wxStaticBoxSizer* sbSizer_testFrames; - wxStaticBox *sb_testFrames = new wxStaticBox(this, wxID_ANY, _("Testing and Channel Simulation")); - sbSizer_testFrames = new wxStaticBoxSizer(sb_testFrames, wxHORIZONTAL); - - m_ckboxTestFrame = new wxCheckBox(this, wxID_ANY, _("Test Frames"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxTestFrame, 0, wxALIGN_LEFT, 0); - - m_ckboxChannelNoise = new wxCheckBox(this, wxID_ANY, _("Channel Noise SNR (dB):"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxChannelNoise, 0, wxALIGN_LEFT, 0); - m_txtNoiseSNR = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_testFrames->Add(m_txtNoiseSNR, 0, wxALIGN_LEFT, 0); - - m_ckboxAttnCarrierEn = new wxCheckBox(this, wxID_ANY, _("Attn Carrier Carrier:"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxAttnCarrierEn, 0, wxALIGN_LEFT, 0); - m_txtAttnCarrier = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_testFrames->Add(m_txtAttnCarrier, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_testFrames,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Interfering tone - //------------------------------ - - wxStaticBoxSizer* sbSizer_tone; - wxStaticBox *sb_tone = new wxStaticBox(this, wxID_ANY, _("Simulated Interference Tone")); - sbSizer_tone = new wxStaticBoxSizer(sb_tone, wxHORIZONTAL); - - m_ckboxTone = new wxCheckBox(this, wxID_ANY, _("Tone Freq (Hz):"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_tone->Add(m_ckboxTone, 0, wxALIGN_LEFT, 0); - m_txtToneFreqHz = new wxTextCtrl(this, wxID_ANY, "1000", wxDefaultPosition, wxSize(60,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_tone->Add(m_txtToneFreqHz, 0, wxALIGN_LEFT, 0); - wxStaticText *m_staticTextta = new wxStaticText(this, wxID_ANY, _(" Amplitude (pk): "), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_tone->Add(m_staticTextta, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtToneAmplitude = new wxTextCtrl(this, wxID_ANY, "1000", wxDefaultPosition, wxSize(60,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_tone->Add(m_txtToneAmplitude, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_tone,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - -#ifdef __EXPERIMENTAL_UDP__ - //------------------------------ - // Txt Encoding - //------------------------------ - - wxStaticBoxSizer* sbSizer_encoding = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Text Encoding")), wxHORIZONTAL); - -#ifdef SHORT_VARICODE - m_rb_textEncoding1 = new wxRadioButton( this, wxID_ANY, wxT("Long varicode"), wxDefaultPosition, wxDefaultSize, 0); - m_rb_textEncoding1->SetValue(true); - sbSizer_encoding->Add(m_rb_textEncoding1, 0, wxALIGN_LEFT|wxALL, 1); - m_rb_textEncoding2 = new wxRadioButton( this, wxID_ANY, wxT("Short Varicode"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_encoding->Add(m_rb_textEncoding2, 0, wxALIGN_LEFT|wxALL, 1); -#endif - - m_ckboxEnableChecksum = new wxCheckBox(this, wxID_ANY, _("Use Checksum on Rx"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_encoding->Add(m_ckboxEnableChecksum, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_encoding,0, wxALL|wxEXPAND, 3); - - //------------------------------ - // Event processing - //------------------------------ - - wxStaticBoxSizer* sbSizer_events; - wxStaticBox *sb_events = new wxStaticBox(this, wxID_ANY, _("Event Processing")); - sbSizer_events = new wxStaticBoxSizer(sb_events, wxVERTICAL); - - // event processing enable and spam timer - - wxStaticBoxSizer* sbSizer_events_top; - wxStaticBox* sb_events1 = new wxStaticBox(this, wxID_ANY, _("")); - sbSizer_events_top = new wxStaticBoxSizer(sb_events1, wxHORIZONTAL); - - m_ckbox_events = new wxCheckBox(this, wxID_ANY, _("Enable System Calls Syscall Spam Timer"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_events->SetToolTip(_("Enable processing of events and generation of system calls")); - sbSizer_events_top->Add(m_ckbox_events, 0, 0, 5); - m_txt_spam_timer = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - m_txt_spam_timer->SetToolTip(_("Many matching events can cause a flood of syscalls. Set minimum time (seconds) between syscalls for each event here")); - sbSizer_events_top->Add(m_txt_spam_timer, 0, 0, 5); - m_rb_spam_timer = new wxRadioButton( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - m_rb_spam_timer->SetForegroundColour( wxColour(0, 255, 0 ) ); - sbSizer_events_top->Add(m_rb_spam_timer, 0, 0, 10); - sbSizer_events->Add(sbSizer_events_top, 0, 0, 5); - - // list of regexps - - wxStaticBoxSizer* sbSizer_regexp = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Regular Expressions to Process Events")), wxHORIZONTAL); - m_txt_events_regexp_match = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,100), wxTE_MULTILINE); - m_txt_events_regexp_match->SetToolTip(_("Enter regular expressions to match events")); - sbSizer_regexp->Add(m_txt_events_regexp_match, 1, wxEXPAND, 5); - m_txt_events_regexp_replace = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,100), wxTE_MULTILINE); - m_txt_events_regexp_replace->SetToolTip(_("Enter regular expressions to replace events")); - sbSizer_regexp->Add(m_txt_events_regexp_replace, 1, wxEXPAND, 5); - sbSizer_events->Add(sbSizer_regexp, 1, wxEXPAND, 5); - - // log of events and responses - - wxStaticBoxSizer* sbSizer_event_log = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Log of Events and Responses")), wxVERTICAL); - wxBoxSizer* bSizer33 = new wxBoxSizer(wxHORIZONTAL); - m_txt_events_in = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,50), wxTE_MULTILINE | wxTE_READONLY); - bSizer33->Add(m_txt_events_in, 1, wxEXPAND, 5); - m_txt_events_out = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,50), wxTE_MULTILINE | wxTE_READONLY); - bSizer33->Add(m_txt_events_out, 1, wxEXPAND, 5); - sbSizer_event_log->Add(bSizer33, 1, wxEXPAND, 5); - sbSizer_events->Add(sbSizer_event_log, 1, wxEXPAND, 5); - - bSizer30->Add(sbSizer_events,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // UDP control port - //------------------------------ - - wxStaticBoxSizer* sbSizer_udp; - wxStaticBox* sb_udp = new wxStaticBox(this, wxID_ANY, _("UDP Control Port")); - sbSizer_udp = new wxStaticBoxSizer(sb_udp, wxHORIZONTAL); - m_ckbox_udp_enable = new wxCheckBox(this, wxID_ANY, _("Enable UDP Control Port UDP Port Number:"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_udp->SetToolTip(_("Enable control of FreeDV via UDP port")); - sbSizer_udp->Add(m_ckbox_udp_enable, 0, wxALIGN_CENTER_HORIZONTAL, 5); - m_txt_udp_port = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(50,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_udp->Add(m_txt_udp_port, 0, wxALIGN_CENTER_HORIZONTAL, 5); - - bSizer30->Add(sbSizer_udp,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); -#endif - - //------------------------------ - // OK - Cancel - Apply Buttons - //------------------------------ - - wxBoxSizer* bSizer31 = new wxBoxSizer(wxHORIZONTAL); - - m_sdbSizer5OK = new wxButton(this, wxID_OK); - bSizer31->Add(m_sdbSizer5OK, 0, wxALL, 2); - - m_sdbSizer5Cancel = new wxButton(this, wxID_CANCEL); - bSizer31->Add(m_sdbSizer5Cancel, 0, wxALL, 2); - - m_sdbSizer5Apply = new wxButton(this, wxID_APPLY); - bSizer31->Add(m_sdbSizer5Apply, 0, wxALL, 2); - - bSizer30->Add(bSizer31, 0, wxALIGN_CENTER, 0); - - this->SetSizer(bSizer30); - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - this->Layout(); - - this->Centre(wxBOTH); - - // Connect Events ------------------------------------------------------- - - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(OptionsDlg::OnInitDialog)); - - m_sdbSizer5OK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnOK), NULL, this); - m_sdbSizer5Cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnCancel), NULL, this); - m_sdbSizer5Apply->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnApply), NULL, this); - - m_ckboxTestFrame->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnTestFrame), NULL, this); - m_ckboxChannelNoise->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnChannelNoise), NULL, this); - m_ckboxAttnCarrierEn->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnAttnCarrierEn), NULL, this); - - m_ckboxFreeDV700txClip->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700txClip), NULL, this); - m_ckboxFreeDV700Combine->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700Combine), NULL, this); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnDebugConsole), NULL, this); -#endif - - m_buttonChooseVoiceKeyerWaveFile->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnChooseVoiceKeyerWaveFile), NULL, this); - - event_in_serial = 0; - event_out_serial = 0; -} - -//------------------------------------------------------------------------- -// ~OptionsDlg() -//------------------------------------------------------------------------- -OptionsDlg::~OptionsDlg() -{ - - // Disconnect Events - - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(OptionsDlg::OnInitDialog)); - - m_sdbSizer5OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnOK), NULL, this); - m_sdbSizer5Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnCancel), NULL, this); - m_sdbSizer5Apply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnApply), NULL, this); - - m_ckboxTestFrame->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnTestFrame), NULL, this); - m_ckboxChannelNoise->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnChannelNoise), NULL, this); - m_ckboxAttnCarrierEn->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnAttnCarrierEn), NULL, this); - - m_ckboxFreeDV700txClip->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700txClip), NULL, this); - m_ckboxFreeDV700Combine->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700Combine), NULL, this); - m_buttonChooseVoiceKeyerWaveFile->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnChooseVoiceKeyerWaveFile), NULL, this); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnDebugConsole), NULL, this); -#endif -} - - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void OptionsDlg::ExchangeData(int inout, bool storePersistent) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - - if(inout == EXCHANGE_DATA_IN) - { - m_txtCtrlCallSign->SetValue(wxGetApp().m_callSign); - - /* Voice Keyer */ - - m_txtCtrlVoiceKeyerWaveFile->SetValue(wxGetApp().m_txtVoiceKeyerWaveFile); - m_txtCtrlVoiceKeyerRxPause->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intVoiceKeyerRxPause)); - m_txtCtrlVoiceKeyerRepeats->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intVoiceKeyerRepeats)); - - m_ckHalfDuplex->SetValue(wxGetApp().m_boolHalfDuplex); - - m_ckboxTestFrame->SetValue(wxGetApp().m_testFrames); - - m_ckboxChannelNoise->SetValue(wxGetApp().m_channel_noise); - m_txtNoiseSNR->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_noise_snr)); - - m_ckboxTone->SetValue(wxGetApp().m_tone); - m_txtToneFreqHz->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_tone_freq_hz)); - m_txtToneAmplitude->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_tone_amplitude)); - - m_ckboxAttnCarrierEn->SetValue(wxGetApp().m_attn_carrier_en); - m_txtAttnCarrier->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_attn_carrier)); - -#ifdef __EXPERIMENTAL_UDP__ - m_ckbox_events->SetValue(wxGetApp().m_events); - m_txt_spam_timer->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_events_spam_timer)); - - m_txt_events_regexp_match->SetValue(wxGetApp().m_events_regexp_match); - m_txt_events_regexp_replace->SetValue(wxGetApp().m_events_regexp_replace); - - m_ckbox_udp_enable->SetValue(wxGetApp().m_udp_enable); - m_txt_udp_port->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_udp_port)); - -#ifdef SHORT_VARICODE - if (wxGetApp().m_textEncoding == 1) - m_rb_textEncoding1->SetValue(true); - if (wxGetApp().m_textEncoding == 2) - m_rb_textEncoding2->SetValue(true); -#endif - m_ckboxEnableChecksum->SetValue(wxGetApp().m_enable_checksum); -#endif - - m_ckboxFreeDV700txClip->SetValue(wxGetApp().m_FreeDV700txClip); - m_ckboxFreeDV700Combine->SetValue(wxGetApp().m_FreeDV700Combine); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->SetValue(wxGetApp().m_debug_console); -#endif - } - - if(inout == EXCHANGE_DATA_OUT) - { - wxGetApp().m_callSign = m_txtCtrlCallSign->GetValue(); - - wxGetApp().m_boolHalfDuplex = m_ckHalfDuplex->GetValue(); - pConfig->Write(wxT("/Rig/HalfDuplex"), wxGetApp().m_boolHalfDuplex); - - /* Voice Keyer */ - - wxGetApp().m_txtVoiceKeyerWaveFile = m_txtCtrlVoiceKeyerWaveFile->GetValue(); - pConfig->Write(wxT("/VoiceKeyer/WaveFile"), wxGetApp().m_txtVoiceKeyerWaveFile); - long tmp; - m_txtCtrlVoiceKeyerRxPause->GetValue().ToLong(&tmp); if (tmp < 0) tmp = 0; wxGetApp().m_intVoiceKeyerRxPause = (int)tmp; - pConfig->Write(wxT("/VoiceKeyer/RxPause"), wxGetApp().m_intVoiceKeyerRxPause); - m_txtCtrlVoiceKeyerRepeats->GetValue().ToLong(&tmp); - if (tmp < 0) tmp = 0; if (tmp > 100) tmp = 100; - wxGetApp().m_intVoiceKeyerRepeats = (int)tmp; - pConfig->Write(wxT("/VoiceKeyer/Repeats"), wxGetApp().m_intVoiceKeyerRepeats); - - wxGetApp().m_testFrames = m_ckboxTestFrame->GetValue(); - - wxGetApp().m_channel_noise = m_ckboxChannelNoise->GetValue(); - long noise_snr; - m_txtNoiseSNR->GetValue().ToLong(&noise_snr); - wxGetApp().m_noise_snr = (int)noise_snr; - - wxGetApp().m_tone = m_ckboxTone->GetValue(); - long tone_freq_hz, tone_amplitude; - m_txtToneFreqHz->GetValue().ToLong(&tone_freq_hz); - wxGetApp().m_tone_freq_hz = (int)tone_freq_hz; - m_txtToneAmplitude->GetValue().ToLong(&tone_amplitude); - wxGetApp().m_tone_amplitude = (int)tone_amplitude; - - wxGetApp().m_attn_carrier_en = m_ckboxAttnCarrierEn->GetValue(); - long attn_carrier; - m_txtAttnCarrier->GetValue().ToLong(&attn_carrier); - wxGetApp().m_attn_carrier = (int)attn_carrier; - -#ifdef __EXPERIMENTAL_UDP__ - wxGetApp().m_events = m_ckbox_events->GetValue(); - long spam_timer; - m_txt_spam_timer->GetValue().ToLong(&spam_timer); - wxGetApp().m_events_spam_timer = (int)spam_timer; - - // make sure regexp lists are terminated by a \n - - if (m_txt_events_regexp_match->GetValue().Last() != '\n') { - m_txt_events_regexp_match->SetValue(m_txt_events_regexp_match->GetValue()+'\n'); - } - if (m_txt_events_regexp_replace->GetValue().Last() != '\n') { - m_txt_events_regexp_replace->SetValue(m_txt_events_regexp_replace->GetValue()+'\n'); - } - wxGetApp().m_events_regexp_match = m_txt_events_regexp_match->GetValue(); - wxGetApp().m_events_regexp_replace = m_txt_events_regexp_replace->GetValue(); - - wxGetApp().m_udp_enable = m_ckbox_udp_enable->GetValue(); - long port; - m_txt_udp_port->GetValue().ToLong(&port); - wxGetApp().m_udp_port = (int)port; - -#ifdef SHORT_VARICODE - if (m_rb_textEncoding1->GetValue()) - wxGetApp().m_textEncoding = 1; - if (m_rb_textEncoding2->GetValue()) - wxGetApp().m_textEncoding = 2; -#endif - wxGetApp().m_enable_checksum = m_ckboxEnableChecksum->GetValue(); -#endif - - wxGetApp().m_FreeDV700txClip = m_ckboxFreeDV700txClip->GetValue(); - wxGetApp().m_FreeDV700Combine = m_ckboxFreeDV700Combine->GetValue(); - -#ifdef __WXMSW__ - wxGetApp().m_debug_console = m_ckboxDebugConsole->GetValue(); -#endif - - if (storePersistent) { - pConfig->Write(wxT("/Data/CallSign"), wxGetApp().m_callSign); -#ifdef SHORT_VARICODE - pConfig->Write(wxT("/Data/TextEncoding"), wxGetApp().m_textEncoding); -#endif - pConfig->Write(wxT("/Data/EnableChecksumOnMsgRx"), wxGetApp().m_enable_checksum); - - pConfig->Write(wxT("/Events/enable"), wxGetApp().m_events); - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - pConfig->Write(wxT("/Events/regexp_match"), wxGetApp().m_events_regexp_match); - pConfig->Write(wxT("/Events/regexp_replace"), wxGetApp().m_events_regexp_replace); - - pConfig->Write(wxT("/UDP/enable"), wxGetApp().m_udp_enable); - pConfig->Write(wxT("/UDP/port"), wxGetApp().m_udp_port); - - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - - pConfig->Write(wxT("/FreeDV700/txClip"), wxGetApp().m_FreeDV700txClip); - - pConfig->Write(wxT("/Noise/noise_snr"), wxGetApp().m_noise_snr); - -#ifdef __WXMSW__ - pConfig->Write(wxT("/Debug/console"), wxGetApp().m_debug_console); -#endif - - pConfig->Flush(); - } - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -//------------------------------------------------------------------------- -// OnOK() -//------------------------------------------------------------------------- -void OptionsDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT, true); - //this->EndModal(wxID_OK); - g_modal = false; - this->Show(false); -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void OptionsDlg::OnCancel(wxCommandEvent& event) -{ - //this->EndModal(wxID_CANCEL); - g_modal = false; - this->Show(false); -} - -//------------------------------------------------------------------------- -// OnApply() -//------------------------------------------------------------------------- -void OptionsDlg::OnApply(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT, true); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void OptionsDlg::OnInitDialog(wxInitDialogEvent& event) -{ - ExchangeData(EXCHANGE_DATA_IN, false); -} - -// immediately change flags rather using ExchangeData() so we can switch on and off at run time - -void OptionsDlg::OnTestFrame(wxScrollEvent& event) { - wxGetApp().m_testFrames = m_ckboxTestFrame->GetValue(); -} - -void OptionsDlg::OnChannelNoise(wxScrollEvent& event) { - wxGetApp().m_channel_noise = m_ckboxChannelNoise->GetValue(); -} - - -void OptionsDlg::OnChooseVoiceKeyerWaveFile(wxCommandEvent& event) { - wxFileDialog openFileDialog( - this, - wxT("Voice Keyer wave file"), - wxGetApp().m_txtVoiceKeyerWaveFilePath, - wxEmptyString, - wxT("WAV files (*.wav)|*.wav"), - wxFD_OPEN - ); - if(openFileDialog.ShowModal() == wxID_CANCEL) { - return; // the user changed their mind... - } - - wxString fileName, extension; - wxGetApp().m_txtVoiceKeyerWaveFile = openFileDialog.GetPath(); - wxFileName::SplitPath(wxGetApp().m_txtVoiceKeyerWaveFile, &wxGetApp().m_txtVoiceKeyerWaveFilePath, &fileName, &extension); - m_txtCtrlVoiceKeyerWaveFile->SetValue(wxGetApp().m_txtVoiceKeyerWaveFile); -} - -// Run time update of carrier amplitude attenuation - -void OptionsDlg::OnAttnCarrierEn(wxScrollEvent& event) { - long attn_carrier; - m_txtAttnCarrier->GetValue().ToLong(&attn_carrier); - wxGetApp().m_attn_carrier = (int)attn_carrier; - - /* uncheck -> checked, attenuate selected carrier */ - - if (m_ckboxAttnCarrierEn->GetValue() && !wxGetApp().m_attn_carrier_en) { - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C) { - freedv_set_carrier_ampl(g_pfreedv, wxGetApp().m_attn_carrier, 0.25); - } else { - wxMessageBox("Carrier attenuation feature only works on 700C", wxT("Warning"), wxOK | wxICON_WARNING, this); - } - } - - /* checked -> unchecked, reset selected carrier */ - - if (!m_ckboxAttnCarrierEn->GetValue() && wxGetApp().m_attn_carrier_en) { - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C) { - freedv_set_carrier_ampl(g_pfreedv, wxGetApp().m_attn_carrier, 1.0); - } - } - - wxGetApp().m_attn_carrier_en = m_ckboxAttnCarrierEn->GetValue(); -} - -void OptionsDlg::OnFreeDV700txClip(wxScrollEvent& event) { - wxGetApp().m_FreeDV700txClip = m_ckboxFreeDV700txClip->GetValue(); -} - -void OptionsDlg::OnFreeDV700Combine(wxScrollEvent& event) { - wxGetApp().m_FreeDV700Combine = m_ckboxFreeDV700Combine->GetValue(); -} - -void OptionsDlg::updateEventLog(wxString event_in, wxString event_out) { - wxString event_in_with_serial, event_out_with_serial; - event_in_with_serial.Printf(_T("[%d] %s"), event_in_serial++, event_in); - event_out_with_serial.Printf(_T("[%d] %s"), event_out_serial++, event_out); - - m_txt_events_in->AppendText(event_in_with_serial+"\n"); - m_txt_events_out->AppendText(event_out_with_serial+"\n"); -} - - -void OptionsDlg::OnDebugConsole(wxScrollEvent& event) { - wxGetApp().m_debug_console = m_ckboxDebugConsole->GetValue(); -#ifdef __WXMSW__ - // somewhere to send printfs while developing, causes conmsole to pop up on Windows - if (wxGetApp().m_debug_console) { - int ret = AllocConsole(); - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - fprintf(stderr, "AllocConsole: %d m_debug_console: %d\n", ret, wxGetApp().m_debug_console); - } -#endif -} diff --git a/freedv/branches/1.2/freedv-dev/src/dlg_options.h b/freedv/branches/1.2/freedv-dev/src/dlg_options.h deleted file mode 100644 index 081448cb..00000000 --- a/freedv/branches/1.2/freedv-dev/src/dlg_options.h +++ /dev/null @@ -1,137 +0,0 @@ -//========================================================================== -// Name: dlg_options.h -// Purpose: Dialog for controlling misc FreeDV options -// Created: Nov 25 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#ifndef __OPTIONS_DIALOG__ -#define __OPTIONS_DIALOG__ - -#include "fdmdv2_main.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class OptionsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class OptionsDlg : public wxDialog -{ - public: - OptionsDlg( wxWindow* parent, - wxWindowID id = wxID_ANY, const wxString& title = _("Options"), - const wxPoint& pos = wxDefaultPosition, -#ifdef __WXMSW__ - /* we add debug console check box for windows */ - const wxSize& size = wxSize(600,410), -#else - const wxSize& size = wxSize(600,380), -#endif - long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~OptionsDlg(); - - void ExchangeData(int inout, bool storePersistent); - void updateEventLog(wxString event_in, wxString event_out); - - bool enableEventsChecked() {return m_ckbox_events->GetValue();} - - void SetSpamTimerLight(bool state) { - - // Colours don't work on Windows - - if (state) { - m_rb_spam_timer->SetForegroundColour( wxColour( 255,0 , 0 ) ); // red - m_rb_spam_timer->SetValue(true); - } - else { - m_rb_spam_timer->SetForegroundColour( wxColour( 0, 255, 0 ) ); // green - m_rb_spam_timer->SetValue(false); - } - } - - - protected: - - // Handlers for events. - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnApply(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void OnInitDialog(wxInitDialogEvent& event); - - void OnTestFrame(wxScrollEvent& event); - void OnChannelNoise(wxScrollEvent& event); - void OnAttnCarrierEn(wxScrollEvent& event); - void OnFreeDV700txClip(wxScrollEvent& event); - void OnFreeDV700Combine(wxScrollEvent& event); - void OnDebugConsole(wxScrollEvent& event); - - wxTextCtrl *m_txtCtrlCallSign; // TODO: this should be renamed to tx_txtmsg, and rename all related incl persis strge - - wxCheckBox* m_ckHalfDuplex; - - /* Voice Keyer */ - - wxButton *m_buttonChooseVoiceKeyerWaveFile; - wxTextCtrl *m_txtCtrlVoiceKeyerWaveFile; - wxTextCtrl *m_txtCtrlVoiceKeyerRxPause; - wxTextCtrl *m_txtCtrlVoiceKeyerRepeats; - - /* test frames, other simulated channel impairments */ - - wxCheckBox *m_ckboxTestFrame; - wxCheckBox *m_ckboxChannelNoise; - wxTextCtrl *m_txtNoiseSNR; - wxCheckBox *m_ckboxAttnCarrierEn; - wxTextCtrl *m_txtAttnCarrier; - - wxCheckBox *m_ckboxTone; - wxTextCtrl *m_txtToneFreqHz; - wxTextCtrl *m_txtToneAmplitude; - - wxCheckBox *m_ckboxFreeDV700txClip; - wxCheckBox *m_ckboxFreeDV700Combine; - - wxRadioButton *m_rb_textEncoding1; - wxRadioButton *m_rb_textEncoding2; - wxCheckBox *m_ckboxEnableChecksum; - - wxCheckBox *m_ckbox_events; - wxTextCtrl *m_txt_events_regexp_match; - wxTextCtrl *m_txt_events_regexp_replace; - wxTextCtrl *m_txt_events_in; - wxTextCtrl *m_txt_events_out; - wxTextCtrl *m_txt_spam_timer; - wxRadioButton *m_rb_spam_timer; - - wxCheckBox *m_ckbox_udp_enable; - wxTextCtrl *m_txt_udp_port; - - wxButton* m_sdbSizer5OK; - wxButton* m_sdbSizer5Cancel; - wxButton* m_sdbSizer5Apply; - - wxCheckBox *m_ckboxDebugConsole; - - unsigned int event_in_serial, event_out_serial; - - void OnChooseVoiceKeyerWaveFile(wxCommandEvent& event); - - private: -}; - -#endif // __OPTIONS_DIALOG__ diff --git a/freedv/branches/1.2/freedv-dev/src/dlg_plugin.cpp b/freedv/branches/1.2/freedv-dev/src/dlg_plugin.cpp deleted file mode 100644 index 68610f42..00000000 --- a/freedv/branches/1.2/freedv-dev/src/dlg_plugin.cpp +++ /dev/null @@ -1,148 +0,0 @@ -//========================================================================== -// Name: dlg_plugin.cpp -// Purpose: Subclasses dialog GUI for PlugIn Config. Creates simple -// wxWidgets dialog GUI to set a few text strings. -// Date: Jan 2016 -// Authors: David Rowe -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "dlg_plugin.h" -#include "fdmdv2_main.h" - -#ifdef __WIN32__ -#include -#endif -#if defined(__FreeBSD__) || defined(__WXOSX__) -#include -#include -#endif - -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlugInDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlugInDlg::PlugInDlg(const wxString& title, int numParam, wxString paramName[], wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - m_name = title; - m_numParam = numParam; - assert(m_numParam <= PLUGIN_MAX_PARAMS); - - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - this->SetSizer(mainSizer); - - int i; - for (i=0; iAdd(m_txtCtrlParam[i], 0, 0, 5); - mainSizer->Add(staticBoxSizer28a, 0, wxEXPAND, 5); - } - - //---------------------------------------------------------------------- - // OK - Cancel - Apply - //---------------------------------------------------------------------- - - wxBoxSizer* boxSizer12 = new wxBoxSizer(wxHORIZONTAL); - - m_buttonOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxSize(-1,-1), 0); - m_buttonOK->SetDefault(); - boxSizer12->Add(m_buttonOK, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonCancel, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - mainSizer->Add(boxSizer12, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL, 5); - - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - Centre(wxBOTH); - - // Connect events - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(PlugInDlg::OnInitDialog), NULL, this); - m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnOK), NULL, this); - m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnCancel), NULL, this); - -} - -//------------------------------------------------------------------------- -// ~PlugInDlg() -//------------------------------------------------------------------------- -PlugInDlg::~PlugInDlg() -{ - // Disconnect Events - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(PlugInDlg::OnInitDialog), NULL, this); - m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnOK), NULL, this); - m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnCancel), NULL, this); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void PlugInDlg::OnInitDialog(wxInitDialogEvent& event) -{ - ExchangeData(EXCHANGE_DATA_IN); -} - - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void PlugInDlg::ExchangeData(int inout) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - wxString str; - int i; - - if(inout == EXCHANGE_DATA_IN) - { - for (i=0; iSetValue(wxGetApp().m_txtPlugInParam[i]); - } - } - if(inout == EXCHANGE_DATA_OUT) - { - for (i=0; iGetValue(); - wxString configStr = "/" + m_name + "/" + m_paramName[i]; - pConfig->Write(configStr, wxGetApp().m_txtPlugInParam[i]); - } - pConfig->Flush(); - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void PlugInDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void PlugInDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - this->EndModal(wxID_OK); -} diff --git a/freedv/branches/1.2/freedv-dev/src/dlg_plugin.h b/freedv/branches/1.2/freedv-dev/src/dlg_plugin.h deleted file mode 100644 index 72efc7bb..00000000 --- a/freedv/branches/1.2/freedv-dev/src/dlg_plugin.h +++ /dev/null @@ -1,65 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.h -// Purpose: Subclasses dialog GUI for PTT Config. -// -// Created: May. 11, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __PLUGIN_DIALOG__ -#define __PLUGIN_DIALOG__ - -#include "fdmdv2_main.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlugInDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlugInDlg : public wxDialog -{ - public: - PlugInDlg(const wxString& title = _("PTT Config"), int numParam = 0, wxString paramNames[]=NULL, wxWindow* parent=NULL, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(450,300), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); - virtual ~PlugInDlg(); - void ExchangeData(int inout); - - protected: - wxString m_name; - int m_numParam; - wxString m_paramName[PLUGIN_MAX_PARAMS]; - - wxTextCtrl* m_txtCtrlParam[PLUGIN_MAX_PARAMS]; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - - -protected: - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - virtual void OnInitDialog(wxInitDialogEvent& event); -}; - -#endif // __PLUGIN_DIALOG__ diff --git a/freedv/branches/1.2/freedv-dev/src/dlg_ptt.cpp b/freedv/branches/1.2/freedv-dev/src/dlg_ptt.cpp deleted file mode 100644 index 1a04c9c4..00000000 --- a/freedv/branches/1.2/freedv-dev/src/dlg_ptt.cpp +++ /dev/null @@ -1,570 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.cpp -// Purpose: Subclasses dialog GUI for PTT Config. Creates simple -// wxWidgets dialog GUI to select real/virtual Comm ports. -// Date: May 11 2012 -// Authors: David Rowe, David Witten, Joel Stanley -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "dlg_ptt.h" -#include "fdmdv2_main.h" - -#ifdef __WIN32__ -#include -#endif -#if defined(__FreeBSD__) || defined(__WXOSX__) -#include -#include -#endif - -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class ComPortsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -ComPortsDlg::ComPortsDlg(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - this->SetSizer(mainSizer); - - //---------------------------------------------------------------------- - // Vox tone option - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer28 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("VOX PTT Settings")), wxHORIZONTAL); - m_ckLeftChannelVoxTone = new wxCheckBox(this, wxID_ANY, _("Left Channel Vox Tone"), wxDefaultPosition, wxSize(-1,-1), 0); - staticBoxSizer28->Add(m_ckLeftChannelVoxTone, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5); - - mainSizer->Add(staticBoxSizer28, 0, wxEXPAND, 5); - - //---------------------------------------------------------------------- - // Hamlib for CAT PTT - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer18 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Hamlib Settings")), wxHORIZONTAL); - wxGridSizer* gridSizerhl = new wxGridSizer(5, 2, 0, 0); - staticBoxSizer18->Add(gridSizerhl, 1, wxEXPAND|wxALIGN_LEFT, 5); - - /* Use Hamlib for PTT checkbox. */ - - m_ckUseHamlibPTT = new wxCheckBox(this, wxID_ANY, _("Use Hamlib PTT"), wxDefaultPosition, wxSize(-1, -1), 0); - m_ckUseHamlibPTT->SetValue(false); - gridSizerhl->Add(m_ckUseHamlibPTT, 0, wxALIGN_CENTER_VERTICAL, 0); - gridSizerhl->Add(new wxStaticText(this, -1, wxT("")), 0, wxEXPAND); - - /* Hamlib Rig Type combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Rig Model:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbRigName = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1), 0, NULL, wxCB_DROPDOWN); - wxGetApp().m_hamlib->populateComboBox(m_cbRigName); - m_cbRigName->SetSelection(wxGetApp().m_intHamlibRig); - gridSizerhl->Add(m_cbRigName, 0, wxALIGN_CENTER_VERTICAL, 0); - - /* Hamlib Serial Port combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Device:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialPort = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizerhl->Add(m_cbSerialPort, 0, wxALIGN_CENTER_VERTICAL, 0); - - /* Hamlib Serial Rate combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Rate:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialRate = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizerhl->Add(m_cbSerialRate, 0, wxALIGN_CENTER_VERTICAL, 0); - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Params:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialParams = new wxStaticText(this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, 0); - gridSizerhl->Add(m_cbSerialParams, 0, wxALIGN_CENTER_VERTICAL, 0); - - mainSizer->Add(staticBoxSizer18, 0, wxEXPAND, 5); - - //---------------------------------------------------------------------- - // Serial port PTT - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer17 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Serial Port Settings")), wxVERTICAL); - mainSizer->Add(staticBoxSizer17, 1, wxEXPAND, 5); - wxStaticBoxSizer* staticBoxSizer31 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("PTT Port")), wxVERTICAL); - staticBoxSizer17->Add(staticBoxSizer31, 1, wxEXPAND, 5); - -#ifdef __WXMSW__ - m_ckUseSerialPTT = new wxCheckBox(this, wxID_ANY, _("Use Serial Port PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckUseSerialPTT->SetValue(false); - staticBoxSizer31->Add(m_ckUseSerialPTT, 0, wxALIGN_LEFT, 20); - - wxArrayString m_listCtrlPortsArr; - m_listCtrlPorts = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1,45), m_listCtrlPortsArr, wxLB_SINGLE | wxLB_SORT); - staticBoxSizer31->Add(m_listCtrlPorts, 1, wxALIGN_CENTER, 0); -#endif - -#if defined(__WXOSX__) || defined(__WXGTK__) - wxBoxSizer* bSizer83; - bSizer83 = new wxBoxSizer(wxHORIZONTAL); - - wxGridSizer* gridSizer200 = new wxGridSizer(1, 3, 0, 0); - - m_ckUseSerialPTT = new wxCheckBox(this, wxID_ANY, _("Use Serial Port PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckUseSerialPTT->SetValue(false); - gridSizer200->Add(m_ckUseSerialPTT, 1, wxALIGN_CENTER|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 2); - - m_staticText12 = new wxStaticText(this, wxID_ANY, _("Serial Device: "), wxDefaultPosition, wxDefaultSize, 0); - m_staticText12->Wrap(-1); - gridSizer200->Add(m_staticText12, 1,wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 2); - - m_cbCtlDevicePath = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizer200->Add(m_cbCtlDevicePath, 1, wxEXPAND|wxALIGN_CENTER|wxALIGN_RIGHT, 2); - - bSizer83->Add(gridSizer200, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 2); - staticBoxSizer31->Add(bSizer83, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - wxBoxSizer* boxSizer19 = new wxBoxSizer(wxVERTICAL); - staticBoxSizer17->Add(boxSizer19, 1, wxEXPAND, 5); - wxStaticBoxSizer* staticBoxSizer16 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Signal polarity")), wxHORIZONTAL); - boxSizer19->Add(staticBoxSizer16, 1, wxEXPAND|wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - wxGridSizer* gridSizer17 = new wxGridSizer(2, 2, 0, 0); - staticBoxSizer16->Add(gridSizer17, 1, wxEXPAND|wxALIGN_RIGHT, 5); - - m_rbUseDTR = new wxRadioButton(this, wxID_ANY, _("Use DTR"), wxDefaultPosition, wxSize(-1,-1), 0); - m_rbUseDTR->SetToolTip(_("Toggle DTR line for PTT")); - m_rbUseDTR->SetValue(1); - gridSizer17->Add(m_rbUseDTR, 0, wxALIGN_CENTER|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5); - - m_ckDTRPos = new wxCheckBox(this, wxID_ANY, _("DTR = +V"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckDTRPos->SetToolTip(_("Set Polarity of the DTR line")); - m_ckDTRPos->SetValue(false); - gridSizer17->Add(m_ckDTRPos, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - m_rbUseRTS = new wxRadioButton(this, wxID_ANY, _("Use RTS"), wxDefaultPosition, wxSize(-1,-1), 0); - m_rbUseRTS->SetToolTip(_("Toggle the RTS pin for PTT")); - m_rbUseRTS->SetValue(1); - gridSizer17->Add(m_rbUseRTS, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - m_ckRTSPos = new wxCheckBox(this, wxID_ANY, _("RTS = +V"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckRTSPos->SetValue(false); - m_ckRTSPos->SetToolTip(_("Set Polarity of the RTS line")); - gridSizer17->Add(m_ckRTSPos, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - //---------------------------------------------------------------------- - // OK - Cancel - Apply - //---------------------------------------------------------------------- - - wxBoxSizer* boxSizer12 = new wxBoxSizer(wxHORIZONTAL); - - m_buttonTest = new wxButton(this, wxID_APPLY, _("Test PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonTest, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxSize(-1,-1), 0); - m_buttonOK->SetDefault(); - boxSizer12->Add(m_buttonOK, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonCancel, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonApply = new wxButton(this, wxID_APPLY, _("Apply"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonApply, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - mainSizer->Add(boxSizer12, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL, 5); - - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - Centre(wxBOTH); - - // Connect events - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(ComPortsDlg::OnInitDialog), NULL, this); - m_ckUseHamlibPTT->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseHamLibClicked), NULL, this); - m_ckUseSerialPTT->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseSerialClicked), NULL, this); - m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnOK), NULL, this); - m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnCancel), NULL, this); - m_buttonApply->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnApply), NULL, this); - m_buttonTest->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnTest), NULL, this); -} - -//------------------------------------------------------------------------- -// ~ComPortsDlg() -//------------------------------------------------------------------------- -ComPortsDlg::~ComPortsDlg() -{ - // Disconnect Events - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(ComPortsDlg::OnInitDialog), NULL, this); - m_ckUseHamlibPTT->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseHamLibClicked), NULL, this); - m_ckUseSerialPTT->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseSerialClicked), NULL, this); - m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnOK), NULL, this); - m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnCancel), NULL, this); - m_buttonApply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnApply), NULL, this); - m_buttonTest->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnTest), NULL, this); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void ComPortsDlg::OnInitDialog(wxInitDialogEvent& event) -{ - populatePortList(); - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// populatePortList() -//------------------------------------------------------------------------- -void ComPortsDlg::populatePortList() -{ - - /* populate Hamlib serial rate combo box */ - - wxString serialRates[] = {"default", "300", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"}; - for(int i=0; iAppend(serialRates[i]); - } - -#ifdef __WXMSW__ - m_listCtrlPorts->Clear(); - m_cbSerialPort->Clear(); - wxArrayString aStr; - wxRegKey key(wxRegKey::HKLM, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM")); - if(!key.Exists()) - { - return; - } - else - { - // Get the number of subkeys and enumerate them. - if(!key.Open(wxRegKey::Read)) - { - return; - } - size_t subkeys; - size_t values; - if(!key.GetKeyInfo(&subkeys, NULL, &values, NULL)) - { - return; - } - if(!key.HasValues()) - { - return; - } - wxString key_name; - long el = 1; - key.GetFirstValue(key_name, el); - wxString valType; - wxString key_data; - for(unsigned int i = 0; i < values; i++) - { - key.QueryValue(key_name, key_data); - //wxPrintf("Value: %s Data: %s\n", key_name, key_data); - aStr.Add(key_data, 1); - key.GetNextValue(key_name, el); - } - } - m_listCtrlPorts->Append(aStr); - m_cbSerialPort->Append(aStr); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - m_cbSerialPort->Clear(); - m_cbCtlDevicePath->Clear(); -#if defined(__FreeBSD__) || defined(__WXOSX__) - glob_t gl; -#ifdef __FreeBSD__ - if(glob("/dev/tty*", GLOB_MARK, NULL, &gl)==0) { -#else - if(glob("/dev/tty.*", GLOB_MARK, NULL, &gl)==0) { -#endif - for(unsigned int i=0; i= 'l' && gl.gl_pathv[i][8] <= 's') - continue; - if(gl.gl_pathv[i][8] >= 'L' && gl.gl_pathv[i][8] <= 'S') - continue; - - /* Exclude virtual TTYs */ - if(gl.gl_pathv[i][8] == 'v') - continue; - - /* Exclude initial-state and lock-state devices */ -#ifndef __WXOSX__ - if(strchr(gl.gl_pathv[i], '.') != NULL) - continue; -#endif - - m_cbSerialPort->Append(gl.gl_pathv[i]); - m_cbCtlDevicePath->Append(gl.gl_pathv[i]); - } - globfree(&gl); - } -#else - /* TODO(Joel): http://stackoverflow.com/questions/2530096/how-to-find-all-serial-devices-ttys-ttyusb-on-linux-without-opening-them */ - m_cbSerialPort->Append("/dev/ttyUSB0"); - m_cbSerialPort->Append("/dev/ttyUSB1"); - m_cbSerialPort->Append("/dev/ttyS0"); - m_cbSerialPort->Append("/dev/ttyS1"); - - m_cbCtlDevicePath->Clear(); - m_cbCtlDevicePath->Append("/dev/ttyUSB0"); - m_cbCtlDevicePath->Append("/dev/ttyUSB1"); - m_cbCtlDevicePath->Append("/dev/ttyS0"); - m_cbCtlDevicePath->Append("/dev/ttyS1"); -#endif -#endif - - -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void ComPortsDlg::ExchangeData(int inout) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - wxString str; - - if(inout == EXCHANGE_DATA_IN) { - m_ckLeftChannelVoxTone->SetValue(wxGetApp().m_leftChannelVoxTone); - - /* Hamlib */ - - m_ckUseHamlibPTT->SetValue(wxGetApp().m_boolHamlibUseForPTT); - m_cbRigName->SetSelection(wxGetApp().m_intHamlibRig); - m_cbSerialPort->SetValue(wxGetApp().m_strHamlibSerialPort); - - if (wxGetApp().m_intHamlibSerialRate == 0) { - m_cbSerialRate->SetSelection(0); - } else { - m_cbSerialRate->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intHamlibSerialRate)); - } - - /* Serial PTT */ - - m_ckUseSerialPTT->SetValue(wxGetApp().m_boolUseSerialPTT); - str = wxGetApp().m_strRigCtrlPort; -#ifdef __WXMSW__ - m_listCtrlPorts->SetStringSelection(str); -#endif -#if defined(__WXOSX__) || defined(__WXGTK__) - m_cbCtlDevicePath->SetValue(str); -#endif - m_rbUseRTS->SetValue(wxGetApp().m_boolUseRTS); - m_ckRTSPos->SetValue(wxGetApp().m_boolRTSPos); - m_rbUseDTR->SetValue(wxGetApp().m_boolUseDTR); - m_ckDTRPos->SetValue(wxGetApp().m_boolDTRPos); - } - - if (inout == EXCHANGE_DATA_OUT) { - wxGetApp().m_leftChannelVoxTone = m_ckLeftChannelVoxTone->GetValue(); - pConfig->Write(wxT("/Rig/leftChannelVoxTone"), wxGetApp().m_leftChannelVoxTone); - - /* Hamlib settings. */ - - wxGetApp().m_boolHamlibUseForPTT = m_ckUseHamlibPTT->GetValue(); - wxGetApp().m_intHamlibRig = m_cbRigName->GetSelection(); - wxGetApp().m_strHamlibSerialPort = m_cbSerialPort->GetValue(); - - wxString s = m_cbSerialRate->GetValue(); - if (s == "default") { - wxGetApp().m_intHamlibSerialRate = 0; - } else { - long tmp; - m_cbSerialRate->GetValue().ToLong(&tmp); - wxGetApp().m_intHamlibSerialRate = tmp; - } - fprintf(stderr, "serial rate: %d\n", wxGetApp().m_intHamlibSerialRate); - - pConfig->Write(wxT("/Hamlib/UseForPTT"), wxGetApp().m_boolHamlibUseForPTT); - pConfig->Write(wxT("/Hamlib/RigName"), wxGetApp().m_intHamlibRig); - pConfig->Write(wxT("/Hamlib/SerialPort"), wxGetApp().m_strHamlibSerialPort); - pConfig->Write(wxT("/Hamlib/SerialRate"), wxGetApp().m_intHamlibSerialRate); - - /* Serial settings */ - - wxGetApp().m_boolUseSerialPTT = m_ckUseSerialPTT->IsChecked(); -#ifdef __WXMSW__ - wxGetApp().m_strRigCtrlPort = m_listCtrlPorts->GetStringSelection(); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - wxGetApp().m_strRigCtrlPort = m_cbCtlDevicePath->GetValue(); -#endif - wxGetApp().m_boolUseRTS = m_rbUseRTS->GetValue(); - wxGetApp().m_boolRTSPos = m_ckRTSPos->IsChecked(); - wxGetApp().m_boolUseDTR = m_rbUseDTR->GetValue(); - wxGetApp().m_boolDTRPos = m_ckDTRPos->IsChecked(); - - pConfig->Write(wxT("/Rig/UseSerialPTT"), wxGetApp().m_boolUseSerialPTT); - pConfig->Write(wxT("/Rig/Port"), wxGetApp().m_strRigCtrlPort); - pConfig->Write(wxT("/Rig/UseRTS"), wxGetApp().m_boolUseRTS); - pConfig->Write(wxT("/Rig/RTSPolarity"), wxGetApp().m_boolRTSPos); - pConfig->Write(wxT("/Rig/UseDTR"), wxGetApp().m_boolUseDTR); - pConfig->Write(wxT("/Rig/DTRPolarity"), wxGetApp().m_boolDTRPos); - - pConfig->Flush(); - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -//------------------------------------------------------------------------- -// PTTUseHamLibClicked() -//------------------------------------------------------------------------- -void ComPortsDlg::PTTUseHamLibClicked(wxCommandEvent& event) -{ - m_ckUseSerialPTT->SetValue(false); -} - - -/* Attempt to toggle PTT for 1 second */ - -void ComPortsDlg::OnTest(wxCommandEvent& event) { - - /* Tone PTT */ - - if (m_ckLeftChannelVoxTone->GetValue()) { - wxMessageBox("Testing of tone based PTT not supported; try PTT after pressing Start on main window", - wxT("Error"), wxOK | wxICON_ERROR, this); - } - - /* Hamlib PTT */ - - if (m_ckUseHamlibPTT->GetValue()) { - - // set up current hamlib config from GUI - - int rig = m_cbRigName->GetSelection(); - wxString port = m_cbSerialPort->GetValue(); - wxString s = m_cbSerialRate->GetValue(); - int serial_rate; - if (s == "default") { - serial_rate = 0; - } else { - long tmp; - m_cbSerialRate->GetValue().ToLong(&tmp); - serial_rate = tmp; - } - - // display serial params - - fprintf(stderr, "serial rate: %d\n", serial_rate); - - // try to open rig - - Hamlib *hamlib = wxGetApp().m_hamlib; - bool status = hamlib->connect(rig, port.mb_str(wxConvUTF8), serial_rate); - if (status == false) { - wxMessageBox("Couldn't connect to Radio with hamlib", wxT("Error"), wxOK | wxICON_ERROR, this); - return; - } - else { - wxString hamlib_serial_config; - hamlib_serial_config.sprintf(" %d, %d, %d", - hamlib->get_serial_rate(), - hamlib->get_data_bits(), - hamlib->get_stop_bits()); - m_cbSerialParams->SetLabel(hamlib_serial_config); - } - - // toggle PTT - - wxString hamlibError; - if (hamlib->ptt(true, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - return; - } - - wxSleep(1); - - if (hamlib->ptt(false, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - } - - /* Serial PTT */ - - if (m_ckUseSerialPTT->IsChecked()) { - Serialport *serialport = wxGetApp().m_serialport; - - wxString ctrlport; -#ifdef __WXMSW__ - ctrlport = m_listCtrlPorts->GetStringSelection(); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - ctrlport = m_cbCtlDevicePath->GetValue(); -#endif - fprintf(stderr, "opening serial port\n"); - - bool success = serialport->openport(ctrlport.c_str(), - m_rbUseRTS->GetValue(), - m_ckRTSPos->IsChecked(), - m_rbUseDTR->GetValue(), - m_ckDTRPos->IsChecked()); - - fprintf(stderr, "serial port open\n"); - - if (!success) { - wxMessageBox("Couldn't open serial port", wxT("Error"), wxOK | wxICON_ERROR, this); - } - - // assert PTT port for 1 sec - - serialport->ptt(true); - wxSleep(1); - serialport->ptt(false); - - fprintf(stderr, "closing serial port\n"); - serialport->closeport(); - fprintf(stderr, "serial port closed\n"); - } - -} - - -//------------------------------------------------------------------------- -// PTTUseSerialClicked() -//------------------------------------------------------------------------- -void ComPortsDlg::PTTUseSerialClicked(wxCommandEvent& event) -{ - m_ckUseHamlibPTT->SetValue(false); -} - -//------------------------------------------------------------------------- -// OnApply() -//------------------------------------------------------------------------- -void ComPortsDlg::OnApply(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void ComPortsDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void ComPortsDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - this->EndModal(wxID_OK); -} diff --git a/freedv/branches/1.2/freedv-dev/src/dlg_ptt.h b/freedv/branches/1.2/freedv-dev/src/dlg_ptt.h deleted file mode 100644 index 5a35c3bd..00000000 --- a/freedv/branches/1.2/freedv-dev/src/dlg_ptt.h +++ /dev/null @@ -1,95 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.h -// Purpose: Subclasses dialog GUI for PTT Config. -// -// Created: May. 11, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __COMPORTS_DIALOG__ -#define __COMPORTS_DIALOG__ - -#include "fdmdv2_main.h" -#include "hamlib.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class ComPortsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class ComPortsDlg : public wxDialog -{ - public: - ComPortsDlg(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("PTT Config"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(450,300), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); - virtual ~ComPortsDlg(); - void ExchangeData(int inout); - - protected: - wxCheckBox* m_ckLeftChannelVoxTone; - - /* Hamlib settings.*/ - - wxCheckBox *m_ckUseHamlibPTT; - wxComboBox *m_cbRigName; - wxComboBox *m_cbSerialPort; - wxComboBox *m_cbSerialRate; - wxStaticText *m_cbSerialParams; - Hamlib *m_hamlib; - - /* Serial Settings */ - - wxListBox *m_listCtrlPorts; - wxCheckBox *m_ckUseSerialPTT; - wxStaticText *m_staticText12; - wxComboBox *m_cbCtlDevicePath; - wxRadioButton *m_rbUseDTR; - wxCheckBox *m_ckRTSPos; - wxRadioButton *m_rbUseRTS; - wxCheckBox *m_ckDTRPos; - - /* Test - Ok - Cancel - Apply */ - - wxButton* m_buttonTest; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - wxButton* m_buttonApply; - - -protected: - void populatePortList(); - - void PTTUseHamLibClicked(wxCommandEvent& event); - void PTTUseSerialClicked(wxCommandEvent& event); - - void OnTest(wxCommandEvent& event); - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnApply(wxCommandEvent& event); - virtual void OnInitDialog(wxInitDialogEvent& event); - -}; - -#endif // __COMPORTS_DIALOG__ diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_defines.h b/freedv/branches/1.2/freedv-dev/src/fdmdv2_defines.h deleted file mode 100644 index a6878890..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_defines.h +++ /dev/null @@ -1,106 +0,0 @@ -//========================================================================== -// Name: fdmdv2_defines.h -// Purpose: Definitions used by plots derived from fdmdv2_plot class. -// Created: August 27, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_DEFINES__ -#define __FDMDV2_DEFINES__ - -#include "wx/wx.h" -#include "freedv_api.h" -#include "modem_stats.h" - -// Spectrogram and Waterfall - -#define MIN_MAG_DB -40.0 // min of spectrogram/waterfall magnitude axis -#define MAX_MAG_DB 0.0 // max of spectrogram/waterfall magnitude axis -#define STEP_MAG_DB 5.0 // magnitude axis step -#define BETA 0.95 // constant for time averaging spectrum data -#define MIN_F_HZ 0 // min freq on Waterfall and Spectrum -#define MAX_F_HZ 3000 // max freq on Waterfall and Spectrum -#define STEP_F_HZ 500 // major (e.g. text legend) freq step on Waterfall and Spectrum graticule -#define STEP_MINOR_F_HZ 100 // minor (ticks) freq step on Waterfall and Spectrum graticule -#define WATERFALL_SECS_Y 30 // number of seconds respresented by y axis of waterfall -#define WATERFALL_SECS_STEP 5 // graticule y axis steps of waterfall -#define DT 0.1 // time between real time graphing updates -#define FS 8000 // FDMDV modem sample rate - -// Scatter diagram - -#define SCATTER_MEM_SECS 10 -// (symbols/frame)/(graphics update period) = symbols/s sent to scatter memory -// memory (symbols) = secs of memory * symbols/sec -#define SCATTER_MEM_SYMS_MAX ((int)(SCATTER_MEM_SECS*((MODEM_STATS_NC_MAX+1)/DT))) -#define SCATTER_EYE_MEM_ROWS ((int)(SCATTER_MEM_SECS/DT)) - -// Waveform plotting constants - -#define WAVEFORM_PLOT_FS 400 // sample rate (points/s) of waveform plotted to screen -#define WAVEFORM_PLOT_TIME 5 // length or entire waveform on screen -#define WAVEFORM_PLOT_BUF ((int)(DT*WAVEFORM_PLOT_FS)) // number of new samples we plot per DT - -// sample rate I/O & conversion constants - -#define MAX_FPB 8096 // maximum value of portAudio framesPerBuffer -#define PA_FPB 1024 // nominal value of portAudio framesPerBuffer -#define SAMPLE_RATE 48000 // 48 kHz sampling rate rec. as we can trust accuracy of sound card -#define N8 160 // processing buffer size at 8 kHz -#define MEM8 (FDMDV_OS_TAPS/FDMDV_OS) -#define N48 (N8*SAMPLE_RATE/FS) // processing buffer size at 48 kHz -#define NUM_CHANNELS 2 // I think most sound cards prefer stereo we will convert to mono -#define VOX_TONE_FREQ 1000.0 // optional left channel vox tone freq -#define VOX_TONE_AMP 30000 // optional left channel vox tone amp - -#define MAX_BITS_PER_CODEC_FRAME 64 // 1600 bit/s mode -#define MAX_BYTES_PER_CODEC_FRAME (MAX_BITS_PER_CODEC_FRAME/8) -#define MAX_BITS_PER_FDMDV_FRAME 40 // 2000 bit/s mode - -// Squelch -#define SQ_DEFAULT_SNR 2.0 - -// Level Gauge -#define FROM_RADIO_MAX 0.8 -#define FROM_MIC_MAX 0.8 -#define LEVEL_BETA 0.99 - -// SNR -#define SNRSLOW_BETA 0.5 // time constant for slow SNR for display - -// Text messaging Data -#define MAX_CALLSIGN 80 -#define MAX_EVENT_LOG 10 -#define MAX_EVENT_RULES 100 - -enum -{ - ID_ROTATE_LEFT = wxID_HIGHEST + 1, - ID_ROTATE_RIGHT, - ID_RESIZE, - ID_PAINT_BG -}; - -// Codec 2 LPC Post Filter defaults, from codec-dev/src/quantise.c - -#define CODEC2_LPC_PF_GAMMA 0.5 -#define CODEC2_LPC_PF_BETA 0.2 - -// PlugIns ... - -#define PLUGIN_MAX_PARAMS 4 - -#endif //__FDMDV2_DEFINES__ diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_main.cpp b/freedv/branches/1.2/freedv-dev/src/fdmdv2_main.cpp deleted file mode 100644 index 506406c1..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_main.cpp +++ /dev/null @@ -1,4042 +0,0 @@ -//========================================================================== -// Name: fdmdv2_main.cpp -// -// Purpose: FreeDV main() -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "fdmdv2_main.h" - -#define wxUSE_FILEDLG 1 -#define wxUSE_LIBPNG 1 -#define wxUSE_LIBJPEG 1 -#define wxUSE_GIF 1 -#define wxUSE_PCX 1 -#define wxUSE_LIBTIFF 1 - -//------------------------------------------------------------------- -// Bunch of globals used for communication with sound card call -// back functions -// ------------------------------------------------------------------ - -int g_in, g_out; - -// Global Codec2 & modem states - just one reqd for tx & rx -int g_Nc; -int g_mode; -struct freedv *g_pfreedv; -struct MODEM_STATS g_stats; -float g_pwr_scale; -int g_clip; - -// test Frames -int g_testFrames; -int g_test_frame_sync_state; -int g_test_frame_count; -int g_channel_noise; -int g_resyncs; -float g_sig_pwr_av = 0.0; -struct FIFO *g_error_pattern_fifo; -short *g_error_hist, *g_error_histn; -float g_tone_phase; - -// time averaged magnitude spectrum used for waterfall and spectrum display -float g_avmag[MODEM_STATS_NSPEC]; - -// GUI controls that affect rx and tx processes -int g_SquelchActive; -float g_SquelchLevel; -int g_analog; -int g_split; -int g_tx; -float g_snr; -bool g_half_duplex; -bool g_modal; - -// sending and receiving Call Sign data -struct FIFO *g_txDataInFifo; -struct FIFO *g_rxDataOutFifo; - -// tx/rx processing states -int g_State, g_prev_State; -paCallBackData *g_rxUserdata; - -// FIFOs used for plotting waveforms -struct FIFO *g_plotDemodInFifo; -struct FIFO *g_plotSpeechOutFifo; -struct FIFO *g_plotSpeechInFifo; - -// Soundcard config -int g_nSoundCards; -int g_soundCard1InDeviceNum; -int g_soundCard1OutDeviceNum; -int g_soundCard1SampleRate; -int g_soundCard2InDeviceNum; -int g_soundCard2OutDeviceNum; -int g_soundCard2SampleRate; - -// playing and recording from sound files - -SNDFILE *g_sfPlayFile; -bool g_playFileToMicIn; -bool g_loopPlayFileToMicIn; -int g_playFileToMicInEventId; - -SNDFILE *g_sfRecFile; -bool g_recFileFromRadio; -unsigned int g_recFromRadioSamples; -int g_recFileFromRadioEventId; - -SNDFILE *g_sfPlayFileFromRadio; -bool g_playFileFromRadio; -int g_sfFs; -bool g_loopPlayFileFromRadio; -int g_playFileFromRadioEventId; -float g_blink; - -wxWindow *g_parent; - -// Click to tune rx and tx frequency offset states -float g_RxFreqOffsetHz; -COMP g_RxFreqOffsetPhaseRect; -float g_TxFreqOffsetHz; -COMP g_TxFreqOffsetPhaseRect; - -// experimental mutex to make sound card callbacks mutually exclusive -// TODO: review code and see if we need this any more, as fifos should -// now be thread safe - -wxMutex g_mutexProtectingCallbackData; - -// Speex pre-processor states - -SpeexPreprocessState *g_speex_st; - -// WxWidgets - initialize the application -IMPLEMENT_APP(MainApp); - -//FILE *ftest; -FILE *g_logfile; - -//------------------------------------------------------------------------- -// OnInit() -//------------------------------------------------------------------------- -bool MainApp::OnInit() -{ - if(!wxApp::OnInit()) - { - return false; - } - SetVendorName(wxT("CODEC2-Project")); - SetAppName(wxT("FreeDV")); // not needed, it's the default value - -#ifdef FILE_RATHER_THAN_REGISTRY - // Force use of file-based configuration persistance on Windows platforma - wxConfig *pConfig = new wxConfig(); - wxFileConfig *pFConfig = new wxFileConfig(wxT("FreeDV"), wxT("CODEC2-Project"), wxT("FreeDV.conf"), wxT("FreeDV.conf"), wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH); - pConfig->Set(pFConfig); - pConfig->SetRecordDefaults(); -#else - wxConfigBase *pConfig = wxConfigBase::Get(); - pConfig->SetRecordDefaults(); -#endif - - m_rTopWindow = wxRect(0, 0, 0, 0); - m_strRxInAudio.Empty(); - m_strRxOutAudio.Empty(); - m_textVoiceInput.Empty(); - m_textVoiceOutput.Empty(); - m_strSampleRate.Empty(); - m_strBitrate.Empty(); - - // Look for Plug In - - m_plugIn = false; - #ifdef __WXMSW__ - wchar_t dll_path[] = L"afreedvplugin.dll"; - m_plugInHandle = LoadLibrary(dll_path); - #else - m_plugInHandle = dlopen("afreedvplugin.so", RTLD_LAZY); - #endif - - if (m_plugInHandle) { - printf("plugin: .so found\n"); - - // lets get some information abt the plugIn - - void (*plugin_namefp)(char s[]); - void *(*plugin_openfp)(char *param_names[], int *nparams, int (*aplugin_get_persistant)(char *, char *)); - - #ifdef __WXMSW__ - plugin_namefp = (void (*)(char*))GetProcAddress((HMODULE)m_plugInHandle, "plugin_name"); - plugin_openfp = (void* (*)(char**,int *, int (*)(char *, char *)))GetProcAddress((HMODULE)m_plugInHandle, "plugin_open"); - m_plugin_startfp = (void (*)(void *))GetProcAddress((HMODULE)m_plugInHandle, "plugin_start"); - m_plugin_stopfp = (void (*)(void *))GetProcAddress((HMODULE)m_plugInHandle, "plugin_stop"); - m_plugin_rx_samplesfp = (void (*)(void *, short *, int))GetProcAddress((HMODULE)m_plugInHandle, "plugin_rx_samples"); - #else - plugin_namefp = (void (*)(char*))dlsym(m_plugInHandle, "plugin_name"); - plugin_openfp = (void* (*)(char**,int *, int (*)(char *, char *)))dlsym(m_plugInHandle, "plugin_open"); - m_plugin_startfp = (void (*)(void *))dlsym(m_plugInHandle, "plugin_start"); - m_plugin_stopfp = (void (*)(void *))dlsym(m_plugInHandle, "plugin_stop"); - m_plugin_rx_samplesfp = (void (*)(void *, short *, int))dlsym(m_plugInHandle, "plugin_rx_samples"); - #endif - - if ((plugin_namefp != NULL) && (plugin_openfp != NULL)) { - - char s[256]; - m_plugIn = true; - (plugin_namefp)(s); - fprintf(stderr, "plugin name: %s\n", s); - m_plugInName = s; - - char param_name1[80], param_name2[80]; - char *param_names[2] = {param_name1, param_name2}; - int nparams, i; - m_plugInStates = (plugin_openfp)(param_names, &nparams, plugin_get_persistant); - m_numPlugInParam = nparams; - for(i=0; iRead(configStr, wxT("")); - //fprintf(stderr, " plugin param name[%d]: %s\n", i, param_names[i]); - fprintf(stderr, " plugin param name[%d]: %s values: %s\n", i, m_plugInParamName[i].mb_str().data(), m_txtPlugInParam[i].mb_str().data()); - } - } - - else { - fprintf(stderr, "plugin: fps not found...\n"); - } - } - else { - fprintf(stderr, "plugin not found...\n"); - } - - // Create the main application window - - frame = new MainFrame(m_plugInName, NULL); - SetTopWindow(frame); - - // Should guarantee that the first plot tab defined is the one - // displayed. But it doesn't when built from command line. Why? - - frame->m_auiNbookCtrl->ChangeSelection(0); - frame->Layout(); - frame->Show(); - g_parent =frame; - - - return true; -} - -//------------------------------------------------------------------------- -// OnExit() -//------------------------------------------------------------------------- -int MainApp::OnExit() -{ - fprintf(stderr, "MainApp::OnExit\n"); - if (m_plugIn) { - #ifdef __WXMSW__ - FreeLibrary((HMODULE)m_plugInHandle); - #else - dlclose(m_plugInHandle); - #endif - } - - return 0; -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainFrame(wxFrame* pa->ent) : TopFrame(parent) -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -MainFrame::MainFrame(wxString plugInName, wxWindow *parent) : TopFrame(plugInName, parent) -{ - m_zoom = 1.; - - #ifdef __WXMSW__ - g_logfile = stderr; - #else - g_logfile = stderr; - #endif - - - SetMinSize(wxSize(400,400)); - - // Init Hamlib library, but we dont start talking to any rigs yet - - wxGetApp().m_hamlib = new Hamlib(); - - // Init Serialport library, but as for Hamlib we dont start talking to any rigs yet - - wxGetApp().m_serialport = new Serialport(); - - tools->AppendSeparator(); - wxMenuItem* m_menuItemToolsConfigDelete; - m_menuItemToolsConfigDelete = new wxMenuItem(tools, wxID_ANY, wxString(_("&Restore defaults")) , wxT("Delete config file/keys and restore defaults"), wxITEM_NORMAL); - this->Connect(m_menuItemToolsConfigDelete->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::OnDeleteConfig)); - - tools->Append(m_menuItemToolsConfigDelete); - - wxConfigBase *pConfig = wxConfigBase::Get(); - - // restore frame position and size - int x = pConfig->Read(wxT("/MainFrame/left"), 20); - int y = pConfig->Read(wxT("/MainFrame/top"), 20); - int w = pConfig->Read(wxT("/MainFrame/width"), 800); - int h = pConfig->Read(wxT("/MainFrame/height"), 550); - - // sanitise frame position as a first pass at Win32 registry bug - - fprintf(g_logfile, "x = %d y = %d w = %d h = %d\n", x,y,w,h); - if (x < 0 || x > 2048) x = 20; - if (y < 0 || y > 2048) y = 20; - if (w < 0 || w > 2048) w = 800; - if (h < 0 || h > 2048) h = 550; - - wxGetApp().m_show_wf = pConfig->Read(wxT("/MainFrame/show_wf"), 1); - wxGetApp().m_show_spect = pConfig->Read(wxT("/MainFrame/show_spect"), 1); - wxGetApp().m_show_scatter = pConfig->Read(wxT("/MainFrame/show_scatter"), 1); - wxGetApp().m_show_timing = pConfig->Read(wxT("/MainFrame/show_timing"), 1); - wxGetApp().m_show_freq = pConfig->Read(wxT("/MainFrame/show_freq"), 1); - wxGetApp().m_show_speech_in = pConfig->Read(wxT("/MainFrame/show_speech_in"), 1); - wxGetApp().m_show_speech_out = pConfig->Read(wxT("/MainFrame/show_speech_out"), 1); - wxGetApp().m_show_demod_in = pConfig->Read(wxT("/MainFrame/show_demod_in"), 1); - wxGetApp().m_show_test_frame_errors = pConfig->Read(wxT("/MainFrame/show_test_frame_errors"), 1); - wxGetApp().m_show_test_frame_errors_hist = pConfig->Read(wxT("/MainFrame/show_test_frame_errors_hist"), 1); - - wxGetApp().m_rxNbookCtrl = pConfig->Read(wxT("/MainFrame/rxNbookCtrl"), (long)0); - - g_SquelchActive = pConfig->Read(wxT("/Audio/SquelchActive"), (long)0); - g_SquelchLevel = pConfig->Read(wxT("/Audio/SquelchLevel"), (int)(SQ_DEFAULT_SNR*2)); - g_SquelchLevel /= 2.0; - - Move(x, y); - SetClientSize(w, h); - - if(wxGetApp().m_show_wf) - { - // Add Waterfall Plot window - m_panelWaterfall = new PlotWaterfall((wxFrame*) m_auiNbookCtrl, false, 0); - m_panelWaterfall->SetToolTip(_("Left click to tune, Right click to toggle mono/colour")); - m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Waterfall"), true, wxNullBitmap); - } - if(wxGetApp().m_show_spect) - { - // Add Spectrum Plot window - m_panelSpectrum = new PlotSpectrum((wxFrame*) m_auiNbookCtrl, g_avmag, - MODEM_STATS_NSPEC*((float)MAX_F_HZ/MODEM_STATS_MAX_F_HZ)); - m_panelSpectrum->SetToolTip(_("Left click to tune")); - m_auiNbookCtrl->AddPage(m_panelSpectrum, _("Spectrum"), true, wxNullBitmap); - } - if(wxGetApp().m_show_scatter) - { - // Add Scatter Plot window - m_panelScatter = new PlotScatter((wxFrame*) m_auiNbookCtrl); - m_auiNbookCtrl->AddPage(m_panelScatter, _("Scatter"), true, wxNullBitmap); - } - if(wxGetApp().m_show_demod_in) - { - // Add Demod Input window - m_panelDemodIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelDemodIn, _("Frm Radio"), true, wxNullBitmap); - g_plotDemodInFifo = fifo_create(2*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_speech_in) - { - // Add Speech Input window - m_panelSpeechIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechIn, _("Frm Mic"), true, wxNullBitmap); - g_plotSpeechInFifo = fifo_create(4*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_speech_out) - { - // Add Speech Output window - m_panelSpeechOut = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechOut, _("To Spkr/Hdphns"), true, wxNullBitmap); - g_plotSpeechOutFifo = fifo_create(2*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_timing) - { - // Add Timing Offset window - m_panelTimeOffset = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 5.0, DT, -0.5, 0.5, 1, 0.1, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelTimeOffset, L"Timing \u0394", true, wxNullBitmap); - } - if(wxGetApp().m_show_freq) - { - // Add Frequency Offset window - m_panelFreqOffset = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 5.0, DT, -200, 200, 1, 50, "%3.0fHz", 0); - m_auiNbookCtrl->AddPage(m_panelFreqOffset, L"Frequency \u0394", true, wxNullBitmap); - } - - if(wxGetApp().m_show_test_frame_errors) - { - // Add Test Frame Errors window - m_panelTestFrameErrors = new PlotScalar((wxFrame*) m_auiNbookCtrl, 2*MODEM_STATS_NC_MAX, 30.0, DT, 0, 2*MODEM_STATS_NC_MAX+2, 1, 1, "", 1); - m_auiNbookCtrl->AddPage(m_panelTestFrameErrors, L"Test Frame Errors", true, wxNullBitmap); - } - - if(wxGetApp().m_show_test_frame_errors_hist) - { - // Add Test Frame Historgram window. 1 column for every bit, 2 bits per carrier - m_panelTestFrameErrorsHist = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 1.0, 1.0/(2*FDMDV_NC_MAX), 0.001, 0.1, 1.0/FDMDV_NC_MAX, 0.1, "%0.0E", 0); - m_auiNbookCtrl->AddPage(m_panelTestFrameErrorsHist, L"Test Frame Histogram", true, wxNullBitmap); - m_panelTestFrameErrorsHist->setBarGraph(1); - m_panelTestFrameErrorsHist->setLogY(1); - } - - wxGetApp().m_framesPerBuffer = pConfig->Read(wxT("/Audio/framesPerBuffer"), PA_FPB); - - g_soundCard1InDeviceNum = pConfig->Read(wxT("/Audio/soundCard1InDeviceNum"), -1); - g_soundCard1OutDeviceNum = pConfig->Read(wxT("/Audio/soundCard1OutDeviceNum"), -1); - g_soundCard1SampleRate = pConfig->Read(wxT("/Audio/soundCard1SampleRate"), -1); - - g_soundCard2InDeviceNum = pConfig->Read(wxT("/Audio/soundCard2InDeviceNum"), -1); - g_soundCard2OutDeviceNum = pConfig->Read(wxT("/Audio/soundCard2OutDeviceNum"), -1); - g_soundCard2SampleRate = pConfig->Read(wxT("/Audio/soundCard2SampleRate"), -1); - - g_nSoundCards = 0; - if ((g_soundCard1InDeviceNum > -1) && (g_soundCard1OutDeviceNum > -1)) { - g_nSoundCards = 1; - if ((g_soundCard2InDeviceNum > -1) && (g_soundCard2OutDeviceNum > -1)) - g_nSoundCards = 2; - } - - wxGetApp().m_playFileToMicInPath = pConfig->Read("/File/playFileToMicInPath", wxT("")); - wxGetApp().m_recFileFromRadioPath = pConfig->Read("/File/recFileFromRadioPath", wxT("")); - wxGetApp().m_recFileFromRadioSecs = pConfig->Read("/File/recFileFromRadioSecs", 30); - wxGetApp().m_playFileFromRadioPath = pConfig->Read("/File/playFileFromRadioPath", wxT("")); - - // PTT ------------------------------------------------------------------- - - wxGetApp().m_boolHalfDuplex = pConfig->ReadBool(wxT("/Rig/HalfDuplex"), true); - wxGetApp().m_leftChannelVoxTone = pConfig->ReadBool("/Rig/leftChannelVoxTone", false); - - wxGetApp().m_txtVoiceKeyerWaveFilePath = pConfig->Read(wxT("/VoiceKeyer/WaveFilePath"), wxT("")); - wxGetApp().m_txtVoiceKeyerWaveFile = pConfig->Read(wxT("/VoiceKeyer/WaveFile"), wxT("voicekeyer.wav")); - wxGetApp().m_intVoiceKeyerRxPause = pConfig->Read(wxT("/VoiceKeyer/RxPause"), 10); - wxGetApp().m_intVoiceKeyerRepeats = pConfig->Read(wxT("/VoiceKeyer/Repeats"), 5); - - wxGetApp().m_boolHamlibUseForPTT = pConfig->ReadBool("/Hamlib/UseForPTT", false); - wxGetApp().m_intHamlibRig = pConfig->ReadLong("/Hamlib/RigName", 0); - wxGetApp().m_strHamlibSerialPort = pConfig->Read("/Hamlib/SerialPort", ""); - wxGetApp().m_intHamlibSerialRate = pConfig->ReadLong("/Hamlib/SerialRate", 0); - - wxGetApp().m_boolUseSerialPTT = pConfig->ReadBool(wxT("/Rig/UseSerialPTT"), false); - wxGetApp().m_strRigCtrlPort = pConfig->Read(wxT("/Rig/Port"), wxT("")); - wxGetApp().m_boolUseRTS = pConfig->ReadBool(wxT("/Rig/UseRTS"), true); - wxGetApp().m_boolRTSPos = pConfig->ReadBool(wxT("/Rig/RTSPolarity"), true); - wxGetApp().m_boolUseDTR = pConfig->ReadBool(wxT("/Rig/UseDTR"), false); - wxGetApp().m_boolDTRPos = pConfig->ReadBool(wxT("/Rig/DTRPolarity"), false); - - assert(wxGetApp().m_serialport != NULL); - - // ----------------------------------------------------------------------- - - bool slow = false; // prevents compile error when using default bool - wxGetApp().m_snrSlow = pConfig->Read("/Audio/snrSlow", slow); - - bool t = true; // prevents compile error when using default bool - wxGetApp().m_codec2LPCPostFilterEnable = pConfig->Read(wxT("/Filter/codec2LPCPostFilterEnable"), t); - wxGetApp().m_codec2LPCPostFilterBassBoost = pConfig->Read(wxT("/Filter/codec2LPCPostFilterBassBoost"), t); - wxGetApp().m_codec2LPCPostFilterGamma = (float)pConfig->Read(wxT("/Filter/codec2LPCPostFilterGamma"), CODEC2_LPC_PF_GAMMA*100)/100.0; - wxGetApp().m_codec2LPCPostFilterBeta = (float)pConfig->Read(wxT("/Filter/codec2LPCPostFilterBeta"), CODEC2_LPC_PF_BETA*100)/100.0; - //printf("main(): m_codec2LPCPostFilterBeta: %f\n", wxGetApp().m_codec2LPCPostFilterBeta); - - wxGetApp().m_speexpp_enable = pConfig->Read(wxT("/Filter/speexpp_enable"), t); - - wxGetApp().m_MicInBassFreqHz = (float)pConfig->Read(wxT("/Filter/MicInBassFreqHz"), 1); - wxGetApp().m_MicInBassGaindB = (float)pConfig->Read(wxT("/Filter/MicInBassGaindB"), (long)0)/10.0; - wxGetApp().m_MicInTrebleFreqHz = (float)pConfig->Read(wxT("/Filter/MicInTrebleFreqHz"), 1); - wxGetApp().m_MicInTrebleGaindB = (float)pConfig->Read(wxT("/Filter/MicInTrebleGaindB"), (long)0)/10.0; - wxGetApp().m_MicInMidFreqHz = (float)pConfig->Read(wxT("/Filter/MicInMidFreqHz"), 1); - wxGetApp().m_MicInMidGaindB = (float)pConfig->Read(wxT("/Filter/MicInMidGaindB"), (long)0)/10.0; - wxGetApp().m_MicInMidQ = (float)pConfig->Read(wxT("/Filter/MicInMidQ"), (long)100)/100.0; - - bool f = false; - wxGetApp().m_MicInEQEnable = (float)pConfig->Read(wxT("/Filter/MicInEQEnable"), f); - - wxGetApp().m_SpkOutBassFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutBassFreqHz"), 1); - wxGetApp().m_SpkOutBassGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutBassGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutTrebleFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutTrebleFreqHz"), 1); - wxGetApp().m_SpkOutTrebleGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutTrebleGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutMidFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutMidFreqHz"), 1); - wxGetApp().m_SpkOutMidGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutMidGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutMidQ = (float)pConfig->Read(wxT("/Filter/SpkOutMidQ"), (long)100)/100.0; - - wxGetApp().m_SpkOutEQEnable = (float)pConfig->Read(wxT("/Filter/SpkOutEQEnable"), f); - - wxGetApp().m_callSign = pConfig->Read("/Data/CallSign", wxT("")); - wxGetApp().m_textEncoding = pConfig->Read("/Data/TextEncoding", 1); - wxGetApp().m_enable_checksum = pConfig->Read("/Data/EnableChecksumOnMsgRx", f); - - wxGetApp().m_events = pConfig->Read("/Events/enable", f); - wxGetApp().m_events_spam_timer = (int)pConfig->Read(wxT("/Events/spam_timer"), 10); - wxGetApp().m_events_regexp_match = pConfig->Read("/Events/regexp_match", wxT("s=(.*)")); - wxGetApp().m_events_regexp_replace = pConfig->Read("/Events/regexp_replace", - wxT("curl http://qso.freedv.org/cgi-bin/onspot.cgi?s=\\1")); - // make sure regexp lists are terminated by a \n - - if (wxGetApp().m_events_regexp_match.Last() != '\n') { - wxGetApp().m_events_regexp_match = wxGetApp().m_events_regexp_match+'\n'; - } - if (wxGetApp().m_events_regexp_replace.Last() != '\n') { - wxGetApp().m_events_regexp_replace = wxGetApp().m_events_regexp_replace+'\n'; - } - - wxGetApp().m_udp_enable = (float)pConfig->Read(wxT("/UDP/enable"), f); - wxGetApp().m_udp_port = (int)pConfig->Read(wxT("/UDP/port"), 3000); - - wxGetApp().m_FreeDV700txClip = (float)pConfig->Read(wxT("/FreeDV700/txClip"), t); - wxGetApp().m_FreeDV700Combine = 1; - wxGetApp().m_noise_snr = (float)pConfig->Read(wxT("/Noise/noise_snr"), 2); - - wxGetApp().m_debug_console = (float)pConfig->Read(wxT("/Debug/console"), f); - - wxGetApp().m_attn_carrier_en = 0; - wxGetApp().m_attn_carrier = 0; - - wxGetApp().m_tone = 0; - wxGetApp().m_tone_freq_hz = 1000; - wxGetApp().m_tone_amplitude = 500; - - int mode = pConfig->Read(wxT("/Audio/mode"), (long)0); - if (mode == 0) - m_rb1600->SetValue(1); - //if (mode == 2) - // m_rb700b->SetValue(1); - if (mode == 3) - m_rb700c->SetValue(1); - if (mode == 4) - m_rb800xa->SetValue(1); - - pConfig->SetPath(wxT("/")); - -// this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - //m_togRxID->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); - //m_togTxID->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnTxIDUI), NULL, this); - m_togBtnOnOff->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnOnOffUI), NULL, this); - m_togBtnSplit->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnSplitClickUI), NULL, this); - m_togBtnAnalog->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnAnalogClickUI), NULL, this); - //m_togBtnALC->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnALCClickUI), NULL, this); - // m_btnTogPTT->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnPTT_UI), NULL, this); - - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - m_btnTogPTT->Disable(); - m_togBtnVoiceKeyer->Disable(); - //m_togBtnALC->Disable(); - - // squelch settings - char sqsnr[15]; - m_sliderSQ->SetValue((int)((g_SquelchLevel+5.0)*2.0)); - sprintf(sqsnr, "%4.1f", g_SquelchLevel); - wxString sqsnr_string(sqsnr); - m_textSQ->SetLabel(sqsnr_string); - m_ckboxSQ->SetValue(g_SquelchActive); - - // SNR settings - - m_ckboxSNR->SetValue(wxGetApp().m_snrSlow); - setsnrBeta(wxGetApp().m_snrSlow); - -#ifdef _USE_TIMER - Bind(wxEVT_TIMER, &MainFrame::OnTimer, this); // ID_MY_WINDOW); - m_plotTimer.SetOwner(this, ID_TIMER_WATERFALL); - //m_panelWaterfall->Refresh(); -#endif - - m_RxRunning = false; - -#ifdef _USE_ONIDLE - Connect(wxEVT_IDLE, wxIdleEventHandler(MainFrame::OnIdle), NULL, this); -#endif //_USE_ONIDLE - - g_sfPlayFile = NULL; - g_playFileToMicIn = false; - g_loopPlayFileToMicIn = false; - - g_sfRecFile = NULL; - g_recFileFromRadio = false; - - g_sfPlayFileFromRadio = NULL; - g_playFileFromRadio = false; - g_loopPlayFileFromRadio = false; - - // init click-tune states - - g_RxFreqOffsetHz = 0.0; - g_RxFreqOffsetPhaseRect.real = cos(0.0); - g_RxFreqOffsetPhaseRect.imag = sin(0.0); - m_panelWaterfall->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelSpectrum->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - - g_TxFreqOffsetHz = 0.0; - g_TxFreqOffsetPhaseRect.real = cos(0.0); - g_TxFreqOffsetPhaseRect.imag = sin(0.0); - - g_tx = 0; - g_split = 0; - - // data states - g_txDataInFifo = fifo_create(MAX_CALLSIGN*VARICODE_MAX_BITS); - g_rxDataOutFifo = fifo_create(MAX_CALLSIGN*VARICODE_MAX_BITS); - - sox_biquad_start(); - - g_testFrames = 0; - g_test_frame_sync_state = 0; - g_resyncs = 0; - wxGetApp().m_testFrames = false; - g_tone_phase = 0.0; - - g_modal = false; - -#ifdef __EXPERIMENTAL_UDP__ - // Start UDP listener thread - - m_UDPThread = NULL; - startUDPThread(); -#endif - - optionsDlg = new OptionsDlg(NULL); - m_schedule_restore = false; - - vk_state = VK_IDLE; - - // Init optional Windows debug console so we can see all those printfs - -#ifdef __WXMSW__ - if (wxGetApp().m_debug_console) { - // somewhere to send printfs while developing - int ret = AllocConsole(); - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - fprintf(stderr, "AllocConsole: %d m_debug_console: %d\n", ret, wxGetApp().m_debug_console); - } -#endif - - //ftest = fopen("ftest.raw", "wb"); - //assert(ftest != NULL); -} - -//------------------------------------------------------------------------- -// ~MainFrame() -//------------------------------------------------------------------------- -MainFrame::~MainFrame() -{ - int x; - int y; - int w; - int h; - - fprintf(stderr, "MainFrame::~MainFrame()\n"); - //fclose(ftest); - #ifdef __WXMSW__ - fclose(g_logfile); - #endif - - if (optionsDlg != NULL) { - delete optionsDlg; - optionsDlg = NULL; - } - -#ifdef __EXPERIMENTAL_UDP__ - stopUDPThread(); -#endif - - if (wxGetApp().m_hamlib) delete wxGetApp().m_hamlib; - if (wxGetApp().m_serialport) delete wxGetApp().m_serialport; - - wxConfigBase *pConfig = wxConfigBase::Get(); - if(pConfig) - { - if (!IsIconized()) { - GetClientSize(&w, &h); - GetPosition(&x, &y); - printf("x = %d y = %d w = %d h = %d\n", x,y,w,h); - pConfig->Write(wxT("/MainFrame/left"), (long) x); - pConfig->Write(wxT("/MainFrame/top"), (long) y); - pConfig->Write(wxT("/MainFrame/width"), (long) w); - pConfig->Write(wxT("/MainFrame/height"), (long) h); - } - pConfig->Write(wxT("/MainFrame/show_wf"), wxGetApp().m_show_wf); - pConfig->Write(wxT("/MainFrame/show_spect"), wxGetApp().m_show_spect); - pConfig->Write(wxT("/MainFrame/show_scatter"), wxGetApp().m_show_scatter); - pConfig->Write(wxT("/MainFrame/show_timing"), wxGetApp().m_show_timing); - pConfig->Write(wxT("/MainFrame/show_freq"), wxGetApp().m_show_freq); - pConfig->Write(wxT("/MainFrame/show_speech_in"), wxGetApp().m_show_speech_in); - pConfig->Write(wxT("/MainFrame/show_speech_out"), wxGetApp().m_show_speech_out); - pConfig->Write(wxT("/MainFrame/show_demod_in"), wxGetApp().m_show_demod_in); - pConfig->Write(wxT("/MainFrame/show_test_frame_errors"), wxGetApp().m_show_test_frame_errors); - pConfig->Write(wxT("/MainFrame/show_test_frame_errors_hist"), wxGetApp().m_show_test_frame_errors_hist); - - pConfig->Write(wxT("/MainFrame/rxNbookCtrl"), wxGetApp().m_rxNbookCtrl); - - pConfig->Write(wxT("/Audio/SquelchActive"), g_SquelchActive); - pConfig->Write(wxT("/Audio/SquelchLevel"), (int)(g_SquelchLevel*2.0)); - - pConfig->Write(wxT("/Audio/framesPerBuffer"), wxGetApp().m_framesPerBuffer); - - pConfig->Write(wxT("/Audio/soundCard1InDeviceNum"), g_soundCard1InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1OutDeviceNum"), g_soundCard1OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1SampleRate"), g_soundCard1SampleRate ); - - pConfig->Write(wxT("/Audio/soundCard2InDeviceNum"), g_soundCard2InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2OutDeviceNum"), g_soundCard2OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2SampleRate"), g_soundCard2SampleRate ); - - pConfig->Write(wxT("/VoiceKeyer/WaveFilePath"), wxGetApp().m_txtVoiceKeyerWaveFilePath); - pConfig->Write(wxT("/VoiceKeyer/WaveFile"), wxGetApp().m_txtVoiceKeyerWaveFile); - pConfig->Write(wxT("/VoiceKeyer/RxPause"), wxGetApp().m_intVoiceKeyerRxPause); - pConfig->Write(wxT("/VoiceKeyer/Repeats"), wxGetApp().m_intVoiceKeyerRepeats); - - pConfig->Write(wxT("/Rig/HalfDuplex"), wxGetApp().m_boolHalfDuplex); - pConfig->Write(wxT("/Rig/leftChannelVoxTone"), wxGetApp().m_leftChannelVoxTone); - pConfig->Write("/Hamlib/UseForPTT", wxGetApp().m_boolHamlibUseForPTT); - pConfig->Write("/Hamlib/RigName", wxGetApp().m_intHamlibRig); - pConfig->Write("/Hamlib/SerialPort", wxGetApp().m_strHamlibSerialPort); - pConfig->Write("/Hamlib/SerialRate", wxGetApp().m_intHamlibSerialRate); - - - pConfig->Write(wxT("/File/playFileToMicInPath"), wxGetApp().m_playFileToMicInPath); - pConfig->Write(wxT("/File/recFileFromRadioPath"), wxGetApp().m_recFileFromRadioPath); - pConfig->Write(wxT("/File/recFileFromRadioSecs"), wxGetApp().m_recFileFromRadioSecs); - pConfig->Write(wxT("/File/playFileFromRadioPath"), wxGetApp().m_playFileFromRadioPath); - - pConfig->Write(wxT("/Audio/snrSlow"), wxGetApp().m_snrSlow); - - pConfig->Write(wxT("/Data/CallSign"), wxGetApp().m_callSign); - pConfig->Write(wxT("/Data/TextEncoding"), wxGetApp().m_textEncoding); - pConfig->Write(wxT("/Data/EnableChecksumOnMsgRx"), wxGetApp().m_enable_checksum); - pConfig->Write(wxT("/Events/enable"), wxGetApp().m_events); - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - pConfig->Write(wxT("/Events/regexp_match"), wxGetApp().m_events_regexp_match); - pConfig->Write(wxT("/Events/regexp_replace"), wxGetApp().m_events_regexp_replace); - - pConfig->Write(wxT("/UDP/enable"), wxGetApp().m_udp_enable); - pConfig->Write(wxT("/UDP/port"), wxGetApp().m_udp_port); - - pConfig->Write(wxT("/Filter/MicInEQEnable"), wxGetApp().m_MicInEQEnable); - pConfig->Write(wxT("/Filter/SpkOutEQEnable"), wxGetApp().m_SpkOutEQEnable); - - pConfig->Write(wxT("/FreeDV700/txClip"), wxGetApp().m_FreeDV700txClip); - pConfig->Write(wxT("/Noise/noise_snr"), wxGetApp().m_noise_snr); - - pConfig->Write(wxT("/Debug/console"), wxGetApp().m_debug_console); - - int mode; - if (m_rb1600->GetValue()) - mode = 0; - //if (m_rb700b->GetValue()) - // mode = 2; - if (m_rb700c->GetValue()) - mode = 3; - if (m_rb800xa->GetValue()) - mode = 4; - pConfig->Write(wxT("/Audio/mode"), mode); - } - - //m_togRxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); - //m_togTxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnTxIDUI), NULL, this); - m_togBtnOnOff->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnOnOffUI), NULL, this); - m_togBtnSplit->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnSplitClickUI), NULL, this); - m_togBtnAnalog->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnAnalogClickUI), NULL, this); - //m_togBtnALC->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnALCClickUI), NULL, this); - //m_btnTogPTT->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnPTT_UI), NULL, this); - - sox_biquad_finish(); - - if (m_RxRunning) - { - stopRxStream(); - } - if (g_sfPlayFile != NULL) - { - sf_close(g_sfPlayFile); - g_sfPlayFile = NULL; - } - if (g_sfRecFile != NULL) - { - sf_close(g_sfRecFile); - g_sfRecFile = NULL; - } -#ifdef _USE_TIMER - if(m_plotTimer.IsRunning()) - { - m_plotTimer.Stop(); - Unbind(wxEVT_TIMER, &MainFrame::OnTimer, this); - } -#endif //_USE_TIMER - -#ifdef _USE_ONIDLE - Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainFrame::OnIdle), NULL, this); -#endif // _USE_ONIDLE - - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - - -#ifdef _USE_ONIDLE -void MainFrame::OnIdle(wxIdleEvent &evt) { -} -#endif - - -#ifdef _USE_TIMER -//---------------------------------------------------------------- -// OnTimer() -// -// when the timer fires every DT seconds we update the GUI displays. -// the tabs only the plot that is visible actually gets updated, this -// keeps CPU load reasonable -//---------------------------------------------------------------- -void MainFrame::OnTimer(wxTimerEvent &evt) -{ - - int r,c; - - if (m_panelWaterfall->checkDT()) { - m_panelWaterfall->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelWaterfall->m_newdata = true; - m_panelWaterfall->Refresh(); - } - - m_panelSpectrum->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelSpectrum->m_newdata = true; - m_panelSpectrum->Refresh(); - - /* update scatter/eye plot ------------------------------------------------------------*/ - - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_800XA) { - /* FSK Mode - eye diagram ---------------------------------------------------------*/ - - /* add samples row by row */ - - int i; - for (i=0; iadd_new_samples_eye(&g_stats.rx_eye[i][0], g_stats.neyesamp); - } - } - else { - /* PSK Modes - scatter plot -------------------------------------------------------*/ - for (r=0; radd_new_samples_scatter(&g_stats.rx_symbols[r][0]); - } - - if (/*(freedv_get_mode(g_pfreedv) == FREEDV_MODE_700B) ||*/(freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C)) { - - if (wxGetApp().m_FreeDV700Combine) { - m_panelScatter->setNc(g_Nc/2); /* m_FreeDV700Combine may have changed at run time */ - - /* - FreeDV 700 uses diversity, so optionaly combine - symbols for scatter plot, as combined symbols are - used for demodulation. Note we need to use a copy - of the symbols, as we are not sure when the stats - will be updated. - */ - - COMP rx_symbols_copy[g_Nc/2]; - - for(c=0; cadd_new_samples_scatter(rx_symbols_copy); - } - else { - m_panelScatter->setNc(g_Nc); /* m_FreeDV700Combine may have changed at run time */ - /* - Sometimes useful to plot carriers separately, e.g. to determine if tx carrier power is constant - across carriers. - */ - m_panelScatter->add_new_samples_scatter(&g_stats.rx_symbols[r][0]); - } - } - - } - } - - m_panelScatter->Refresh(); - - // Oscilliscope type speech plots ------------------------------------------------------- - - short speechInPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotSpeechInFifo, speechInPlotSamples, WAVEFORM_PLOT_BUF)) { - memset(speechInPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - //fprintf(stderr, "empty!\n"); - } - m_panelSpeechIn->add_new_short_samples(0, speechInPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelSpeechIn->Refresh(); - - short speechOutPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotSpeechOutFifo, speechOutPlotSamples, WAVEFORM_PLOT_BUF)) - memset(speechOutPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - m_panelSpeechOut->add_new_short_samples(0, speechOutPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelSpeechOut->Refresh(); - - short demodInPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotDemodInFifo, demodInPlotSamples, WAVEFORM_PLOT_BUF)) { - memset(demodInPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - } - m_panelDemodIn->add_new_short_samples(0,demodInPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelDemodIn->Refresh(); - - // Demod states ----------------------------------------------------------------------- - - m_panelTimeOffset->add_new_sample(0, (float)g_stats.rx_timing/FDMDV_NOM_SAMPLES_PER_FRAME); - m_panelTimeOffset->Refresh(); - - m_panelFreqOffset->add_new_sample(0, g_stats.foff); - m_panelFreqOffset->Refresh(); - - // SNR text box and gauge ------------------------------------------------------------ - - // LP filter g_stats.snr_est some more to stabilise the - // display. g_stats.snr_est already has some low pass filtering - // but we need it fairly fast to activate squelch. So we - // optionally perform some further filtering for the display - // version of SNR. The "Slow" checkbox controls the amount of - // filtering. The filtered snr also controls the squelch - - g_snr = m_snrBeta*g_snr + (1.0 - m_snrBeta)*g_stats.snr_est; - float snr_limited = g_snr; - if (snr_limited < -5.0) snr_limited = -5.0; - if (snr_limited > 20.0) snr_limited = 20.0; - - char snr[15]; - sprintf(snr, "%d", (int)(g_snr+0.5)); // round to nearest dB - - //printf("snr_est: %f m_snrBeta: %f g_snr: %f snr_limited: %f\n", g_stats.snr_est, m_snrBeta, g_snr, snr_limited); - - wxString snr_string(snr); - m_textSNR->SetLabel(snr_string); - m_gaugeSNR->SetValue((int)(snr_limited+5)); - - - // Level Gauge ----------------------------------------------------------------------- - - float tooHighThresh; - if (!g_tx && m_RxRunning) - { - // receive mode - display From Radio peaks - // peak from this DT sampling period - int maxDemodIn = 0; - for(int i=0; i m_maxLevel) - m_maxLevel = maxDemodIn; - - tooHighThresh = FROM_RADIO_MAX; - } - else - { - // transmit mode - display From Mic peaks - - // peak from this DT sampling period - int maxSpeechIn = 0; - for(int i=0; i m_maxLevel) - m_maxLevel = maxSpeechIn; - - tooHighThresh = FROM_MIC_MAX; - } - - // Peak Reading meter: updates peaks immediately, then slowly decays - int maxScaled = (int)(100.0 * ((float)m_maxLevel/32767.0)); - m_gaugeLevel->SetValue(maxScaled); - //printf("maxScaled: %d\n", maxScaled); - if (((float)maxScaled/100) > tooHighThresh) - m_textLevel->SetLabel("Too High"); - else - m_textLevel->SetLabel(""); - - m_maxLevel *= LEVEL_BETA; - - // sync LED (Colours don't work on Windows) ------------------------ - - //fprintf(stderr, "g_State: %d m_rbSync->GetValue(): %d\n", g_State, m_rbSync->GetValue()); - if (g_State) { - if (g_prev_State == 0) { - g_resyncs++; - } - m_rbSync->SetForegroundColour( wxColour( 0, 255, 0 ) ); // green - m_rbSync->SetValue(true); - } - else { - m_rbSync->SetForegroundColour( wxColour( 255, 0, 0 ) ); // red - m_rbSync->SetValue(false); - } - g_prev_State = g_State; - - // send Callsign ---------------------------------------------------- - - char callsign[MAX_CALLSIGN]; - strncpy(callsign, (const char*) wxGetApp().m_callSign.mb_str(wxConvUTF8), MAX_CALLSIGN-1); - - // buffer 1 txt message to ensure tx data fifo doesn't "run dry" - - if ((unsigned)fifo_used(g_txDataInFifo) < strlen(callsign)) { - unsigned int i; - - //fprintf(g_logfile, "tx callsign: %s.\n", callsign); - - /* optionally append checksum */ - - if (wxGetApp().m_enable_checksum) { - - unsigned char checksum = 0; - char callsign_checksum_cr[MAX_CALLSIGN+1]; - - for(i=0; i MAX_CALLSIGN-1)) { - // CR completes line - *m_pcallsign = 0; - - /* Checksums can be disabled, e.g. for compatability with - older vesions. In that case we print msg but don't do - any event processing. If checksums enabled, only print - out when checksum is good. */ - - if (wxGetApp().m_enable_checksum) { - // lets see if checksum is OK - - unsigned char checksum_rx = 0; - if (strlen(m_callsign) > 2) { - for(unsigned int i=0; iSetValue(s); - -#ifdef __UDP_EXPERIMENTAL__ - char s1[MAX_CALLSIGN]; - sprintf(s1,"rx_txtmsg %s", m_callsign); - processTxtEvent(s1); - - m_checksumGood++; - s.Printf("%d", m_checksumGood); - m_txtChecksumGood->SetLabel(s); -#endif - } - else { -#ifdef __UDP_EXPERIMENTAL__ - m_checksumBad++; - s.Printf("%d", m_checksumBad); - m_txtChecksumBad->SetLabel(s); -#endif - } - } - - //fprintf(g_logfile,"resetting callsign %s %ld\n", m_callsign, m_pcallsign-m_callsign); - // reset ptr to start of string - m_pcallsign = m_callsign; - } - else { - //fprintf(g_logfile, "new char %d %c\n", ashort, (char)ashort); - *m_pcallsign++ = (char)ashort; - } - - /* If checksums disabled, display txt chars as they arrive */ - - if (!wxGetApp().m_enable_checksum) { - m_txtCtrlCallSign->SetValue(m_callsign); - } - } - - - // Run time update of EQ filters ----------------------------------- - - if (m_newMicInFilter || m_newSpkOutFilter) { - g_mutexProtectingCallbackData.Lock(); - deleteEQFilters(g_rxUserdata); - designEQFilters(g_rxUserdata); - g_mutexProtectingCallbackData.Unlock(); - m_newMicInFilter = m_newSpkOutFilter = false; - } - g_rxUserdata->micInEQEnable = wxGetApp().m_MicInEQEnable; - g_rxUserdata->spkOutEQEnable = wxGetApp().m_SpkOutEQEnable; - - - if (g_mode != -1) { - - // Run time update of FreeDV 700 tx clipper - - freedv_set_clip(g_pfreedv, (int)wxGetApp().m_FreeDV700txClip); - - // Test Frame Bit Error Updates ------------------------------------ - - // Toggle test frame mode at run time - - if (!freedv_get_test_frames(g_pfreedv) && wxGetApp().m_testFrames) { - - // reset stats on check box off to on transition - - freedv_set_test_frames(g_pfreedv, 1); - freedv_set_total_bits(g_pfreedv, 0); - freedv_set_total_bit_errors(g_pfreedv, 0); - } - freedv_set_test_frames(g_pfreedv, wxGetApp().m_testFrames); - freedv_set_test_frames_diversity(g_pfreedv, wxGetApp().m_FreeDV700Combine); - g_channel_noise = wxGetApp().m_channel_noise; - - if (g_State) { - char bits[80], errors[80], ber[80], resyncs[80]; - - // update stats on main page - - sprintf(bits, "Bits: %d", freedv_get_total_bits(g_pfreedv)); wxString bits_string(bits); m_textBits->SetLabel(bits_string); - sprintf(errors, "Errs: %d", freedv_get_total_bit_errors(g_pfreedv)); wxString errors_string(errors); m_textErrors->SetLabel(errors_string); - float b = (float)freedv_get_total_bit_errors(g_pfreedv)/(1E-6+freedv_get_total_bits(g_pfreedv)); - sprintf(ber, "BER: %4.3f", b); wxString ber_string(ber); m_textBER->SetLabel(ber_string); - sprintf(resyncs, "Resyncs: %d", g_resyncs); wxString resyncs_string(resyncs); m_textResyncs->SetLabel(resyncs_string); - - // update error pattern plots if supported - - int sz_error_pattern = freedv_get_sz_error_pattern(g_pfreedv); - //fprintf(stderr, "sz_error_pattern: %d\n", sz_error_pattern); - if (sz_error_pattern) { - short error_pattern[sz_error_pattern]; - - if (fifo_read(g_error_pattern_fifo, error_pattern, sz_error_pattern) == 0) { - int i,b; - - /* both modes map IQ to alternate bits, but on same carrier */ - - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_1600) { - /* FreeDV 1600 mapping from error pattern to two bits on each carrier */ - - for(b=0; badd_new_sample(b, b + 0.8*error_pattern[i]); - g_error_hist[b] += error_pattern[i]; - g_error_histn[b]++; - } - //if (b%2) - // printf("g_error_hist[%d]: %d\n", b/2, g_error_hist[b/2]); - } - - /* calculate BERs and send to plot */ - - float ber[2*FDMDV_NC_MAX]; - for(b=0; b<2*FDMDV_NC_MAX; b++) { - ber[b] = 0.0; - } - for(b=0; badd_new_samples(0, ber, 2*FDMDV_NC_MAX); - } - - if (/*(freedv_get_mode(g_pfreedv) == FREEDV_MODE_700B) || */(freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C)) { - int c; - //fprintf(stderr, "after g_error_pattern_fifo read 2\n"); - - /* - FreeDV 700 mapping from error pattern to bit on each carrier, see - data bit to carrier mapping in: - - codec2-dev/octave/cohpsk_frame_design.ods - - We can plot a histogram of the errors/carrier before or after diversity - recombination. Actually one bar for each IQ bit in carrier order. - */ - - int hist_Nc = sz_error_pattern/4; - //fprintf(stderr, "hist_Nc: %d\n", hist_Nc); - - for(i=0; iadd_new_sample(c, c + 0.8*error_pattern[i]); - g_error_hist[c] += error_pattern[i]; - g_error_histn[c]++; - //printf("i: %d c: %d\n", i, c); - } - for(; i<2*MODEM_STATS_NC_MAX*4; i++) { - c = floor(i/4); - m_panelTestFrameErrors->add_new_sample(c, c); - //printf("i: %d c: %d\n", i, c); - } - - /* calculate BERs and send to plot */ - - float ber[2*FDMDV_NC_MAX]; - for(b=0; b<2*FDMDV_NC_MAX; b++) { - ber[b] = 0.0; - } - for(b=0; badd_new_samples(0, ber, 2*FDMDV_NC_MAX); - } - - m_panelTestFrameErrors->Refresh(); - m_panelTestFrameErrorsHist->Refresh(); - } - } - } - } - - // command from UDP thread that is best processed in main thread to avoid seg faults - - if (m_schedule_restore) { - if (IsIconized()) - Restore(); - m_schedule_restore = false; - } - -#ifdef __UDP_EXPERIMENTAL__ - // Light Spam Timer LED if at least one timer is running - - int i; - optionsDlg->SetSpamTimerLight(false); - for(i=0; iSetSpamTimerLight(true); -#endif - - // Blink file playback status line - - if (g_playFileFromRadio) { - g_blink += DT; - //fprintf(g_logfile, "g_blink: %f\n", g_blink); - if ((g_blink >= 1.0) && (g_blink < 2.0)) - SetStatusText(wxT("Playing into from radio"), 0); - if (g_blink >= 2.0) { - SetStatusText(wxT(""), 0); - g_blink = 0.0; - } - } - - // Voice Keyer state machine - - VoiceKeyerProcessEvent(VK_DT); -} -#endif - - -//------------------------------------------------------------------------- -// OnCloseFrame() -//------------------------------------------------------------------------- -void MainFrame::OnCloseFrame(wxCloseEvent& event) -{ - fprintf(stderr, "MainFrame::OnCloseFrame()\n"); - Pa_Terminate(); - Destroy(); -} - -//------------------------------------------------------------------------- -// OnTop() -//------------------------------------------------------------------------- -void MainFrame::OnTop(wxCommandEvent& event) -{ - int style = GetWindowStyle(); - - if (style & wxSTAY_ON_TOP) - { - style &= ~wxSTAY_ON_TOP; - } - else - { - style |= wxSTAY_ON_TOP; - } - SetWindowStyle(style); -} - -//------------------------------------------------------------------------- -// OnDeleteConfig() -//------------------------------------------------------------------------- -void MainFrame::OnDeleteConfig(wxCommandEvent&) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - if(pConfig->DeleteAll()) - { - wxLogMessage(wxT("Config file/registry key successfully deleted.")); - - delete wxConfigBase::Set(NULL); - wxConfigBase::DontCreateOnDemand(); - } - else - { - wxLogError(wxT("Deleting config file/registry key failed.")); - } -} - -//------------------------------------------------------------------------- -// Paint() -//------------------------------------------------------------------------- -void MainFrame::OnPaint(wxPaintEvent& WXUNUSED(event)) -{ - wxPaintDC dc(this); - - if(GetMenuBar()->IsChecked(ID_PAINT_BG)) - { - dc.Clear(); - } - dc.SetUserScale(m_zoom, m_zoom); -} - -//------------------------------------------------------------------------- -// OnCmdSliderScroll() -//------------------------------------------------------------------------- -void MainFrame::OnCmdSliderScroll(wxScrollEvent& event) -{ - char sqsnr[15]; - g_SquelchLevel = (float)m_sliderSQ->GetValue()/2.0 - 5.0; - sprintf(sqsnr, "%4.1f", g_SquelchLevel); // 0.5 dB steps - wxString sqsnr_string(sqsnr); - m_textSQ->SetLabel(sqsnr_string); - - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnCheckSQClick() -//------------------------------------------------------------------------- -void MainFrame::OnCheckSQClick(wxCommandEvent& event) -{ - if(!g_SquelchActive) - { - g_SquelchActive = true; - } - else - { - g_SquelchActive = false; - } -} - -void MainFrame::setsnrBeta(bool snrSlow) -{ - if(snrSlow) - { - m_snrBeta = 0.95; // make this closer to 1.0 to smooth SNR est further - } - else - { - m_snrBeta = 0.0; // no smoothing of SNR estimate from demodulator - } -} - -//------------------------------------------------------------------------- -// OnCheckSQClick() -//------------------------------------------------------------------------- -void MainFrame::OnCheckSNRClick(wxCommandEvent& event) -{ - wxGetApp().m_snrSlow = m_ckboxSNR->GetValue(); - setsnrBeta(wxGetApp().m_snrSlow); - //printf("m_snrSlow: %d\n", (int)wxGetApp().m_snrSlow); -} - -// check for space bar press (only when running) - -int MainApp::FilterEvent(wxEvent& event) -{ - if ((event.GetEventType() == wxEVT_KEY_DOWN) && - (((wxKeyEvent&)event).GetKeyCode() == WXK_SPACE)) - { - // only use space to toggle PTT if we are running and no modal dialogs (like options) up - //fprintf(stderr,"frame->m_RxRunning: %d g_modal: %d\n", - // frame->m_RxRunning, g_modal); - if (frame->m_RxRunning && !g_modal) { - - // space bar controls rx/rx if keyer not running - if (frame->vk_state == VK_IDLE) { - if (frame->m_btnTogPTT->GetValue()) - frame->m_btnTogPTT->SetValue(false); - else - frame->m_btnTogPTT->SetValue(true); - - frame->togglePTT(); - } - else // spavce bar stops keyer - frame->VoiceKeyerProcessEvent(VK_SPACE_BAR); - - return true; // absorb space so we don't toggle control with focus (e.g. Start) - - } - } - - return -1; -} - -//------------------------------------------------------------------------- -// OnTogBtnPTT () -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnPTT (wxCommandEvent& event) -{ - togglePTT(); - event.Skip(); -} - -void MainFrame::togglePTT(void) { - - // Change tabbed page in centre panel depending on PTT state - - if (g_tx) - { - // tx-> rx transition, swap to the page we were on for last rx - m_auiNbookCtrl->ChangeSelection(wxGetApp().m_rxNbookCtrl); - } - else - { - // rx-> tx transition, swap to Mic In page to monitor speech - wxGetApp().m_rxNbookCtrl = m_auiNbookCtrl->GetSelection(); - m_auiNbookCtrl->ChangeSelection(m_auiNbookCtrl->GetPageIndex((wxWindow *)m_panelSpeechIn)); -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"ptt"); processTxtEvent(e); -#endif - } - - g_tx = m_btnTogPTT->GetValue(); - - // Hamlib PTT - - if (wxGetApp().m_boolHamlibUseForPTT) { - Hamlib *hamlib = wxGetApp().m_hamlib; - wxString hamlibError; - if (wxGetApp().m_boolHamlibUseForPTT && hamlib != NULL) { - if (hamlib->ptt(g_tx, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - } - } - - // Serial PTT - - if (wxGetApp().m_boolUseSerialPTT && (wxGetApp().m_serialport->isopen())) { - wxGetApp().m_serialport->ptt(g_tx); - } - - // reset level gauge - - m_maxLevel = 0; - m_textLevel->SetLabel(wxT("")); - m_gaugeLevel->SetValue(0); -} - -/* - Voice Keyer: - - + space bar turns keyer off - + 5 secs of valid sync turns it off - - [X] complete state machine and builds OK - [ ] file select dialog - [ ] test all states - [ ] restore size -*/ - -void MainFrame::OnTogBtnVoiceKeyerClick (wxCommandEvent& event) -{ - if (vk_state == VK_IDLE) - VoiceKeyerProcessEvent(VK_START); - else - VoiceKeyerProcessEvent(VK_SPACE_BAR); - - event.Skip(); -} - - -int MainFrame::VoiceKeyerStartTx(void) -{ - int next_state; - - // start playing wave file or die trying - - SF_INFO sfInfo; - sfInfo.format = 0; - - g_sfPlayFile = sf_open(wxGetApp().m_txtVoiceKeyerWaveFile, SFM_READ, &sfInfo); - if(g_sfPlayFile == NULL) { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open:") + wxGetApp().m_txtVoiceKeyerWaveFile, wxOK); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - else { - SetStatusText(wxT("Voice Keyer: Playing File") + wxGetApp().m_txtVoiceKeyerWaveFile + wxT(" to Mic Input") , 0); - g_loopPlayFileToMicIn = false; - g_playFileToMicIn = true; - - m_btnTogPTT->SetValue(true); togglePTT(); - next_state = VK_TX; - } - - return next_state; -} - - -void MainFrame::VoiceKeyerProcessEvent(int vk_event) { - int next_state = vk_state; - - switch(vk_state) { - - case VK_IDLE: - if (vk_event == VK_START) { - // sample these puppies at start just in case they are changed while VK running - vk_rx_pause = wxGetApp().m_intVoiceKeyerRxPause; - vk_repeats = wxGetApp().m_intVoiceKeyerRepeats; - fprintf(stderr, "vk_rx_pause: %d vk_repeats: %d\n", vk_rx_pause, vk_repeats); - - vk_repeat_counter = 0; - next_state = VoiceKeyerStartTx(); - } - break; - - case VK_TX: - - // In this state we are transmitting and playing a wave file - // to Mic In - - if (vk_event == VK_SPACE_BAR) { - m_btnTogPTT->SetValue(false); togglePTT(); - StopPlayFileToMicIn(); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if (vk_event == VK_PLAY_FINISHED) { - m_btnTogPTT->SetValue(false); togglePTT(); - vk_repeat_counter++; - if (vk_repeat_counter > vk_repeats) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - else { - vk_rx_time = 0.0; - next_state = VK_RX; - } - } - - break; - - case VK_RX: - - // in this state we are receiving and waiting for - // delay timer or valid sync - - if (vk_event == VK_DT) { - if (freedv_get_sync(g_pfreedv) == 1) { - // if we detect sync simulate a smooth transition to SYNC_WAIT State - TODO: review - if (vk_rx_time >= DT) { - vk_rx_time -= DT; - } else { - next_state = VK_SYNC_WAIT; - } - } else { - vk_rx_time += DT; - if (vk_rx_time >= vk_rx_pause) { - next_state = VoiceKeyerStartTx(); - } - } - } - - if (vk_event == VK_SPACE_BAR) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - break; - - case VK_SYNC_WAIT: - - // In this state we wait for valid sync to last - // VK_SYNC_WAIT_TIME seconds - - if (vk_event == VK_SPACE_BAR) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if (vk_event == VK_DT) { - if (freedv_get_sync(g_pfreedv) == 0) { - // if we lose sync simulate a smooth transition to return in RX State - TODO: review - if (vk_rx_time >= DT) { - vk_rx_time -= DT; - } else { - next_state = VK_RX; - } - } else { - vk_rx_time += DT; - } - - // drop out of voice keyer if we get a few seconds of valid sync - - if (vk_rx_time >= VK_SYNC_WAIT_TIME) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - } - break; - - default: - // catch anything we missed - - m_btnTogPTT->SetValue(false); togglePTT(); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - //if ((vk_event != VK_DT) || (vk_state != next_state)) - // fprintf(stderr, "VoiceKeyerProcessEvent: vk_state: %d vk_event: %d next_state: %d vk_repeat_counter: %d\n", vk_state, vk_event, next_state, vk_repeat_counter); - vk_state = next_state; -} - - -//------------------------------------------------------------------------- -// OnTogBtnRxID() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnRxID(wxCommandEvent& event) -{ - // empty any junk in rx data FIFO - short junk; - while(fifo_read(g_rxDataOutFifo,&junk,1) == 0); - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnTogBtnTxID() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnTxID(wxCommandEvent& event) -{ - event.Skip(); -} - -void MainFrame::OnTogBtnSplitClick(wxCommandEvent& event) { - if (g_split) - g_split = 0; - else - g_split = 1; - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnTogBtnAnalogClick() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnAnalogClick (wxCommandEvent& event) -{ - if (g_analog == 0) { - g_analog = 1; - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(FS/2))); - m_panelWaterfall->setFs(FS); - } - else { - g_analog = 0; - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(freedv_get_modem_sample_rate(g_pfreedv)/2))); - m_panelWaterfall->setFs(freedv_get_modem_sample_rate(g_pfreedv)); - } - - g_State = g_prev_State = 0; - g_stats.snr_est = 0; - - event.Skip(); -} - -void MainFrame::OnCallSignReset(wxCommandEvent& event) -{ - m_pcallsign = m_callsign; - memset(m_callsign, 0, MAX_CALLSIGN); - wxString s; - s.Printf("%s", m_callsign); - m_txtCtrlCallSign->SetValue(s); -#ifdef __UDP__EXPERIMENTAL__ - m_checksumGood = m_checksumBad = 0; - m_txtChecksumGood->SetLabel(_("0")); - m_txtChecksumBad->SetLabel(_("0")); -#endif -} - -void MainFrame::OnBerReset(wxCommandEvent& event) -{ - freedv_set_total_bits(g_pfreedv, 0); - freedv_set_total_bit_errors(g_pfreedv, 0); - g_resyncs = 0; - int i; - for(i=0; i<2*g_Nc; i++) { - g_error_hist[i] = 0; - g_error_histn[i] = 0; - } - -} - -#ifdef ALC -//------------------------------------------------------------------------- -// OnTogBtnALCClick() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnALCClick(wxCommandEvent& event) -{ - wxMessageBox(wxT("Got Click!"), wxT("OnTogBtnALCClick"), wxOK); - - event.Skip(); -} -#endif - -// extra panel added to file open dialog to add loop checkbox -MyExtraPlayFilePanel::MyExtraPlayFilePanel(wxWindow *parent): wxPanel(parent) -{ - m_cb = new wxCheckBox(this, -1, wxT("Loop")); - m_cb->SetToolTip(_("When checked file will repeat forever")); - m_cb->SetValue(g_loopPlayFileToMicIn); - - // bug: I can't this to align right..... - wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL); - sizerTop->Add(m_cb, 0, wxALIGN_RIGHT, 0); - SetSizerAndFit(sizerTop); -} - -static wxWindow* createMyExtraPlayFilePanel(wxWindow *parent) -{ - return new MyExtraPlayFilePanel(parent); -} - -void MainFrame::StopPlayFileToMicIn(void) -{ - g_mutexProtectingCallbackData.Lock(); - g_playFileToMicIn = false; - sf_close(g_sfPlayFile); - SetStatusText(wxT("")); - g_mutexProtectingCallbackData.Unlock(); -} - -//------------------------------------------------------------------------- -// OnPlayFileToMicIn() -//------------------------------------------------------------------------- -void MainFrame::OnPlayFileToMicIn(wxCommandEvent& event) -{ - wxUnusedVar(event); - - if(g_playFileToMicIn) { - StopPlayFileToMicIn(); - VoiceKeyerProcessEvent(VK_PLAY_FINISHED); - } - else - { - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Play File to Mic In"), - wxGetApp().m_playFileToMicInPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_OPEN | wxFD_FILE_MUST_EXIST - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraPlayFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_playFileToMicInPath, &fileName, &extension); - //wxLogDebug("m_playFileToMicInPath: %s", wxGetApp().m_playFileToMicInPath); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = FS; - } - } - g_sfPlayFile = sf_open(soundFile.c_str(), SFM_READ, &sfInfo); - if(g_sfPlayFile == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - - // Huh?! I just copied wxWidgets-2.9.4/samples/dialogs .... - g_loopPlayFileToMicIn = static_cast(ctrl)->getLoopPlayFileToMicIn(); - - SetStatusText(wxT("Playing File: ") + fileName + wxT(" to Mic Input") , 0); - g_playFileToMicIn = true; - } -} - -//------------------------------------------------------------------------- -// OnPlayFileFromRadio() -// This puppy "plays" a recorded file into the demodulator input, allowing us -// to replay off air signals. -//------------------------------------------------------------------------- -void MainFrame::OnPlayFileFromRadio(wxCommandEvent& event) -{ - wxUnusedVar(event); - - printf("OnPlayFileFromRadio:: %d\n", (int)g_playFileFromRadio); - if (g_playFileFromRadio) - { - printf("OnPlayFileFromRadio:: Stop\n"); - g_mutexProtectingCallbackData.Lock(); - g_playFileFromRadio = false; - sf_close(g_sfPlayFileFromRadio); - SetStatusText(wxT(""),0); - SetStatusText(wxT(""),1); - g_mutexProtectingCallbackData.Unlock(); - } - else - { - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Play File - From Radio"), - wxGetApp().m_playFileFromRadioPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_OPEN | wxFD_FILE_MUST_EXIST - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraPlayFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_playFileFromRadioPath, &fileName, &extension); - //wxLogDebug("m_playFileToFromRadioPath: %s", wxGetApp().m_playFileFromRadioPath); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } - } - g_sfPlayFileFromRadio = sf_open(soundFile.c_str(), SFM_READ, &sfInfo); - g_sfFs = sfInfo.samplerate; - if(g_sfPlayFileFromRadio == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - - // Huh?! I just copied wxWidgets-2.9.4/samples/dialogs .... - g_loopPlayFileFromRadio = static_cast(ctrl)->getLoopPlayFileToMicIn(); - - SetStatusText(wxT("Playing into from radio"), 0); - if(extension == wxT("raw")) { - wxString stringnumber = wxString::Format(wxT("%d"), (int)sfInfo.samplerate); - SetStatusText(wxT("raw file assuming Fs=") + stringnumber, 1); - } - fprintf(g_logfile, "OnPlayFileFromRadio:: Playing File\n"); - g_playFileFromRadio = true; - g_blink = 0.0; - } -} - -// extra panel added to file save dialog to set number of seconds to record for - -MyExtraRecFilePanel::MyExtraRecFilePanel(wxWindow *parent): wxPanel(parent) -{ - wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL); - - wxStaticText* staticText = new wxStaticText(this, wxID_ANY, _("Seconds:"), wxDefaultPosition, wxDefaultSize, 0); - sizerTop->Add(staticText, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_secondsToRecord = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - m_secondsToRecord->SetToolTip(_("Number of seconds to record for")); - m_secondsToRecord->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_recFileFromRadioSecs)); - sizerTop->Add(m_secondsToRecord, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5); - SetSizerAndFit(sizerTop); -} - -static wxWindow* createMyExtraRecFilePanel(wxWindow *parent) -{ - return new MyExtraRecFilePanel(parent); -} - -//------------------------------------------------------------------------- -// OnRecFileFromRadio() -//------------------------------------------------------------------------- -void MainFrame::OnRecFileFromRadio(wxCommandEvent& event) -{ - wxUnusedVar(event); - - if (g_recFileFromRadio) { - printf("Stopping Record....\n"); - g_mutexProtectingCallbackData.Lock(); - g_recFileFromRadio = false; - sf_close(g_sfRecFile); - SetStatusText(wxT("")); - g_mutexProtectingCallbackData.Unlock(); - } - else { - - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Record File From Radio"), - wxGetApp().m_recFileFromRadioPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_SAVE - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraRecFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_recFileFromRadioPath, &fileName, &extension); - wxLogDebug("m_recFileFromRadioPath: %s", wxGetApp().m_recFileFromRadioPath); - wxLogDebug("soundFile: %s", soundFile); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } - else if(extension == wxT("wav")) - { - sfInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } else { - wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); - return; - } - } - else { - wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); - return; - } - - // Bug: on Win32 I cant read m_recFileFromRadioSecs, so have hard coded it -#ifdef __WIN32__ - long secs = wxGetApp().m_recFileFromRadioSecs; - g_recFromRadioSamples = FS*(unsigned int)secs; -#else - // work out number of samples to record - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - wxString secsString = static_cast(ctrl)->getSecondsToRecord(); - wxLogDebug("test: %s secsString: %s\n", wxT("testing 123"), secsString); - - long secs; - if (secsString.ToLong(&secs)) { - wxGetApp().m_recFileFromRadioSecs = (unsigned int)secs; - //printf(" secondsToRecord: %d\n", (unsigned int)secs); - g_recFromRadioSamples = FS*(unsigned int)secs; - //printf("g_recFromRadioSamples: %d\n", g_recFromRadioSamples); - } - else { - wxMessageBox(wxT("Invalid number of Seconds"), wxT("Record File From Radio"), wxOK); - return; - } -#endif - - g_sfRecFile = sf_open(soundFile.c_str(), SFM_WRITE, &sfInfo); - if(g_sfRecFile == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - SetStatusText(wxT("Recording File: ") + fileName + wxT(" From Radio") , 0); - g_recFileFromRadio = true; - } - -} - -//------------------------------------------------------------------------- -// OnExit() -//------------------------------------------------------------------------- -void MainFrame::OnExit(wxCommandEvent& event) -{ - fprintf(stderr, "MainFrame::OnExit\n"); - wxUnusedVar(event); -#ifdef _USE_TIMER - m_plotTimer.Stop(); -#endif // _USE_TIMER - if(g_sfPlayFile != NULL) - { - sf_close(g_sfPlayFile); - g_sfPlayFile = NULL; - } - if(g_sfRecFile != NULL) - { - sf_close(g_sfRecFile); - g_sfRecFile = NULL; - } - if(m_RxRunning) - { - stopRxStream(); - } - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - //m_togBtnALC->Disable(); - //m_btnTogPTT->Disable(); - Pa_Terminate(); - Destroy(); -} - -//------------------------------------------------------------------------- -// OnExitClick() -//------------------------------------------------------------------------- -void MainFrame::OnExitClick(wxCommandEvent& event) -{ - OnExit(event); -} - -//------------------------------------------------------------------------- -// OnToolsAudio() -//------------------------------------------------------------------------- -void MainFrame::OnToolsAudio(wxCommandEvent& event) -{ - wxUnusedVar(event); - int rv = 0; - AudioOptsDialog *dlg = new AudioOptsDialog(NULL); - rv = dlg->ShowModal(); - if(rv == wxID_OK) - { - dlg->ExchangeData(EXCHANGE_DATA_OUT); - } - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsAudioUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsAudioUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning); -} - -//------------------------------------------------------------------------- -// OnToolsFilter() -//------------------------------------------------------------------------- -void MainFrame::OnToolsFilter(wxCommandEvent& event) -{ - wxUnusedVar(event); - FilterDlg *dlg = new FilterDlg(NULL, m_RxRunning, &m_newMicInFilter, &m_newSpkOutFilter); - dlg->ShowModal(); - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsOptions() -//------------------------------------------------------------------------- -void MainFrame::OnToolsOptions(wxCommandEvent& event) -{ - wxUnusedVar(event); - g_modal = true; - //fprintf(stderr,"g_modal: %d\n", g_modal); - optionsDlg->Show(); -} - -//------------------------------------------------------------------------- -// OnToolsOptionsUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsOptionsUI(wxUpdateUIEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnToolsComCfg() -//------------------------------------------------------------------------- -void MainFrame::OnToolsComCfg(wxCommandEvent& event) -{ - wxUnusedVar(event); - - ComPortsDlg *dlg = new ComPortsDlg(NULL); - - dlg->ShowModal(); - - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsComCfgUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsComCfgUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning); -} - -//------------------------------------------------------------------------- -// OnToolsPlugInCfg() -//------------------------------------------------------------------------- -void MainFrame::OnToolsPlugInCfg(wxCommandEvent& event) -{ - wxUnusedVar(event); - PlugInDlg *dlg = new PlugInDlg(wxGetApp().m_plugInName, wxGetApp().m_numPlugInParam, wxGetApp().m_plugInParamName); - dlg->ShowModal(); - delete dlg; -} - -void MainFrame::OnToolsPlugInCfgUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning && wxGetApp().m_plugIn); -} - - -//------------------------------------------------------------------------- -// OnHelpCheckUpdates() -//------------------------------------------------------------------------- -void MainFrame::OnHelpCheckUpdates(wxCommandEvent& event) -{ - wxMessageBox("Got Click!", "OnHelpCheckUpdates", wxOK); - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnHelpCheckUpdatesUI() -//------------------------------------------------------------------------- -void MainFrame::OnHelpCheckUpdatesUI(wxUpdateUIEvent& event) -{ - event.Enable(false); -} - -//------------------------------------------------------------------------- -//OnHelpAbout() -//------------------------------------------------------------------------- -void MainFrame::OnHelpAbout(wxCommandEvent& event) -{ - wxUnusedVar(event); - wxString msg; - msg.Printf( wxT("FreeDV %s\n\n") - wxT("Open Source Digital Voice\n\n") - wxT("For Help and Support visit: http://freedv.org\n\n") - - wxT("GNU Public License V2.1\n\n") - wxT("Copyright (c) David Witten KD0EAG and David Rowe VK5DGR\n\n") - wxT("svn revision: %s\n"), FREEDV_VERSION, SVN_REVISION); - - wxMessageBox(msg, wxT("About"), wxOK | wxICON_INFORMATION, this); -} - - -// Attempt to talk to rig using Hamlib - -bool MainFrame::OpenHamlibRig() { - if (wxGetApp().m_boolHamlibUseForPTT != true) - return false; - if (wxGetApp().m_intHamlibRig == 0) - return false; - if (wxGetApp().m_hamlib == NULL) - return false; - - int rig = wxGetApp().m_intHamlibRig; - wxString port = wxGetApp().m_strHamlibSerialPort; - int serial_rate = wxGetApp().m_intHamlibSerialRate; - bool status = wxGetApp().m_hamlib->connect(rig, port.mb_str(wxConvUTF8), serial_rate); - if (status == false) - wxMessageBox("Couldn't connect to Radio with hamlib", wxT("Error"), wxOK | wxICON_ERROR, this); - - return status; -} - -//------------------------------------------------------------------------- -// OnTogBtnOnOff() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnOnOff(wxCommandEvent& event) -{ - wxString startStop = m_togBtnOnOff->GetLabel(); - - // we are attempting to start - - if (startStop.IsSameAs("Start")) - { - // - // Start Running ------------------------------------------------- - // - - // modify some button states when running - - m_togBtnSplit->Enable(); - m_togBtnAnalog->Enable(); - m_togBtnOnOff->SetLabel(wxT("Stop")); - m_btnTogPTT->Enable(); - m_togBtnVoiceKeyer->Enable(); - vk_state = VK_IDLE; - - m_rb1600->Disable(); - //m_rb700b->Disable(); - m_rb700c->Disable(); - m_rb800xa->Disable(); - if (m_rbPlugIn != NULL) - m_rbPlugIn->Disable(); - - // determine what mode we are using - - if (m_rb1600->GetValue()) { - g_mode = FREEDV_MODE_1600; - g_Nc = 16; - m_panelScatter->setNc(g_Nc+1); /* +1 for BPSK pilot */ - } - #ifdef DISABLED - if (m_rb700b->GetValue()) { - g_mode = FREEDV_MODE_700B; - g_Nc = 14; - if (wxGetApp().m_FreeDV700Combine) { - m_panelScatter->setNc(g_Nc/2); /* diversity combnation */ - } - else { - m_panelScatter->setNc(g_Nc); - } - } - #endif - if (m_rb700c->GetValue()) { - g_mode = FREEDV_MODE_700C; - g_Nc = 14; - if (wxGetApp().m_FreeDV700Combine) { - m_panelScatter->setNc(g_Nc/2); /* diversity combnation */ - } - else { - m_panelScatter->setNc(g_Nc); - } - } - if (m_rb800xa->GetValue()) { - g_mode = FREEDV_MODE_800XA; - } - if (m_rbPlugIn != NULL) { - if (m_rbPlugIn->GetValue()) { - g_mode = -1; /* TODO; a better way of handling (enumarating?) non-freedv modes */ - - /* scale plots assuming Fs = 8000 Hz for now */ - - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ)/8000.0); - m_panelWaterfall->setFs(8000.0); - - (wxGetApp().m_plugin_startfp)(wxGetApp().m_plugInStates); - } - } - - if (g_mode != -1) { - // init freedv states - - g_pfreedv = freedv_open(g_mode); - freedv_set_callback_txt(g_pfreedv, &my_put_next_rx_char, &my_get_next_tx_char, NULL); - - freedv_set_callback_error_pattern(g_pfreedv, my_freedv_put_error_pattern, (void*)m_panelTestFrameErrors); - g_error_pattern_fifo = fifo_create(2*freedv_get_sz_error_pattern(g_pfreedv)+1); - g_error_hist = new short[FDMDV_NC_MAX*2]; - g_error_histn = new short[FDMDV_NC_MAX*2]; - int i; - for(i=0; i<2*FDMDV_NC_MAX; i++) { - g_error_hist[i] = 0; - g_error_histn[i] = 0; - } - - assert(g_pfreedv != NULL); - - // init Codec 2 LPC Post Filter - - codec2_set_lpc_post_filter(freedv_get_codec2(g_pfreedv), - wxGetApp().m_codec2LPCPostFilterEnable, - wxGetApp().m_codec2LPCPostFilterBassBoost, - wxGetApp().m_codec2LPCPostFilterBeta, - wxGetApp().m_codec2LPCPostFilterGamma); - - // Init Speex pre-processor states - // by inspecting Speex source it seems that only denoiser is on be default - - g_speex_st = speex_preprocess_state_init(freedv_get_n_speech_samples(g_pfreedv), FS); - - // adjust spectrum and waterfall freq scaling base on mode - - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(freedv_get_modem_sample_rate(g_pfreedv)/2))); - m_panelWaterfall->setFs(freedv_get_modem_sample_rate(g_pfreedv)); - - // Init text msg decoding - - freedv_set_varicode_code_num(g_pfreedv, wxGetApp().m_textEncoding); - } - - modem_stats_open(&g_stats); - g_State = g_prev_State = 0; - g_snr = 0.0; - g_half_duplex = wxGetApp().m_boolHalfDuplex; - - if (g_mode == FREEDV_MODE_800XA) { - m_panelScatter->setEyeScatter(PLOT_SCATTER_MODE_EYE); - } - else { - m_panelScatter->setEyeScatter(PLOT_SCATTER_MODE_SCATTER); - } - - m_pcallsign = m_callsign; - memset(m_callsign, 0, sizeof(m_callsign)); -#ifdef __UDP_EXPERIMENTAL__ - m_checksumGood = m_checksumBad = 0; - wxString s; - s.Printf("%d", m_checksumGood); - m_txtChecksumGood->SetLabel(s); - s.Printf("%d", m_checksumBad); - m_txtChecksumBad->SetLabel(s); -#endif - - m_maxLevel = 0; - m_textLevel->SetLabel(wxT("")); - m_gaugeLevel->SetValue(0); - - //printf("m_textEncoding = %d\n", wxGetApp().m_textEncoding); - //printf("g_stats.snr: %f\n", g_stats.snr_est); - - // attempt to start PTT ...... - - if (wxGetApp().m_boolHamlibUseForPTT) - OpenHamlibRig(); - if (wxGetApp().m_boolUseSerialPTT) { - OpenSerialPort(); - } - - // attempt to start sound cards and tx/rx processing - - startRxStream(); - - if (m_RxRunning) - { -#ifdef _USE_TIMER - m_plotTimer.Start(_REFRESH_TIMER_PERIOD, wxTIMER_CONTINUOUS); -#endif // _USE_TIMER - } -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"start"); processTxtEvent(e); -#endif - } - - // Stop was pressed or start up failed - - if (startStop.IsSameAs("Stop") || !m_RxRunning ) { - - // - // Stop Running ------------------------------------------------- - // - -#ifdef __UDP_EXPERIMENTAL__ - optionsDlg->SetSpamTimerLight(false); -#endif - -#ifdef _USE_TIMER - m_plotTimer.Stop(); -#endif // _USE_TIMER - - // ensure we are not transmitting and shut down audio processing - - if (wxGetApp().m_boolHamlibUseForPTT) { - Hamlib *hamlib = wxGetApp().m_hamlib; - wxString hamlibError; - if (wxGetApp().m_boolHamlibUseForPTT && hamlib != NULL) { - if (hamlib->ptt(false, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - hamlib->close(); - } - } - - if (wxGetApp().m_boolUseSerialPTT) { - CloseSerialPort(); - } - - m_btnTogPTT->SetValue(false); - VoiceKeyerProcessEvent(VK_SPACE_BAR); - - stopRxStream(); - modem_stats_close(&g_stats); - - // free up states, clean up - - if (g_mode == -1) { - // PlugIn clean up - (wxGetApp().m_plugin_stopfp)(wxGetApp().m_plugInStates); - } - else { - // FreeDV clean up - delete g_error_hist; - delete g_error_histn; - fifo_destroy(g_error_pattern_fifo); - freedv_close(g_pfreedv); - speex_preprocess_state_destroy(g_speex_st); - } - - m_newMicInFilter = m_newSpkOutFilter = true; - - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - m_btnTogPTT->Disable(); - m_togBtnVoiceKeyer->Disable(); - m_togBtnOnOff->SetLabel(wxT("Start")); - m_rb1600->Enable(); - //m_rb700b->Enable(); - m_rb700c->Enable(); - m_rb800xa->Enable(); - if (m_rbPlugIn != NULL) - m_rbPlugIn->Enable(); - -#ifdef DISABLED_FEATURE - m_rb700->Enable(); - m_rb1400old->Enable(); - m_rb1600Wide->Enable(); - m_rb1400->Enable(); - m_rb2000->Enable(); -#endif -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"stop"); processTxtEvent(e); -#endif - } -} - -//------------------------------------------------------------------------- -// stopRxStream() -//------------------------------------------------------------------------- -void MainFrame::stopRxStream() -{ - if(m_RxRunning) - { - m_RxRunning = false; - - fprintf(stderr, "waiting for thread to stop\n"); - m_txRxThread->m_run = 0; - m_txRxThread->Wait(); - fprintf(stderr, "thread stopped\n"); - - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(m_rxOutPa != m_rxInPa) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - - if (g_nSoundCards == 2) { - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - if(m_txInPa != m_txOutPa) { - m_txOutPa->stop(); - m_txOutPa->streamClose(); - delete m_txOutPa; - } - } - - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - } -} - -void MainFrame::destroy_fifos(void) -{ - fifo_destroy(g_rxUserdata->infifo1); - fifo_destroy(g_rxUserdata->outfifo1); - fifo_destroy(g_rxUserdata->infifo2); - fifo_destroy(g_rxUserdata->outfifo2); - fifo_destroy(g_rxUserdata->rxinfifo); - fifo_destroy(g_rxUserdata->rxoutfifo); -} - -void MainFrame::destroy_src(void) -{ - src_delete(g_rxUserdata->insrc1); - src_delete(g_rxUserdata->outsrc1); - src_delete(g_rxUserdata->insrc2); - src_delete(g_rxUserdata->outsrc2); - src_delete(g_rxUserdata->insrcsf); -} - -void MainFrame::initPortAudioDevice(PortAudioWrap *pa, int inDevice, int outDevice, - int soundCard, int sampleRate, int inputChannels) -{ - // Note all of the wrapper functions below just set values in a - // portaudio struct so can't return any errors. So no need to trap - // any errors in this function. - - // init input params - - pa->setInputDevice(inDevice); - if(inDevice != paNoDevice) { - pa->setInputChannelCount(inputChannels); // stereo input - pa->setInputSampleFormat(PA_SAMPLE_TYPE); - pa->setInputLatency(pa->getInputDefaultLowLatency()); - pa->setInputHostApiStreamInfo(NULL); - } - - pa->setOutputDevice(paNoDevice); - - // init output params - - pa->setOutputDevice(outDevice); - if(outDevice != paNoDevice) { - pa->setOutputChannelCount(2); // stereo output - pa->setOutputSampleFormat(PA_SAMPLE_TYPE); - pa->setOutputLatency(pa->getOutputDefaultLowLatency()); - pa->setOutputHostApiStreamInfo(NULL); - } - - // init params that affect input and output - - /* - On Linux, setting this to wxGetApp().m_framesPerBuffer caused - intermittant break up on the audio from my IC7200 on Ubuntu 14. - After a day of bug hunting I found that 0, as recommended by the - PortAudio docunmentation, fixed the problem. - */ - - //pa->setFramesPerBuffer(wxGetApp().m_framesPerBuffer); - pa->setFramesPerBuffer(0); - - pa->setSampleRate(sampleRate); - pa->setStreamFlags(paClipOff); -} - -//------------------------------------------------------------------------- -// startRxStream() -//------------------------------------------------------------------------- -void MainFrame::startRxStream() -{ - int src_error; - const PaDeviceInfo *deviceInfo1 = NULL, *deviceInfo2 = NULL; - int inputChannels1, inputChannels2; - bool two_rx=false; - bool two_tx=false; - - if(!m_RxRunning) { - m_RxRunning = true; - - if(Pa_Initialize()) - { - wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK); - } - - m_rxInPa = new PortAudioWrap(); - if(g_soundCard1InDeviceNum != g_soundCard1OutDeviceNum) - two_rx=true; - if(g_soundCard2InDeviceNum != g_soundCard2OutDeviceNum) - two_tx=true; - - fprintf(g_logfile, "two_rx: %d two_tx: %d\n", two_rx, two_tx); - if(two_rx) - m_rxOutPa = new PortAudioWrap(); - else - m_rxOutPa = m_rxInPa; - - if (g_nSoundCards == 0) { - wxMessageBox(wxT("No Sound Cards configured, use Tools - Audio Config to configure"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - - // Init Sound card 1 ---------------------------------------------- - // sanity check on sound card device numbers - - if ((m_rxInPa->getDeviceCount() <= g_soundCard1InDeviceNum) || - (m_rxOutPa->getDeviceCount() <= g_soundCard1OutDeviceNum)) { - wxMessageBox(wxT("Sound Card 1 not present"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - - // work out how many input channels this device supports. - - deviceInfo1 = Pa_GetDeviceInfo(g_soundCard1InDeviceNum); - if (deviceInfo1 == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card 1"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - if (deviceInfo1->maxInputChannels == 1) - inputChannels1 = 1; - else - inputChannels1 = 2; - - if(two_rx) { - initPortAudioDevice(m_rxInPa, g_soundCard1InDeviceNum, paNoDevice, 1, - g_soundCard1SampleRate, inputChannels1); - initPortAudioDevice(m_rxOutPa, paNoDevice, g_soundCard1OutDeviceNum, 1, - g_soundCard1SampleRate, inputChannels1); - } - else - initPortAudioDevice(m_rxInPa, g_soundCard1InDeviceNum, g_soundCard1OutDeviceNum, 1, - g_soundCard1SampleRate, inputChannels1); - - // Init Sound Card 2 ------------------------------------------------ - - if (g_nSoundCards == 2) { - - m_txInPa = new PortAudioWrap(); - if(two_tx) - m_txOutPa = new PortAudioWrap(); - else - m_txOutPa = m_txInPa; - - // sanity check on sound card device numbers - - //printf("m_txInPa->getDeviceCount(): %d\n", m_txInPa->getDeviceCount()); - //printf("g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - //printf("g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - - if ((m_txInPa->getDeviceCount() <= g_soundCard2InDeviceNum) || - (m_txOutPa->getDeviceCount() <= g_soundCard2OutDeviceNum)) { - wxMessageBox(wxT("Sound Card 2 not present"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - m_RxRunning = false; - return; - } - - deviceInfo2 = Pa_GetDeviceInfo(g_soundCard2InDeviceNum); - if (deviceInfo2 == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card 1"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - m_RxRunning = false; - return; - } - if (deviceInfo2->maxInputChannels == 1) - inputChannels2 = 1; - else - inputChannels2 = 2; - - if(two_tx) { - initPortAudioDevice(m_txInPa, g_soundCard2InDeviceNum, paNoDevice, 2, - g_soundCard2SampleRate, inputChannels2); - initPortAudioDevice(m_txOutPa, paNoDevice, g_soundCard2OutDeviceNum, 2, - g_soundCard2SampleRate, inputChannels2); - } - else - initPortAudioDevice(m_txInPa, g_soundCard2InDeviceNum, g_soundCard2OutDeviceNum, 2, - g_soundCard2SampleRate, inputChannels2); - } - - // Init call back data structure ---------------------------------------------- - - g_rxUserdata = new paCallBackData; - g_rxUserdata->inputChannels1 = inputChannels1; - if (deviceInfo2 != NULL) - g_rxUserdata->inputChannels2 = inputChannels2; - - // init sample rate conversion states - - g_rxUserdata->insrc1 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrc1 != NULL); - g_rxUserdata->outsrc1 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->outsrc1 != NULL); - g_rxUserdata->insrc2 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrc2 != NULL); - g_rxUserdata->outsrc2 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->outsrc2 != NULL); - - g_rxUserdata->insrcsf = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrcsf != NULL); - - // create FIFOs used to interface between different buffer sizes - - g_rxUserdata->infifo1 = fifo_create(8*N48); - g_rxUserdata->outfifo1 = fifo_create(10*N48); - g_rxUserdata->outfifo2 = fifo_create(8*N48); - g_rxUserdata->infifo2 = fifo_create(8*N48); - printf("N48: %d\n", N48); - - g_rxUserdata->rxinfifo = fifo_create(10 * N8); - g_rxUserdata->rxoutfifo = fifo_create(10 * N8); - - // Init Equaliser Filters ------------------------------------------------------ - - m_newMicInFilter = m_newSpkOutFilter = true; - designEQFilters(g_rxUserdata); - g_rxUserdata->micInEQEnable = wxGetApp().m_MicInEQEnable; - g_rxUserdata->spkOutEQEnable = wxGetApp().m_SpkOutEQEnable; - - // optional tone in left channel to reliably trigger vox - - g_rxUserdata->leftChannelVoxTone = wxGetApp().m_leftChannelVoxTone; - g_rxUserdata->voxTonePhase = 0; - - // Start sound card 1 ---------------------------------------------------------- - - m_rxInPa->setUserData(g_rxUserdata); - m_rxErr = m_rxInPa->setCallback(rxCallback); - - m_rxErr = m_rxInPa->streamOpen(); - - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Open/Setup error."), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - m_rxErr = m_rxInPa->streamStart(); - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Stream Start Error."), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - // Start separate output stream if needed - - if(two_rx) { - m_rxOutPa->setUserData(g_rxUserdata); - m_rxErr = m_rxOutPa->setCallback(rxCallback); - - m_rxErr = m_rxOutPa->streamOpen(); - - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Second Stream Open/Setup error."), wxT("Error"), wxOK); - delete m_rxInPa; - delete m_rxOutPa; - delete m_txOutPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - m_rxErr = m_rxOutPa->streamStart(); - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Second Stream Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - delete m_rxOutPa; - delete m_txOutPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - } - - // Start sound card 2 ---------------------------------------------------------- - - if (g_nSoundCards == 2) { - - // question: can we use same callback data - // (g_rxUserdata)or both sound card callbacks? Is there a - // chance of them both being called at the same time? We - // could need a mutex ... - - m_txInPa->setUserData(g_rxUserdata); - m_txErr = m_txInPa->setCallback(txCallback); - m_txErr = m_txInPa->streamOpen(); - - if(m_txErr != paNoError) { - fprintf(stderr, "Err: %d\n", m_txErr); - wxMessageBox(wxT("Sound Card 2 Open/Setup error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - m_txErr = m_txInPa->streamStart(); - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - // Start separate output stream if needed - - if (two_tx) { - - // question: can we use same callback data - // (g_rxUserdata)or both sound card callbacks? Is there a - // chance of them both being called at the same time? We - // could need a mutex ... - - m_txOutPa->setUserData(g_rxUserdata); - m_txErr = m_txOutPa->setCallback(txCallback); - m_txErr = m_txOutPa->streamOpen(); - - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Second Stream Open/Setup error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - m_txErr = m_txOutPa->streamStart(); - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Second Stream Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - } - } - - // start tx/rx processing thread - - m_txRxThread = new txRxThread; - - if ( m_txRxThread->Create() != wxTHREAD_NO_ERROR ) - { - wxLogError(wxT("Can't create thread!")); - } - - m_txRxThread->SetPriority(WXTHREAD_MAX_PRIORITY); - - if ( m_txRxThread->Run() != wxTHREAD_NO_ERROR ) - { - wxLogError(wxT("Can't start thread!")); - } - - } -} - - -void MainFrame::processTxtEvent(char event[]) { - int rule = 0; - - //printf("processTxtEvent:\n"); - //printf(" event: %s\n", event); - - // process with regexp and issue system command - - // Each regexp in our list is separated by a newline. We want to try all of them. - - wxString event_str(event); - int match_end, replace_end; - match_end = replace_end = 0; - wxString regexp_match_list = wxGetApp().m_events_regexp_match; - wxString regexp_replace_list = wxGetApp().m_events_regexp_replace; - - bool found_match = false; - - while (((match_end = regexp_match_list.Find('\n')) != wxNOT_FOUND) && (rule < MAX_EVENT_RULES)) { - //printf("match_end: %d\n", match_end); - if ((replace_end = regexp_replace_list.Find('\n')) != wxNOT_FOUND) { - //printf("replace_end = %d\n", replace_end); - - // candidate match and replace regexps strings exist, so lets try them - - wxString regexp_match = regexp_match_list.SubString(0, match_end-1); - wxString regexp_replace = regexp_replace_list.SubString(0, replace_end-1); - //printf("match: %s replace: %s\n", (const char *)regexp_match.c_str(), (const char *)regexp_replace.c_str()); - wxRegEx re(regexp_match); - //printf(" checking for match against: %s\n", (const char *)regexp_match.c_str()); - - // if we found a match, lets run the replace regexp and issue the system command - - wxString event_str_rep = event_str; - - if (re.Replace(&event_str_rep, regexp_replace) != 0) { - fprintf(stderr, " found match! event_str: %s\n", (const char *)event_str.c_str()); - found_match = true; - - bool enableSystem = false; - if (wxGetApp().m_events) - enableSystem = true; - - // no syscall if spam timer still running - - if (spamTimer[rule].IsRunning()) { - enableSystem = false; - fprintf(stderr, " spam timer running\n"); - } - - const char *event_out = event_str_rep.ToUTF8(); - wxString event_out_with_return_code; - - if (enableSystem) { - int ret = wxExecute(event_str_rep); - event_out_with_return_code.Printf(_T("%s -> returned %d"), event_out, ret); - spamTimer[rule].Start((wxGetApp().m_events_spam_timer)*1000, wxTIMER_ONE_SHOT); - } - else - event_out_with_return_code.Printf(_T("%s T: %d"), event_out, spamTimer[rule].IsRunning()); - - // update event log GUI if currently displayed - - if (optionsDlg != NULL) { - optionsDlg->updateEventLog(wxString(event), event_out_with_return_code); - } - } - } - regexp_match_list = regexp_match_list.SubString(match_end+1, regexp_match_list.length()); - regexp_replace_list = regexp_replace_list.SubString(replace_end+1, regexp_replace_list.length()); - - rule++; - } - - if ((optionsDlg != NULL) && !found_match) { - optionsDlg->updateEventLog(wxString(event), _("")); - } -} - - -#define SBQ_MAX_ARGS 4 - -void *MainFrame::designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q) -{ - char *arg[SBQ_MAX_ARGS]; - char argstorage[SBQ_MAX_ARGS][80]; - void *sbq; - int i, argc; - - assert((strcmp(filterType, "bass") == 0) || - (strcmp(filterType, "treble") == 0) || - (strcmp(filterType, "equalizer") == 0)); - - for(i=0; isbqMicInBass = designAnEQFilter("bass", wxGetApp().m_MicInBassFreqHz, wxGetApp().m_MicInBassGaindB); - cb->sbqMicInTreble = designAnEQFilter("treble", wxGetApp().m_MicInTrebleFreqHz, wxGetApp().m_MicInTrebleGaindB); - cb->sbqMicInMid = designAnEQFilter("equalizer", wxGetApp().m_MicInMidFreqHz, wxGetApp().m_MicInMidGaindB, wxGetApp().m_MicInMidQ); - } - - // init Spk Out Equaliser Filters - - if (m_newSpkOutFilter) { - //printf("designing new Spk Out filters\n"); - //printf("designEQFilters: wxGetApp().m_SpkOutBassFreqHz: %f\n",wxGetApp().m_SpkOutBassFreqHz); - cb->sbqSpkOutBass = designAnEQFilter("bass", wxGetApp().m_SpkOutBassFreqHz, wxGetApp().m_SpkOutBassGaindB); - cb->sbqSpkOutTreble = designAnEQFilter("treble", wxGetApp().m_SpkOutTrebleFreqHz, wxGetApp().m_SpkOutTrebleGaindB); - cb->sbqSpkOutMid = designAnEQFilter("equalizer", wxGetApp().m_SpkOutMidFreqHz, wxGetApp().m_SpkOutMidGaindB, wxGetApp().m_SpkOutMidQ); - } -} - -void MainFrame::deleteEQFilters(paCallBackData *cb) -{ - if (m_newMicInFilter) { - sox_biquad_destroy(cb->sbqMicInBass); - sox_biquad_destroy(cb->sbqMicInTreble); - sox_biquad_destroy(cb->sbqMicInMid); - } - if (m_newSpkOutFilter) { - sox_biquad_destroy(cb->sbqSpkOutBass); - sox_biquad_destroy(cb->sbqSpkOutTreble); - sox_biquad_destroy(cb->sbqSpkOutMid); - } -} - -// returns number of output samples generated by resampling -int resample(SRC_STATE *src, - short output_short[], - short input_short[], - int output_sample_rate, - int input_sample_rate, - int length_output_short, // maximum output array length in samples - int length_input_short - ) -{ - SRC_DATA src_data; - float input[N48*4]; - float output[N48*4]; - int ret; - - assert(src != NULL); - assert(length_input_short <= N48*4); - assert(length_output_short <= N48*4); - - src_short_to_float_array(input_short, input, length_input_short); - - src_data.data_in = input; - src_data.data_out = output; - src_data.input_frames = length_input_short; - src_data.output_frames = length_output_short; - src_data.end_of_input = 0; - src_data.src_ratio = (float)output_sample_rate/input_sample_rate; - - ret = src_process(src, &src_data); - assert(ret == 0); - - assert(src_data.output_frames_gen <= length_output_short); - src_float_to_short_array(output, output_short, src_data.output_frames_gen); - - return src_data.output_frames_gen; -} - - -// Decimates samples using an algorithm that produces nice plots of -// speech signals at a low sample rate. We want a low sample rate so -// we don't hammer the graphics system too hard. Saves decimated data -// to a fifo for plotting on screen. -void resample_for_plot(struct FIFO *plotFifo, short buf[], int length, int fs) -{ - int decimation = fs/WAVEFORM_PLOT_FS; - int nSamples, sample; - int i, st, en, max, min; - short dec_samples[length]; - - nSamples = length/decimation; - - for(sample = 0; sample < nSamples; sample += 2) - { - st = decimation*sample; - en = decimation*(sample+2); - max = min = 0; - for(i=st; i buf[i]) min = buf[i]; - } - dec_samples[sample] = max; - dec_samples[sample+1] = min; - } - fifo_write(plotFifo, dec_samples, nSamples); -} - -void txRxProcessing() -{ - - paCallBackData *cbData = g_rxUserdata; - - // Buffers re-used by tx and rx processing - // signals in in48k/out48k are at a maximum sample rate of 48k, could be 44.1kHz - // depending on sound hardware. - - short in8k_short[4*N8]; - short in48k_short[4*N48]; - short out8k_short[4*N8]; - short out48k_short[4*N48]; - int nout, samplerate, n_samples; - - //fprintf(g_logfile, "start infifo1: %5d outfifo2: %5d\n", fifo_used(cbData->infifo1), fifo_used(cbData->outfifo2)); - - // FreeDV 700 uses a modem sample rate of 7500 Hz which requires some special treatment - - if (g_analog || g_mode == -1) - samplerate = FS; - else - samplerate = freedv_get_modem_sample_rate(g_pfreedv); - - // - // RX side processing -------------------------------------------- - // - - // while we have enough input samples available ... - - int nsam = g_soundCard1SampleRate * (float)N8/FS; - assert(nsam <= N48); - g_mutexProtectingCallbackData.Lock(); - while ((fifo_read(cbData->infifo1, in48k_short, nsam) == 0) && ((g_half_duplex && !g_tx) || !g_half_duplex)) - { - g_mutexProtectingCallbackData.Unlock(); - unsigned int n8k; - - n8k = resample(cbData->insrc1, in8k_short, in48k_short, samplerate, g_soundCard1SampleRate, N8, nsam); - assert(n8k <= N8); - - // optionally save "from radio" signal (write demod input to file) - // Really useful for testing and development as it allows us - // to repeat tests using off air signals - - g_mutexProtectingCallbackData.Lock(); - if (g_recFileFromRadio && (g_sfRecFile != NULL)) { - //printf("g_recFromRadioSamples: %d n8k: %d \n", g_recFromRadioSamples); - if (g_recFromRadioSamples < n8k) { - sf_write_short(g_sfRecFile, in8k_short, g_recFromRadioSamples); - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_recFileFromRadioEventId ); - // call stop/start record menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - g_recFromRadioSamples = 0; - //printf("finished recording g_recFromRadioSamples: %d n8k: %d!\n", g_recFileFromRadio, n8k); - } - else { - sf_write_short(g_sfRecFile, in8k_short, n8k); - g_recFromRadioSamples -= n8k; - } - } - g_mutexProtectingCallbackData.Unlock(); - - // optionally read "from radio" signal from file (read demod input from file) - - g_mutexProtectingCallbackData.Lock(); - if (g_playFileFromRadio && (g_sfPlayFileFromRadio != NULL)) { - unsigned int nsf = n8k*g_sfFs/samplerate; - short insf_short[nsf]; - unsigned int n = sf_read_short(g_sfPlayFileFromRadio, insf_short, nsf); - n8k = resample(cbData->insrcsf, in8k_short, insf_short, samplerate, g_sfFs, N8, nsf); - //fprintf(g_logfile, "n: %d nsf: %d n8k: %d samplerate: %d\n", n, nsf, n8k, samplerate); - assert(n8k <= N8); - - if (n == 0) { - if (g_loopPlayFileFromRadio) - sf_seek(g_sfPlayFileFromRadio, 0, SEEK_SET); - else { - printf("playFileFromRadio finished, issuing event!\n"); - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_playFileFromRadioEventId ); - // call stop/start play menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - } - } - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotDemodInFifo, in8k_short, n8k, samplerate); - - if (g_mode != -1) { - // send latest squelch level to FreeDV API, as it handles squelch internally - - freedv_set_squelch_en(g_pfreedv, g_SquelchActive); - freedv_set_snr_squelch_thresh(g_pfreedv, g_SquelchLevel); - } - - // Optional tone interferer - - if (wxGetApp().m_tone) { - float w = 2.0*M_PI*wxGetApp().m_tone_freq_hz/freedv_get_modem_sample_rate(g_pfreedv); - float s; - unsigned int i; - for(i=0; isnr_squelch_thresh); - - // compute rx spectrum - do here so update rate in constant - - COMP rx_fdm[n8k]; - float rx_spec[MODEM_STATS_NSPEC]; - unsigned int i; - - for(i=0; irxinfifo, in8k_short, n8k); - per_frame_rx_processing(cbData->rxoutfifo, cbData->rxinfifo); - memset(out8k_short, 0, sizeof(short)*N8); - fifo_read(cbData->rxoutfifo, out8k_short, N8); - //printf("out8k_short: %d\n", out8k_short[0]); - } - - - // Optional Spk Out EQ Filtering, need mutex as filter can change at run time - g_mutexProtectingCallbackData.Lock(); - if (cbData->spkOutEQEnable) { - sox_biquad_filter(cbData->sbqSpkOutBass, out8k_short, out8k_short, N8); - sox_biquad_filter(cbData->sbqSpkOutTreble, out8k_short, out8k_short, N8); - sox_biquad_filter(cbData->sbqSpkOutMid, out8k_short, out8k_short, N8); - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotSpeechOutFifo, out8k_short, N8, FS); - - g_mutexProtectingCallbackData.Lock(); - if (g_nSoundCards == 1) { - nout = resample(cbData->outsrc2, out48k_short, out8k_short, g_soundCard1SampleRate, FS, N48, N8); - fifo_write(cbData->outfifo1, out48k_short, nout); - } - else { - nout = resample(cbData->outsrc2, out48k_short, out8k_short, g_soundCard2SampleRate, FS, N48, N8); - fifo_write(cbData->outfifo2, out48k_short, nout); - } - } - g_mutexProtectingCallbackData.Unlock(); - - // - // TX side processing -------------------------------------------- - // - - if ((g_mode != -1) && ((g_nSoundCards == 2) && ((g_half_duplex && g_tx) || !g_half_duplex))) { - int ret; - - // Make sure we have q few frames of modulator output - // samples. This also locks the modulator to the sample rate - // of sound card 1. We want to make sure that modulator - // samples are uninterrupted by differences in sample rate - // between this sound card and sound card 2. - - g_mutexProtectingCallbackData.Lock(); - while((unsigned)fifo_used(cbData->outfifo1) < 6*N48) - { - g_mutexProtectingCallbackData.Unlock(); - - int nsam = g_soundCard2SampleRate * freedv_get_n_speech_samples(g_pfreedv)/FS; - assert(nsam <= 4*N48); - - // infifo2 is written to by another sound card so it may - // over or underflow, but we don't realy care. It will - // just result in a short interruption in audio being fed - // to codec2_enc, possibly making a click every now and - // again in the decoded audio at the other end. - - // zero speech input just in case infifo2 underflows - memset(in48k_short, 0, nsam*sizeof(short)); - fifo_read(cbData->infifo2, in48k_short, nsam); - - nout = resample(cbData->insrc2, in8k_short, in48k_short, FS, g_soundCard2SampleRate, 4*N8, nsam); - - // optionally use file for mic input signal - - g_mutexProtectingCallbackData.Lock(); - if (g_playFileToMicIn && (g_sfPlayFile != NULL)) { - int n = sf_read_short(g_sfPlayFile, in8k_short, nout); - //fprintf(stderr, "n: %d nout: %d\n", n, nout); - if (n != nout) { - if (g_loopPlayFileToMicIn) - sf_seek(g_sfPlayFile, 0, SEEK_SET); - else { - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_playFileToMicInEventId ); - // call stop/start play menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - } - } - } - g_mutexProtectingCallbackData.Unlock(); - - // Optional Speex pre-processor for acoustic noise reduction - - if (wxGetApp().m_speexpp_enable) { - speex_preprocess_run(g_speex_st, in8k_short); - } - - // Optional Mic In EQ Filtering, need mutex as filter can change at run time - - g_mutexProtectingCallbackData.Lock(); - if (cbData->micInEQEnable) { - sox_biquad_filter(cbData->sbqMicInBass, in8k_short, in8k_short, nout); - sox_biquad_filter(cbData->sbqMicInTreble, in8k_short, in8k_short, nout); - sox_biquad_filter(cbData->sbqMicInMid, in8k_short, in8k_short, nout); - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotSpeechInFifo, in8k_short, nout, FS); - - n_samples = freedv_get_n_nom_modem_samples(g_pfreedv); - - if (g_analog) { - n_samples = freedv_get_n_speech_samples(g_pfreedv); - - // Boost the "from mic" -> "to radio" audio in analog - // mode. The need for the gain was found by - // experiment - analog SSB sounded too quiet compared - // to digital. With digital voice we generally drive - // the "to radio" (SSB radio mic input) at about 25% - // of the peak level for normal SSB voice. So we - // introduce 6dB gain to make analog SSB sound the - // same level as the digital. Watch out for clipping. - for(int i=0; i 32767) out = 32767.0; - if (out < -32767) out = -32767.0; - out8k_short[i] = out; - } - } - else { - COMP tx_fdm[freedv_get_n_nom_modem_samples(g_pfreedv)]; - COMP tx_fdm_offset[freedv_get_n_nom_modem_samples(g_pfreedv)]; - int i; - - if (g_mode == FREEDV_MODE_800XA) { - /* 800XA doesn't support complex output just yet */ - freedv_tx(g_pfreedv, out8k_short, in8k_short); - } - else { - freedv_comptx(g_pfreedv, tx_fdm, in8k_short); - - freq_shift_coh(tx_fdm_offset, tx_fdm, g_TxFreqOffsetHz, freedv_get_modem_sample_rate(g_pfreedv), &g_TxFreqOffsetPhaseRect, freedv_get_n_nom_modem_samples(g_pfreedv)); - for(i=0; ioutsrc1, out48k_short, out8k_short, g_soundCard1SampleRate, samplerate, N48*4, n_samples); - g_mutexProtectingCallbackData.Lock(); - ret = fifo_write(cbData->outfifo1, out48k_short, nout); - //fprintf(stderr,"nout: %d ret: %d N48*4: %d\n", nout, ret, N48*4); - - assert(ret != -1); - } - g_mutexProtectingCallbackData.Unlock(); - } - - //fprintf(g_logfile, " end infifo1: %5d outfifo2: %5d\n", fifo_used(cbData->infifo1), fifo_used(cbData->outfifo2)); - -} - -//---------------------------------------------------------------- -// per_frame_rx_processing() -//---------------------------------------------------------------- - -void per_frame_rx_processing( - FIFO *output_fifo, // decoded speech samples - FIFO *input_fifo - ) -{ - #ifdef OLDSPEC - float rx_spec[MODEM_STATS_NSPEC]; - #endif - int i; - - if (g_mode == -1) { - // PlugIn processing --------------------------------------------------- - - int nin = 160; // TODO: hard code for now - some sort of plugin supplied param in future - short input_buf[nin]; - - while (fifo_read(input_fifo, input_buf, nin) == 0) { - (wxGetApp().m_plugin_rx_samplesfp)(wxGetApp().m_plugInStates, input_buf, nin); - } - - #ifdef OLD_SPEC - COMP rx_fdm[nin]; - - for(i=0; isnr); - - // grab extended stats so we can plot spectrum, scatter diagram etc - - freedv_get_modem_extended_stats(g_pfreedv, &g_stats); - - #ifdef OLD_SPEC - // compute rx spectrum - - modem_stats_get_rx_spectrum(&g_stats, rx_spec, rx_fdm, nin_prev); - - // Average rx spectrum data using a simple IIR low pass filter - - for(i = 0; iinputChannels1) - indata[i] = rptr[0]; - if (fifo_write(cbData->infifo1, indata, framesPerBuffer)) { - //fprintf(g_logfile, "infifo1 full\n"); - } - //fifo_write(cbData->outfifo1, indata, framesPerBuffer); - } - - // OK now set up output samples for this callback - - if(wptr) { - //fprintf(g_logfile,"out %ld %d\n", framesPerBuffer, g_out++); - if (fifo_read(cbData->outfifo1, outdata, framesPerBuffer) == 0) { - - // write signal to both channels - - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - if (cbData->leftChannelVoxTone) { - cbData->voxTonePhase += 2.0*M_PI*VOX_TONE_FREQ/g_soundCard1SampleRate; - cbData->voxTonePhase -= 2.0*M_PI*floor(cbData->voxTonePhase/(2.0*M_PI)); - wptr[0] = VOX_TONE_AMP*cos(cbData->voxTonePhase); - //printf("%f %d\n", cbData->voxTonePhase, wptr[0]); - } - else - wptr[0] = outdata[i]; - - wptr[1] = outdata[i]; - } - } - else { - //fprintf(g_logfile, "outfifo1 empty\n"); - // zero output if no data available - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = 0; - wptr[1] = 0; - } - } - } - - return paContinue; -} - - -//------------------------------------------------------------------------- -// txCallback() -//------------------------------------------------------------------------- -int MainFrame::txCallback( - const void *inputBuffer, - void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ) -{ - paCallBackData *cbData = (paCallBackData*)userData; - unsigned int i; - short *rptr = (short*)inputBuffer; - short *wptr = (short*)outputBuffer; - short indata[MAX_FPB]; - short outdata[MAX_FPB]; - - wxMutexLocker lock(g_mutexProtectingCallbackData); - - // if (statusFlags) - // printf("cb2 statusFlags: 0x%x\n", (int)statusFlags); - - // assemble a mono buffer and write to FIFO - - assert(framesPerBuffer < MAX_FPB); - - if(rptr) { - for(i = 0; i < framesPerBuffer; i++, rptr += cbData->inputChannels2) - indata[i] = rptr[0]; - } - - //#define SC2_LOOPBACK -#ifdef SC2_LOOPBACK - //TODO: This doesn't work unless using the same soundcard! - - if(wptr) { - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = indata[i]; - wptr[1] = indata[i]; - } - } - -#else - if(rptr) { - if (fifo_write(cbData->infifo2, indata, framesPerBuffer)) { - //fprintf(g_logfile, "infifo2 full\n"); - } - } - - // OK now set up output samples for this callback - - if(wptr) { - if (fifo_read(cbData->outfifo2, outdata, framesPerBuffer) == 0) { - - // write signal to both channels */ - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = outdata[i]; - wptr[1] = outdata[i]; - } - } - else { - //fprintf(g_logfile, "outfifo2 empty\n"); - // zero output if no data available - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = 0; - wptr[1] = 0; - } - } - } -#endif - return paContinue; -} - -// Callback from plot_spectrum & plot_waterfall. would be nice to -// work out a way to do this without globals. - -void fdmdv2_clickTune(float freq) { - - // The demod is hard-wired to expect a centre frequency of - // FDMDV_FCENTRE. So we want to take the signal centered on the - // click tune freq and re-centre it on FDMDV_FCENTRE. For example - // if the click tune freq is 1500Hz, and FDMDV_CENTRE is 1200 Hz, - // we need to shift the input signal centred on 1500Hz down to - // 1200Hz, an offset of -300Hz. - - // Bit of an "indent" as we are often trying to get it back - // exactly in the centre - - if (fabs(FDMDV_FCENTRE - freq) < 10.0) { - freq = FDMDV_FCENTRE; - fprintf(stderr, "indent!\n"); - } - - if (g_split) { - g_RxFreqOffsetHz = FDMDV_FCENTRE - freq; - } - else { - g_TxFreqOffsetHz = freq - FDMDV_FCENTRE; - g_RxFreqOffsetHz = FDMDV_FCENTRE - freq; - } -} - -//---------------------------------------------------------------- -// OpenSerialPort() -//---------------------------------------------------------------- - -void MainFrame::OpenSerialPort(void) -{ - Serialport *serialport = wxGetApp().m_serialport; - - if(!wxGetApp().m_strRigCtrlPort.IsEmpty()) { - serialport->openport(wxGetApp().m_strRigCtrlPort.c_str(), - wxGetApp().m_boolUseRTS, - wxGetApp().m_boolRTSPos, - wxGetApp().m_boolUseDTR, - wxGetApp().m_boolDTRPos); - if (serialport->isopen()) { - // always start PTT in Rx state - serialport->ptt(false); - } - else { - wxMessageBox("Couldn't open Serial Port", wxT("About"), wxOK | wxICON_ERROR, this); - } - } -} - - -//---------------------------------------------------------------- -// CloseSerialPort() -//---------------------------------------------------------------- - -void MainFrame::CloseSerialPort(void) -{ - Serialport *serialport = wxGetApp().m_serialport; - if (serialport->isopen()) { - // always end with PTT in rx state - - serialport->ptt(false); - serialport->closeport(); - } -} - - -#ifdef __UDP_SUPPORT__ - -//---------------------------------------------------------------- -// PollUDP() - see if any commands on UDP port -//---------------------------------------------------------------- - -// test this puppy with netcat: -// $ echo "hello" | nc -u -q1 localhost 3000 - -int MainFrame::PollUDP(void) -{ - // this will block until message received, so we put it in it's own thread - - char buf[1024]; - char reply[80]; - size_t n = m_udp_sock->RecvFrom(m_udp_addr, buf, sizeof(buf)).LastCount(); - - if (n) { - wxString bufstr = wxString::From8BitData(buf, n); - bufstr.Trim(); - wxString ipaddr = m_udp_addr.IPAddress(); - printf("Received: \"%s\" from %s:%u\n", - (const char *)bufstr.c_str(), - (const char *)ipaddr.c_str(), m_udp_addr.Service()); - - // for security only accept commands from local host - - sprintf(reply,"nope\n"); - if (ipaddr.Cmp(_("127.0.0.1")) == 0) { - - // process commands - - if (bufstr.Cmp(_("restore")) == 0) { - m_schedule_restore = true; // Make Restore happen in main thread to avoid crashing - sprintf(reply,"ok\n"); - } - - wxString itemToSet, val; - if (bufstr.StartsWith(_("set "), &itemToSet)) { - if (itemToSet.StartsWith("txtmsg ", &val)) { - // note: if options dialog is open this will get overwritten - wxGetApp().m_callSign = val; - } - sprintf(reply,"ok\n"); - } - if (bufstr.StartsWith(_("ptton"), &itemToSet)) { - // note: if options dialog is open this will get overwritten - m_btnTogPTT->SetValue(true); - togglePTT(); - sprintf(reply,"ok\n"); - } - if (bufstr.StartsWith(_("pttoff"), &itemToSet)) { - // note: if options dialog is open this will get overwritten - m_btnTogPTT->SetValue(false); - togglePTT(); - sprintf(reply,"ok\n"); - } - - } - else { - printf("We only accept messages from locahost!\n"); - } - - if ( m_udp_sock->SendTo(m_udp_addr, reply, strlen(reply)).LastCount() != strlen(reply)) { - printf("ERROR: failed to send data\n"); - } - } - - return n; -} - -void MainFrame::startUDPThread(void) { - fprintf(stderr, "starting UDP thread!\n"); - m_UDPThread = new UDPThread; - m_UDPThread->mf = this; - if (m_UDPThread->Create() != wxTHREAD_NO_ERROR ) { - wxLogError(wxT("Can't create thread!")); - } - if (m_UDPThread->Run() != wxTHREAD_NO_ERROR ) { - wxLogError(wxT("Can't start thread!")); - delete m_UDPThread; - } -} - -void MainFrame::stopUDPThread(void) { - printf("stopping UDP thread!\n"); - if ((m_UDPThread != NULL) && m_UDPThread->m_run) { - m_UDPThread->m_run = 0; - m_UDPThread->Wait(); - m_UDPThread = NULL; - } -} - -void *UDPThread::Entry() { - printf("UDP thread started!\n"); - while (m_run) { - if (wxGetApp().m_udp_enable) { - printf("m_udp_enable\n"); - mf->m_udp_addr.Service(wxGetApp().m_udp_port); - mf->m_udp_sock = new wxDatagramSocket(mf->m_udp_addr, wxSOCKET_NOWAIT); - - while (m_run && wxGetApp().m_udp_enable) { - if (mf->PollUDP() == 0) { - wxThread::Sleep(20); - } - } - - delete mf->m_udp_sock; - } - wxThread::Sleep(20); - } - return NULL; -} - -#endif - -char my_get_next_tx_char(void *callback_state) { - short ch = 0; - - fifo_read(g_txDataInFifo, &ch, 1); - //fprintf(stderr, "get_next_tx_char: %c\n", (char)ch); - return (char)ch; -} - -void my_put_next_rx_char(void *callback_state, char c) { - short ch = (short)c; - //fprintf(stderr, "put_next_rx_char: %c\n", (char)c); - fifo_write(g_rxDataOutFifo, &ch, 1); -} - -// Callback from FreeDv API to update error plots - -void my_freedv_put_error_pattern(void *state, short error_pattern[], int sz_error_pattern) { - fifo_write(g_error_pattern_fifo, error_pattern, sz_error_pattern); - //fprintf(stderr, "my_freedv_put_error_pattern: sz_error_pattern: %d ret: %d used: %d\n", - // sz_error_pattern, ret, fifo_used(g_error_pattern_fifo) ); -} - -void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin) -{ - COMP foff_rect; - float mag; - int i; - - foff_rect.real = cosf(2.0*M_PI*foff/Fs); - foff_rect.imag = sinf(2.0*M_PI*foff/Fs); - for(i=0; ireal /= mag; - foff_phase_rect->imag /= mag; -} - -int plugin_get_persistant(char name[], char value[]) { - wxString n,v; - int i; - - for(i=0; i. -// -//========================================================================== -#ifndef __FDMDV2_MAIN__ -#define __FDMDV2_MAIN__ - -#include "version.h" -#ifndef _NO_AUTOTOOLS_ -#include "../config.h" -#endif -#include - -#include -#include -#include "wx/rawbmp.h" -#include "wx/file.h" -#include "wx/filename.h" -#include "wx/config.h" -#include -#include "wx/graphics.h" -#include "wx/mstream.h" -#include "wx/wfstream.h" -#include "wx/quantize.h" -#include "wx/scopedptr.h" -#include "wx/stopwatch.h" -#include "wx/versioninfo.h" -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -#include "codec2.h" -#include "codec2_fifo.h" -#include "modem_stats.h" - -#include "topFrame.h" -#include "dlg_ptt.h" -#include "dlg_options.h" -#include "fdmdv2_plot.h" -#include "fdmdv2_plot_scalar.h" -#include "fdmdv2_plot_scatter.h" -#include "fdmdv2_plot_waterfall.h" -#include "fdmdv2_plot_spectrum.h" -#include "fdmdv2_pa_wrapper.h" -#include "sndfile.h" -#include "portaudio.h" -#include "dlg_audiooptions.h" -#include "dlg_filter.h" -#include "dlg_options.h" -#include "varicode.h" -#include "sox_biquad.h" -#include "comp_prim.h" -#include "dlg_plugin.h" -#include "hamlib.h" -#include "serialport.h" - -#define _USE_TIMER 1 -#define _USE_ONIDLE 1 -#define _DUMMY_DATA 1 -//#define _AUDIO_PASSTHROUGH 1 -#define _REFRESH_TIMER_PERIOD (DT*1000) -//#define _USE_ABOUT_DIALOG 1 - -enum { - ID_START = wxID_HIGHEST, - ID_TIMER_WATERFALL, - ID_TIMER_SPECTRUM, - ID_TIMER_SCATTER, - ID_TIMER_SCALAR - }; - -#define EXCHANGE_DATA_IN 0 -#define EXCHANGE_DATA_OUT 1 - - -extern int g_nSoundCards; -extern int g_soundCard1InDeviceNum; -extern int g_soundCard1OutDeviceNum; -extern int g_soundCard1SampleRate; -extern int g_soundCard2InDeviceNum; -extern int g_soundCard2OutDeviceNum; -extern int g_soundCard2SampleRate; - -// Voice Keyer Constants - -#define VK_SYNC_WAIT_TIME 5.0 - -// Voice Keyer States - -#define VK_IDLE 0 -#define VK_TX 1 -#define VK_RX 2 -#define VK_SYNC_WAIT 3 - -// Voice Keyer Events - -#define VK_START 0 -#define VK_SPACE_BAR 1 -#define VK_PLAY_FINISHED 2 -#define VK_DT 3 -#define VK_SYNC 4 - -class MainFrame; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainApp -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MainApp : public wxApp -{ - public: - virtual bool OnInit(); - virtual int OnExit(); - - wxString m_strVendName; - wxString m_StrAppName; - - wxString m_textNumChOut; - wxString m_textNumChIn; - - wxString m_strRxInAudio; - wxString m_strRxOutAudio; - wxString m_textVoiceInput; - wxString m_textVoiceOutput; - wxString m_strSampleRate; - wxString m_strBitrate; - - // PTT ----------------------------------- - - bool m_boolHalfDuplex; - - wxString m_txtVoiceKeyerWaveFilePath; - wxString m_txtVoiceKeyerWaveFile; - int m_intVoiceKeyerRxPause; - int m_intVoiceKeyerRepeats; - - bool m_boolHamlibUseForPTT; - unsigned int m_intHamlibRig; - wxString m_strHamlibSerialPort; - unsigned int m_intHamlibSerialRate; - Hamlib *m_hamlib; - - bool m_boolUseSerialPTT; - wxString m_strRigCtrlPort; - bool m_boolUseRTS; - bool m_boolRTSPos; - bool m_boolUseDTR; - bool m_boolDTRPos; - Serialport *m_serialport; - - // Play/Rec files - - wxString m_playFileToMicInPath; - wxString m_recFileFromRadioPath; - unsigned int m_recFileFromRadioSecs; - wxString m_playFileFromRadioPath; - - // Options dialog - - wxString m_callSign; - bool m_events; - int m_events_spam_timer; - unsigned int m_textEncoding; - bool m_enable_checksum; - wxString m_events_regexp_match; - wxString m_events_regexp_replace; - - bool m_snrSlow; - - // LPC Post Filter - bool m_codec2LPCPostFilterEnable; - bool m_codec2LPCPostFilterBassBoost; - float m_codec2LPCPostFilterGamma; - float m_codec2LPCPostFilterBeta; - - // Speex Pre-Processor - bool m_speexpp_enable; - - // Mic In Equaliser - float m_MicInBassFreqHz; - float m_MicInBassGaindB; - float m_MicInTrebleFreqHz; - float m_MicInTrebleGaindB; - float m_MicInMidFreqHz; - float m_MicInMidGaindB; - float m_MicInMidQ; - bool m_MicInEQEnable; - - // Spk Out Equaliser - float m_SpkOutBassFreqHz; - float m_SpkOutBassGaindB; - float m_SpkOutTrebleFreqHz; - float m_SpkOutTrebleGaindB; - float m_SpkOutMidFreqHz; - float m_SpkOutMidGaindB; - float m_SpkOutMidQ; - bool m_SpkOutEQEnable; - - // Flags for displaying windows - int m_show_wf; - int m_show_spect; - int m_show_scatter; - int m_show_timing; - int m_show_freq; - int m_show_speech_in; - int m_show_speech_out; - int m_show_demod_in; - int m_show_test_frame_errors; - int m_show_test_frame_errors_hist; - - // optional vox trigger tone - bool m_leftChannelVoxTone; - - // UDP control port - bool m_udp_enable; - int m_udp_port; - - // notebook display after tx->rxtransition - int m_rxNbookCtrl; - - wxRect m_rTopWindow; - - int m_framesPerBuffer; - - bool loadConfig(); - bool saveConfig(); - - // Plugins ----------------------------------- - - wxString m_txtPlugInParam[PLUGIN_MAX_PARAMS]; - - // plugin details - - void *m_plugInHandle; - bool m_plugIn; - wxString m_plugInName; - int m_numPlugInParam; - wxString m_plugInParamName[PLUGIN_MAX_PARAMS]; - void *m_plugInStates; - void (*m_plugin_startfp)(void *); - void (*m_plugin_stopfp)(void *); - void (*m_plugin_rx_samplesfp)(void *, short *, int); - - // misc - - bool m_testFrames; - bool m_channel_noise; - float m_channel_snr_dB; - - int FilterEvent(wxEvent& event); - MainFrame *frame; - - // 700 options - - bool m_FreeDV700txClip; - bool m_FreeDV700Combine; - - // Noise simulation - - int m_noise_snr; - - // carrier attenuation - - bool m_attn_carrier_en; - int m_attn_carrier; - - // tone interferer simulation - - bool m_tone; - int m_tone_freq_hz; - int m_tone_amplitude; - - // Windows debug console - - bool m_debug_console; - protected: -}; - -// declare global static function wxGetApp() -DECLARE_APP(MainApp) - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// paCallBackData -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -typedef struct -{ - // libresample states for 48 to 8 kHz conversions - - SRC_STATE *insrc1; - SRC_STATE *outsrc1; - SRC_STATE *insrc2; - SRC_STATE *outsrc2; - SRC_STATE *insrcsf; - - // FIFOs attached to first sound card - - struct FIFO *infifo1; - struct FIFO *outfifo1; - - // FIFOs attached to second sound card - struct FIFO *infifo2; - struct FIFO *outfifo2; - - // FIFOs for rx process - struct FIFO *rxinfifo; - struct FIFO *rxoutfifo; - - int inputChannels1, inputChannels2; - - // EQ filter states - void *sbqMicInBass; - void *sbqMicInTreble; - void *sbqMicInMid; - void *sbqSpkOutBass; - void *sbqSpkOutTreble; - void *sbqSpkOutMid; - - bool micInEQEnable; - bool spkOutEQEnable; - - // optional loud tone on left channel to reliably trigger vox - bool leftChannelVoxTone; - float voxTonePhase; - -} paCallBackData; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// panel with custom loop checkbox for play file dialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MyExtraPlayFilePanel : public wxPanel -{ -public: - MyExtraPlayFilePanel(wxWindow *parent); - void setLoopPlayFileToMicIn(bool checked) { m_cb->SetValue(checked); } - bool getLoopPlayFileToMicIn(void) { return m_cb->GetValue(); } -private: - wxCheckBox *m_cb; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// panel with custom Seconds-to-record control for record file dialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MyExtraRecFilePanel : public wxPanel -{ -public: - MyExtraRecFilePanel(wxWindow *parent); - ~MyExtraRecFilePanel() - { - wxLogDebug("Destructor\n"); - } - void setSecondsToRecord(wxString value) { m_secondsToRecord->SetValue(value); } - wxString getSecondsToRecord(void) - { - wxLogDebug("getSecondsToRecord: %s\n",m_secondsToRecord->GetValue()); - return m_secondsToRecord->GetValue(); - } -private: - wxTextCtrl *m_secondsToRecord; -}; - -class txRxThread; -class UDPThread; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainFrame -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MainFrame : public TopFrame -{ - public: - MainFrame(wxString plugInName, wxWindow *parent); - virtual ~MainFrame(); - - PlotSpectrum* m_panelSpectrum; - PlotWaterfall* m_panelWaterfall; - PlotScatter* m_panelScatter; - PlotScalar* m_panelTimeOffset; - PlotScalar* m_panelFreqOffset; - PlotScalar* m_panelSpeechIn; - PlotScalar* m_panelSpeechOut; - PlotScalar* m_panelDemodIn; - PlotScalar* m_panelTestFrameErrors; - PlotScalar* m_panelTestFrameErrorsHist; - - bool m_RxRunning; - - PortAudioWrap *m_rxInPa; - PortAudioWrap *m_rxOutPa; - PortAudioWrap *m_txInPa; - PortAudioWrap *m_txOutPa; - - PaError m_rxErr; - PaError m_txErr; - - txRxThread* m_txRxThread; - - bool OpenHamlibRig(); - void OpenSerialPort(void); - void CloseSerialPort(void); - void SerialPTTRx(void); - - bool m_modal; - -#ifdef _USE_TIMER - wxTimer m_plotTimer; -#endif - - void destroy_fifos(void); - void destroy_src(void); - void autoDetectSoundCards(PortAudioWrap *pa); - - static int rxCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); - - static int txCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); - - - void initPortAudioDevice(PortAudioWrap *pa, int inDevice, int outDevice, - int soundCard, int sampleRate, int inputChannels); - - void togglePTT(void); - - wxIPV4address m_udp_addr; - wxDatagramSocket *m_udp_sock; - UDPThread *m_UDPThread; - void startUDPThread(void); - void stopUDPThread(void); - int PollUDP(); - bool m_schedule_restore; - - int vk_state; - void VoiceKeyerProcessEvent(int vk_event); - - protected: - - void setsnrBeta(bool snrSlow); - - // protected event handlers - virtual void OnCloseFrame(wxCloseEvent& event); - void OnExitClick(wxCommandEvent& event); - - void startTxStream(); - void startRxStream(); - void stopTxStream(); - void stopRxStream(); - void abortTxStream(); - void abortRxStream(); - - void OnTop(wxCommandEvent& event); - void OnExit( wxCommandEvent& event ); - - void OnToolsAudio( wxCommandEvent& event ); - void OnToolsAudioUI( wxUpdateUIEvent& event ); - void OnToolsComCfg( wxCommandEvent& event ); - void OnToolsComCfgUI( wxUpdateUIEvent& event ); - void OnToolsFilter( wxCommandEvent& event ); - void OnToolsOptions(wxCommandEvent& event); - void OnToolsOptionsUI(wxUpdateUIEvent& event); - - void OnToolsPlugInCfg( wxCommandEvent& event ); - void OnToolsPlugInCfgUI( wxUpdateUIEvent& event ); - - void OnPlayFileToMicIn( wxCommandEvent& event ); - void StopPlayFileToMicIn(void); - void OnRecFileFromRadio( wxCommandEvent& event ); - void OnPlayFileFromRadio( wxCommandEvent& event ); - - void OnHelpCheckUpdates( wxCommandEvent& event ); - void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ); - void OnHelpAbout( wxCommandEvent& event ); - void OnCmdSliderScroll( wxScrollEvent& event ); -// void OnSliderScrollBottom( wxScrollEvent& event ); -// void OnCmdSliderScrollChanged( wxScrollEvent& event ); -// void OnSliderScrollTop( wxScrollEvent& event ); - void OnCheckSQClick( wxCommandEvent& event ); - void OnCheckSNRClick( wxCommandEvent& event ); - - // Toggle Buttons - void OnTogBtnSplitClick(wxCommandEvent& event); - void OnTogBtnAnalogClick(wxCommandEvent& event); - void OnTogBtnRxID( wxCommandEvent& event ); - void OnTogBtnTxID( wxCommandEvent& event ); - void OnTogBtnPTT( wxCommandEvent& event ); - void OnTogBtnVoiceKeyerClick (wxCommandEvent& event); - void OnTogBtnOnOff( wxCommandEvent& event ); - - void OnCallSignReset( wxCommandEvent& event ); - void OnBerReset( wxCommandEvent& event ); - - //System Events - void OnPaint(wxPaintEvent& event); - void OnSize( wxSizeEvent& event ); - void OnUpdateUI( wxUpdateUIEvent& event ); - void OnDeleteConfig(wxCommandEvent&); -#ifdef _USE_TIMER - void OnTimer(wxTimerEvent &evt); -#endif -#ifdef _USE_ONIDLE - void OnIdle(wxIdleEvent &evt); -#endif - - int VoiceKeyerStartTx(void); - - private: - bool m_useMemory; - wxTextCtrl* m_tc; - int m_zoom; - float m_snrBeta; - - // Callsign/text messaging - char m_callsign[MAX_CALLSIGN]; - char *m_pcallsign; - unsigned int m_checksumGood; - unsigned int m_checksumBad; - - // Events - void processTxtEvent(char event[]); - class OptionsDlg *optionsDlg; - wxTimer spamTimer[MAX_EVENT_RULES]; - - // level Gauge - float m_maxLevel; - - // flags to indicate when new EQ filters need to be designed - - bool m_newMicInFilter; - bool m_newSpkOutFilter; - - void* designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q = 0.0); - void designEQFilters(paCallBackData *cb); - void deleteEQFilters(paCallBackData *cb); - - // Voice Keyer States - - int vk_rx_pause; - int vk_repeats, vk_repeat_counter; - float vk_rx_time; -}; - -void txRxProcessing(); - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class txRxThread - experimental tx/rx processing thread -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class txRxThread : public wxThread -{ -public: - txRxThread(void) : wxThread(wxTHREAD_JOINABLE) { m_run = 1; } - - // thread execution starts here - void *Entry() - { - while (m_run) - { - txRxProcessing(); - wxThread::Sleep(20); - } - return NULL; - } - - // called when the thread exits - whether it terminates normally or is - // stopped with Delete() (but not when it is Kill()ed!) - void OnExit() { } - -public: - bool m_run; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class UDPThread - waits for UDP messages -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class UDPThread : public wxThread -{ -public: - UDPThread(void) : wxThread(wxTHREAD_JOINABLE) { m_run = 1; } - - // thread execution starts here - void *Entry(); - - // called when the thread exits - whether it terminates normally or is - // stopped with Delete() (but not when it is Kill()ed!) - void OnExit() { } - -public: - MainFrame *mf; - bool m_run; -}; - -void resample_for_plot(struct FIFO *plotFifo, short buf[], int length, int fs); - -int resample(SRC_STATE *src, - short output_short[], - short input_short[], - int output_sample_rate, - int input_sample_rate, - int length_output_short, // maximum output array length in samples - int length_input_short - ); -void txRxProcessing(); -void per_frame_rx_processing( - FIFO *output_fifo, // decoded speech samples - FIFO *input_fifo // modem samples input to demod - ); - -// FreeDv API calls this when there is a test frame that needs a-plottin' - -void my_freedv_put_error_pattern(void *state, short error_pattern[], int sz_error_pattern); - -// FreeDv API calls these puppies when it needs/receives a text char - -char my_get_next_tx_char(void *callback_state); -void my_put_next_rx_char(void *callback_state, char c); - -// helper complex freq shift function - -void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin); - -// Helper function called by plugin - -int plugin_get_persistant(char name[], char value[]); - -#endif //__FDMDV2_MAIN__ diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_pa_wrapper.cpp b/freedv/branches/1.2/freedv-dev/src/fdmdv2_pa_wrapper.cpp deleted file mode 100644 index 2f67ca26..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_pa_wrapper.cpp +++ /dev/null @@ -1,324 +0,0 @@ -//========================================================================== -// Name: fdmdv2_pa_wrapper.cpp -// Purpose: Implements a wrapper class around the PortAudio library. -// Created: August 12, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "fdmdv2_pa_wrapper.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// PortAudioWrap() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PortAudioWrap::PortAudioWrap() -{ - m_pStream = NULL; - m_pUserData = NULL; - m_samplerate = 0; - m_framesPerBuffer = 0; - m_statusFlags = 0; - m_pStreamCallback = NULL; - m_pStreamFinishedCallback = NULL; - m_pTimeInfo = 0; - m_newdata = false; - -// loadData(); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// ~PortAudioWrap() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PortAudioWrap::~PortAudioWrap() -{ -} - -//---------------------------------------------------------------- -// streamOpen() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamOpen() -{ - return Pa_OpenStream( - &m_pStream, - m_inputBuffer.device == paNoDevice ? NULL : &m_inputBuffer, - m_outputBuffer.device == paNoDevice ? NULL : &m_outputBuffer, - m_samplerate, - m_framesPerBuffer, - m_statusFlags, - *m_pStreamCallback, - m_pUserData - ); -} - -//---------------------------------------------------------------- -// streamStart() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamStart() -{ - return Pa_StartStream(m_pStream); -} - -//---------------------------------------------------------------- -// streamClose() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamClose() -{ - if(isOpen()) - { - PaError rv = Pa_CloseStream(m_pStream); - return rv; - } - else - { - return paNoError; - } -} - -//---------------------------------------------------------------- -// terminate() -//---------------------------------------------------------------- -void PortAudioWrap::terminate() -{ - if(Pa_IsStreamStopped(m_pStream) != paNoError) - { - Pa_StopStream(m_pStream); - } - Pa_Terminate(); -} - -//---------------------------------------------------------------- -// stop() -//---------------------------------------------------------------- -void PortAudioWrap::stop() -{ - Pa_StopStream(m_pStream); -} - -//---------------------------------------------------------------- -// abort() -//---------------------------------------------------------------- -void PortAudioWrap::abort() -{ - Pa_AbortStream(m_pStream); -} - -//---------------------------------------------------------------- -// isStopped() -//---------------------------------------------------------------- -bool PortAudioWrap::isStopped() const -{ - PaError ret = Pa_IsStreamStopped(m_pStream); - return ret; -} - -//---------------------------------------------------------------- -// isActive() -//---------------------------------------------------------------- -bool PortAudioWrap::isActive() const -{ - PaError ret = Pa_IsStreamActive(m_pStream); - return ret; -} - -//---------------------------------------------------------------- -// isOpen() -//---------------------------------------------------------------- -bool PortAudioWrap::isOpen() const -{ - return (m_pStream != NULL); -} - -//---------------------------------------------------------------- -// getDefaultInputDevice() -//---------------------------------------------------------------- -PaDeviceIndex PortAudioWrap::getDefaultInputDevice() -{ - return Pa_GetDefaultInputDevice(); -} - -//---------------------------------------------------------------- -// getDefaultOutputDevice() -//---------------------------------------------------------------- -PaDeviceIndex PortAudioWrap::getDefaultOutputDevice() -{ - return Pa_GetDefaultOutputDevice(); -} - -//---------------------------------------------------------------- -// setInputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputChannelCount(int count) -{ - m_inputBuffer.channelCount = count; - return paNoError; -} - -//---------------------------------------------------------------- -// getInputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::getInputChannelCount() -{ - return m_inputBuffer.channelCount; -} - -//---------------------------------------------------------------- -// setInputSampleFormat() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputSampleFormat(PaSampleFormat format) -{ - m_inputBuffer.sampleFormat = format; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputLatency() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputLatency(PaTime latency) -{ - m_inputBuffer.suggestedLatency = latency; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputHostApiStreamInfo() -//---------------------------------------------------------------- -void PortAudioWrap::setInputHostApiStreamInfo(void *info) -{ - m_inputBuffer.hostApiSpecificStreamInfo = info; -} - -//---------------------------------------------------------------- -// getInputDefaultLowLatency() -//---------------------------------------------------------------- -PaTime PortAudioWrap::getInputDefaultLowLatency() -{ - return Pa_GetDeviceInfo(m_inputBuffer.device)->defaultLowInputLatency; -} - -//---------------------------------------------------------------- -// setOutputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputChannelCount(int count) -{ - m_outputBuffer.channelCount = count; - return paNoError; -} - -//---------------------------------------------------------------- -// getOutputChannelCount() -//---------------------------------------------------------------- -const int PortAudioWrap::getOutputChannelCount() -{ - return m_outputBuffer.channelCount; -} - -//---------------------------------------------------------------- -// getDeviceName() -//---------------------------------------------------------------- -const char *PortAudioWrap::getDeviceName(PaDeviceIndex dev) -{ - const PaDeviceInfo *info; - info = Pa_GetDeviceInfo(dev); - return info->name; -} - -//---------------------------------------------------------------- -// setOutputSampleFormat() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputSampleFormat(PaSampleFormat format) -{ - m_outputBuffer.sampleFormat = format; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputLatency() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputLatency(PaTime latency) -{ - m_outputBuffer.suggestedLatency = latency; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputHostApiStreamInfo() -//---------------------------------------------------------------- -void PortAudioWrap::setOutputHostApiStreamInfo(void *info) -{ - m_outputBuffer.hostApiSpecificStreamInfo = info; -} - -//---------------------------------------------------------------- -// getOutputDefaultLowLatency() -//---------------------------------------------------------------- -PaTime PortAudioWrap::getOutputDefaultLowLatency() -{ - return Pa_GetDeviceInfo(m_outputBuffer.device)->defaultLowOutputLatency; -} - -//---------------------------------------------------------------- -// setFramesPerBuffer() -//---------------------------------------------------------------- -PaError PortAudioWrap::setFramesPerBuffer(unsigned long size) -{ - m_framesPerBuffer = size; - return paNoError; -} - -//---------------------------------------------------------------- -// setSampleRate() -//---------------------------------------------------------------- -PaError PortAudioWrap::setSampleRate(unsigned long rate) -{ - m_samplerate = rate; - return paNoError; -} - -//---------------------------------------------------------------- -// setStreamFlags() -//---------------------------------------------------------------- -PaError PortAudioWrap::setStreamFlags(PaStreamFlags flags) -{ - m_statusFlags = flags; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputDevice() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputDevice(PaDeviceIndex index) -{ - m_inputBuffer.device = index; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputDevice() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputDevice(PaDeviceIndex index) -{ - m_outputBuffer.device = index; - return paNoError; -} - -//---------------------------------------------------------------- -// setCallback() -//---------------------------------------------------------------- -PaError PortAudioWrap::setCallback(PaStreamCallback *callback) -{ - m_pStreamCallback = callback; - return paNoError; -} - diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_pa_wrapper.h b/freedv/branches/1.2/freedv-dev/src/fdmdv2_pa_wrapper.h deleted file mode 100644 index 3d216c0d..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_pa_wrapper.h +++ /dev/null @@ -1,115 +0,0 @@ -//========================================================================== -// Name: fdmdv2_pa_wrapper.h -// Purpose: Defines a wrapper class around PortAudio -// Created: August 12, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include -#include "fdmdv2_defines.h" -#include "codec2_fdmdv.h" -#include "codec2.h" -#include "portaudio.h" - -#define PA_SAMPLE_TYPE paInt16 //paFloat32 -#define FRAMES_PER_BUFFER (64) - -typedef float SAMPLE; - -class PortAudioWrap -{ - public: - PortAudioWrap(); - ~PortAudioWrap(); - -// float m_av_mag[FDMDV_NSPEC]; - - private: - PaStream *m_pStream; - void *m_pUserData; - PaStreamCallback *m_pStreamCallback; - PaStreamFinishedCallback *m_pStreamFinishedCallback; - const PaStreamCallbackTimeInfo *m_pTimeInfo; - struct FDMDV *m_pFDMDV_state; - PaStreamParameters m_inputBuffer; - PaStreamParameters m_outputBuffer; - int m_samplerate; - unsigned long m_framesPerBuffer; - PaStreamCallbackFlags m_statusFlags; - bool m_newdata; - - public: - - void averageData(float mag_dB[]); - - int getDeviceCount() { return Pa_GetDeviceCount(); } - PaDeviceIndex getDefaultInputDevice(); - PaDeviceIndex getDefaultOutputDevice(); - PaStreamParameters *getDeviceInfo(PaDeviceIndex idx); - - PaError setFramesPerBuffer(unsigned long size); - PaError setSampleRate(unsigned long size); - - PaError setStreamFlags(PaStreamFlags flags); - PaError setCallback(PaStreamCallback *m_pStreamCallback); - PaError setStreamCallback(PaStream *stream, PaStreamCallback* callback) { m_pStreamCallback = callback; return 0;} - PaError setStreamFinishedCallback(PaStream *stream, PaStreamFinishedCallback* m_pStreamFinishedCallback); - - void setInputBuffer(const PaStreamParameters& inputBuffer) {this->m_inputBuffer = inputBuffer;} - PaError setInputDevice(PaDeviceIndex dev); - PaError setInputChannelCount(int count); - int getInputChannelCount(); - PaError setInputSampleFormat(PaSampleFormat format); - PaError setInputSampleRate(PaSampleFormat format); - PaError setInputLatency(PaTime latency); - void setInputHostApiStreamInfo(void *info = NULL); - PaTime getInputDefaultLowLatency(); - const char *getDeviceName(PaDeviceIndex dev); - - PaError setOutputDevice(PaDeviceIndex dev); - PaError setOutputChannelCount(int count); - const int getOutputChannelCount(); - PaError setOutputSampleFormat(PaSampleFormat format); - PaError setOutputLatency(PaTime latency); - void setOutputHostApiStreamInfo(void *info = NULL); - PaTime getOutputDefaultLowLatency(); - - void setFdmdvState(FDMDV* fdmdv_state) {this->m_pFDMDV_state = fdmdv_state;} - void setOutputBuffer(const PaStreamParameters& outputBuffer) {this->m_outputBuffer = outputBuffer;} - void setTimeInfo(PaStreamCallbackTimeInfo* timeInfo) {this->m_pTimeInfo = timeInfo;} - void setUserData(void* userData) {this->m_pUserData = userData;} - unsigned long getFramesPerBuffer() const {return m_framesPerBuffer;} - const PaStreamParameters& getInputBuffer() const {return m_inputBuffer;} - const PaStreamParameters& getOutputBuffer() const {return m_outputBuffer;} - const PaStreamCallbackFlags& getStatusFlags() const {return m_statusFlags;} - - FDMDV* getFdmdvState() {return m_pFDMDV_state;} - int getSamplerate() const {return m_samplerate;} - PaStream* getStream() {return m_pStream;} - void *getUserData() {return m_pUserData;} - bool getDataAvail() {return m_newdata;} - PaError streamStart(); - PaError streamClose(); - PaError streamOpen(); - void terminate(); - void stop(); - void abort(); - bool isOpen() const; - bool isStopped() const; - bool isActive() const; -// void loadData(); -}; diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot.cpp b/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot.cpp deleted file mode 100644 index 1b85cd90..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot.cpp +++ /dev/null @@ -1,283 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot.cpp -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "fdmdv2_plot.h" - -BEGIN_EVENT_TABLE(PlotPanel, wxPanel) - EVT_PAINT (PlotPanel::OnPaint) - EVT_MOTION (PlotPanel::OnMouseMove) - EVT_LEFT_DOWN (PlotPanel::OnMouseLeftDown) - EVT_LEFT_UP (PlotPanel::OnMouseLeftUp) - EVT_RIGHT_DOWN (PlotPanel::OnMouseRightDown) - EVT_MOUSEWHEEL (PlotPanel::OnMouseWheelMoved) - EVT_SIZE (PlotPanel::OnSize) - EVT_SHOW (PlotPanel::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotPanel(wxFrame* parent) : wxPanel(parent) -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotPanel::PlotPanel(wxFrame* parent) : wxPanel(parent) -{ - m_pNoteBook = (wxAuiNotebook *) parent; - m_pTopFrame = (MainFrame *)m_pNoteBook->GetParent(); - m_zoomFactor = 1.0; - m_pBmp = NULL; - m_pPix = NULL; - m_firstPass = true; - m_line_color = 0; - m_newdata = false; - m_clip = false; - m_use_bitmap = true; - m_rubberBand = false; - m_mouseDown = false; - m_penShortDash = wxPen(wxColor(0xA0, 0xA0, 0xA0), 1, wxPENSTYLE_SHORT_DASH); - m_penDotDash = wxPen(wxColor(0xD0, 0xD0, 0xD0), 1, wxPENSTYLE_DOT_DASH); - m_penSolid = wxPen(wxColor(0x00, 0x00, 0x00), 1, wxPENSTYLE_SOLID); - SetBackgroundStyle(wxBG_STYLE_PAINT); - SetLabelSize(10.0); -} - -//------------------------------------------------------------------------- -// ~PlotPanel() -//------------------------------------------------------------------------- -PlotPanel::~PlotPanel() -{ - if(m_pBmp != NULL) - { - delete m_pBmp; - } -} - -//------------------------------------------------------------------------- -// GetLabelSize() -//------------------------------------------------------------------------- -double PlotPanel::GetLabelSize() -{ - return m_label_size; -} - -//------------------------------------------------------------------------- -// SetLabelSize() -//------------------------------------------------------------------------- -void PlotPanel::SetLabelSize(double size) -{ - m_label_size = size; -} - -//------------------------------------------------------------------------- -// OnShow() -//------------------------------------------------------------------------- -void PlotPanel::OnShow(wxShowEvent& event) -{ - this->Refresh(); -} - -//------------------------------------------------------------------------- -// OnErase() -//------------------------------------------------------------------------- -void PlotPanel::OnErase(wxEraseEvent& event) -{ - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnSize() -//------------------------------------------------------------------------- -void PlotPanel::OnSize(wxSizeEvent& event) -{ - m_rCtrlPrev = m_rCtrl; - m_rCtrl = GetClientRect(); - if(m_use_bitmap) - { - if(!m_oImage.IsOk()) - { - m_oImage.Create(m_rCtrl.GetWidth(), m_rCtrl.GetHeight(), true); - } - else - { - m_oImage.Rescale(m_rCtrl.GetWidth(), m_rCtrl.GetHeight()); - } - m_pBmp = new wxBitmap(m_oImage, wxBITMAP_SCREEN_DEPTH); - m_firstPass = true; - } - this->Refresh(); -} - -//------------------------------------------------------------------------- -// OnMouseMove() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseMove(wxMouseEvent& event) -{ -// if(m_mouseDown) -// { -// paintNow(); -// } -} - -//------------------------------------------------------------------------- -// OnMouseLeftDown() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseLeftDown(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseRightDown() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseRightDown(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseWheelMoved() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseWheelMoved(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseLeftUp() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseLeftUp(wxMouseEvent& event) -{ - m_mouseDown = false; -} - -//------------------------------------------------------------------------- -// SetZoomFactor() -//------------------------------------------------------------------------- -double PlotPanel::SetZoomFactor(double zf) -{ - if((zf > 0) && (zf < 5.0)) - { - m_zoomFactor = zf; - } - return zf; -} - -//------------------------------------------------------------------------- -// GetZoomFactor() -//------------------------------------------------------------------------- -double PlotPanel::GetZoomFactor(double zf) -{ - return m_zoomFactor; -} - -//------------------------------------------------------------------------- -// draw() -//------------------------------------------------------------------------- -void PlotPanel::draw(wxAutoBufferedPaintDC& pDC) -{ - printf("PlotPanel::draw()"); - wxMemoryDC m_mDC; - m_mDC.SelectObject(*m_pBmp); - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - m_rGrid.Offset(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER); - - pDC.Clear(); - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - if(m_firstPass) - { - m_firstPass = false; - m_mDC.FloodFill(0, 0, VERY_LTGREY_COLOR); - - // Draw a filled rectangle with aborder - wxBrush ltGraphBkgBrush = wxBrush(DARK_BLUE_COLOR); - m_mDC.SetBrush(ltGraphBkgBrush); - m_mDC.SetPen(wxPen(BLACK_COLOR, 0)); - m_mDC.DrawRectangle(m_rPlot); - } - if(m_newdata) - { - m_newdata = false; - int t = m_rPlot.GetTop(); - int l = m_rPlot.GetLeft(); -// int r = m_rPlot.GetRight(); - int h = m_rPlot.GetHeight(); - int w = m_rPlot.GetWidth(); - pDC.Blit(l, t, w, h, &m_mDC, l, t); - } - drawGraticule(pDC); - m_mDC.SetBrush(wxNullBrush); - m_mDC.SelectObject(wxNullBitmap); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotPanel::drawGraticule(wxAutoBufferedPaintDC& pDC) -{ - int p; - char buf[15]; - wxString s; - - // Vertical gridlines - pDC.SetPen(m_penShortDash); - for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < ((m_rGrid.GetWidth() - XLEFT_OFFSET) + GRID_INCREMENT); p += GRID_INCREMENT) - { - pDC.DrawLine(p, (m_rGrid.GetHeight() + PLOT_BORDER), p, PLOT_BORDER); - } - // Horizontal gridlines - pDC.SetPen(m_penDotDash); - for(p = (m_rGrid.GetHeight() - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT) - { - pDC.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (p + PLOT_BORDER), (m_rGrid.GetWidth() + PLOT_BORDER + XLEFT_OFFSET), (p + PLOT_BORDER)); - } - // Label the X-Axis - pDC.SetPen(wxPen(GREY_COLOR, 1)); - for(p = GRID_INCREMENT; p < (m_rGrid.GetWidth() - YBOTTOM_OFFSET); p += GRID_INCREMENT) - { - sprintf(buf, "%1.1f Hz",(double)(p / 10)); - pDC.DrawText(buf, p - PLOT_BORDER + XLEFT_OFFSET, m_rGrid.GetHeight() + YBOTTOM_OFFSET/2); - } - // Label the Y-Axis - //for(p = GRID_INCREMENT; p < (h - YBOTTOM_OFFSET); p += GRID_INCREMENT) - for(p = (m_rGrid.GetHeight() - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT) - { - sprintf(buf, "%1.0f", (double)((m_rGrid.GetHeight() - p) * -10)); - pDC.DrawText(buf, XLEFT_TEXT_OFFSET, p); - } -} - -//------------------------------------------------------------------------- -// paintEvent() -// -// Called by the system of by wxWidgets when the panel needs -// to be redrawn. You can also trigger this call by calling -// Refresh()/Update(). -//------------------------------------------------------------------------- -void PlotPanel::OnPaint(wxPaintEvent & evt) -{ - wxAutoBufferedPaintDC pdc(this); - draw(pdc); -} - diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot.h b/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot.h deleted file mode 100644 index 25309d3a..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot.h +++ /dev/null @@ -1,150 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot.h -// Purpose: Declares simple wxWidgets application with GUI -// Created: Apr. 10, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -//#include "fdmdv2_main.h" -#ifndef __FDMDV2_PLOT__ -#define __FDMDV2_PLOT__ -#include -#include -#include -#include -#include - -#define MAX_ZOOM 7 -#define MAX_BMP_X (400 * MAX_ZOOM) -#define MAX_BMP_Y (400 * MAX_ZOOM) -#define DATA_LINE_HEIGHT 10 -#define TEXT_BASELINE_OFFSET_Y -5 - - -#define wxUSE_FILEDLG 1 -#define wxUSE_LIBPNG 1 -#define wxUSE_LIBJPEG 1 -#define wxUSE_GIF 1 -#define wxUSE_PCX 1 -#define wxUSE_LIBTIFF 1 - -#define PLOT_BORDER 12 -#define XLEFT_OFFSET 40 -#define XLEFT_TEXT_OFFSET 6 -#define YBOTTOM_OFFSET 20 -#define YBOTTOM_TEXT_OFFSET 15 -#define GRID_INCREMENT 50 - -#define BLACK_COLOR wxColor(0x00, 0x00, 0x00) -#define GREY_COLOR wxColor(0x80, 0x80, 0x80) -#define DARK_GREY_COLOR wxColor(0x40, 0x40, 0x40) -#define MEDIUM_GREY_COLOR wxColor(0xC0, 0xC0, 0xC0) -#define LIGHT_GREY_COLOR wxColor(0xE0, 0xE0, 0xE0) -#define VERY_LTGREY_COLOR wxColor(0xF8, 0xF8, 0xF8) -#define WHITE_COLOR wxColor(0xFF, 0xFF, 0xFF) - -#define DARK_BLUE_COLOR wxColor(0x00, 0x00, 0x60) -#define BLUE_COLOR wxColor(0x00, 0x00, 0xFF) -#define LIGHT_BLUE_COLOR wxColor(0x80, 0x80, 0xFF) - -#define RED_COLOR wxColor(0xFF, 0x5E, 0x5E) -#define LIGHT_RED_COLOR wxColor(0xFF, 0xE0, 0xE0) -#define DARK_RED_COLOR wxColor(0xFF, 0x00, 0x00) -#define PINK_COLOR wxColor(0xFF, 0x80, 0xFF) - -#define LIGHT_GREEN_COLOR wxColor(0xE3, 0xFF, 0xE0) -#define GREEN_COLOR wxColor(0x95, 0xFF, 0x8A) -#define DARK_GREEN_COLOR wxColor(0x20, 0xFF, 0x08) -#define VERY_GREEN_COLOR wxColor(0x00, 0xFF, 0x00) - -#define YELLOW_COLOR wxColor(0xFF, 0xFF, 0x5E) -#define LIGHT_YELLOW_COLOR wxColor(0xFF, 0xFF, 0xB5) -#define DARK_YELLOW_COLOR wxColor(0xFF, 0xFF, 0x08) - -class MainFrame; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotPanel -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotPanel : public wxPanel -{ - public: - PlotPanel(wxFrame* parent); - ~PlotPanel(); - wxPen m_penShortDash; - wxPen m_penDotDash; - wxPen m_penSolid; - wxRect m_rCtrlPrev; - wxRect m_rCtrl; - wxRect m_rGrid; - wxRect m_rPlot; - MainFrame *m_pTopFrame; - wxAuiNotebook *m_pNoteBook; - double m_label_size; - wxSize m_Bufsz; - bool m_newdata; - wxImage m_oImage; - wxBitmap *m_pBmp; - wxNativePixelData *m_pPix; - - // some useful events - void OnMouseMove(wxMouseEvent& event); - virtual void OnMouseLeftDown(wxMouseEvent& event); - void OnMouseLeftUp(wxMouseEvent& event); - virtual void OnMouseRightDown(wxMouseEvent& event); - void OnMouseWheelMoved(wxMouseEvent& event); - void OnClose(wxCloseEvent& event ){ event.Skip(); } - void OnSize( wxSizeEvent& event ); - void OnErase(wxEraseEvent& event); - void OnPaint(wxPaintEvent& event); - //void OnUpdateUI( wxUpdateUIEvent& event ){ event.Skip(); } - - void paintEvent(wxPaintEvent & evt); - virtual void draw(wxAutoBufferedPaintDC& pdc); - virtual void drawGraticule(wxAutoBufferedPaintDC& pdc); - virtual double SetZoomFactor(double zf); - virtual double GetZoomFactor(double zf); - virtual void OnShow(wxShowEvent& event); - virtual double GetLabelSize(); - virtual void SetLabelSize(double size); - - protected: - int m_x; - int m_y; - int m_left; - int m_top; - int m_prev_w; - int m_prev_h; - int m_prev_x; - int m_prev_y; - bool m_use_bitmap; - bool m_clip; - bool m_rubberBand; - bool m_mouseDown; - bool m_firstPass; - double m_zoomFactor; - int m_greyscale; - int m_line_color; - DECLARE_EVENT_TABLE() -}; -#endif //__FDMDV2_PLOT__ diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scalar.cpp b/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scalar.cpp deleted file mode 100644 index 9a794f53..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scalar.cpp +++ /dev/null @@ -1,344 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_scalar.cpp -// Purpose: Plots scalar amplitude against time -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_main.h" -#include "fdmdv2_plot_scalar.h" - -BEGIN_EVENT_TABLE(PlotScalar, PlotPanel) - EVT_PAINT (PlotScalar::OnPaint) - EVT_MOTION (PlotScalar::OnMouseMove) - EVT_MOUSEWHEEL (PlotScalar::OnMouseWheelMoved) - EVT_SIZE (PlotScalar::OnSize) - EVT_SHOW (PlotScalar::OnShow) -// EVT_ERASE_BACKGROUND(PlotScalar::OnErase) -END_EVENT_TABLE() - -//---------------------------------------------------------------- -// PlotScalar() -//---------------------------------------------------------------- -PlotScalar::PlotScalar(wxFrame* parent, - int channels, // number on channels to plot - float t_secs, // time covered by entire x axis in seconds - float sample_period_secs, // time between each sample in seconds - float a_min, // min ampltude of samples being plotted - float a_max, // max ampltude of samples being plotted - float graticule_t_step, // time step of x (time) axis graticule in seconds - float graticule_a_step, // step of amplitude axis graticule - const char a_fmt[], // printf format string for amplitude axis labels - int mini // true for mini-plot - don't draw graticule - ): PlotPanel(parent) -{ - int i; - - m_rCtrl = GetClientRect(); - - m_channels = channels; - m_t_secs = t_secs; - m_sample_period_secs = sample_period_secs; - m_a_min = a_min; - m_a_max = a_max; - m_graticule_t_step = graticule_t_step; - m_graticule_a_step = graticule_a_step; - assert(strlen(a_fmt) < 15); - strcpy(m_a_fmt, a_fmt); - m_mini = mini; - m_bar_graph = 0; - m_logy = 0; - - // work out number of samples we will store and allocate storage - - m_samples = m_t_secs/m_sample_period_secs; - m_mem = new float[m_samples*m_channels]; - - for(i = 0; i < m_samples*m_channels; i++) - { - m_mem[i] = 0.0; - } -} - -//---------------------------------------------------------------- -// ~PlotScalar() -//---------------------------------------------------------------- -PlotScalar::~PlotScalar() -{ - delete m_mem; -} - -//---------------------------------------------------------------- -// add_new_sample() -//---------------------------------------------------------------- -void PlotScalar::add_new_sample(int channel, float sample) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-1; i++) - { - m_mem[offset+i] = m_mem[offset+i+1]; - } - m_mem[offset+m_samples-1] = sample; -} - -//---------------------------------------------------------------- -// add_new_samples() -//---------------------------------------------------------------- -void PlotScalar::add_new_samples(int channel, float samples[], int length) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-length; i++) - m_mem[offset+i] = m_mem[offset+i+length]; - for(; i < m_samples; i++) - m_mem[offset+i] = *samples++; -} - -//---------------------------------------------------------------- -// add_new_short_samples() -//---------------------------------------------------------------- -void PlotScalar::add_new_short_samples(int channel, short samples[], int length, float scale_factor) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-length; i++) - m_mem[offset+i] = m_mem[offset+i+length]; - for(; i < m_samples; i++) - m_mem[offset+i] = (float)*samples++/scale_factor; -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotScalar::draw(wxAutoBufferedPaintDC& dc) -{ - float index_to_px; - float a_to_py; - int i; - int prev_x, prev_y; - float a; - - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - if (!m_mini) - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - //printf("h %d w %d\n", m_rCtrl.GetWidth(), m_rCtrl.GetHeight()); - //printf("h %d w %d\n", m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - // black background - - dc.Clear(); - if (m_mini) - m_rPlot = wxRect(0, 0, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - else - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - index_to_px = (float)m_rGrid.GetWidth()/m_samples; - a_to_py = (float)m_rGrid.GetHeight()/(m_a_max - m_a_min); - - wxPen pen; - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - // draw all samples - - prev_x = prev_y = 0; // stop warning - - // plot each channel - - int offset, x, y; - for(offset=0; offset m_a_max) a = m_a_max; - - // invert y axis and offset by minimum - - y = m_rGrid.GetHeight() - a_to_py * a + m_a_min*a_to_py; - - // regular point-point line graph - - x = index_to_px * i; - - // put inside plot window - - if (!m_mini) { - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - } - - if (m_bar_graph) { - - if (m_logy) { - - // can't take log(0) - - assert(m_a_min > 0.0); - assert(m_a_max > 0.0); - - float norm = (log10(a) - log10(m_a_min))/(log10(m_a_max) - log10(m_a_min)); - y = m_rGrid.GetHeight()*(1.0 - norm); - } else { - y = m_rGrid.GetHeight() - a_to_py * a + m_a_min*a_to_py; - } - - // use points to make a bar graph - - int x1, x2, y1; - - x1 = index_to_px * ((float)i - 0.5); - x2 = index_to_px * ((float)i + 0.5); - y1 = m_rGrid.GetHeight(); - x1 += PLOT_BORDER + XLEFT_OFFSET; x2 += PLOT_BORDER + XLEFT_OFFSET; - y1 += PLOT_BORDER; - dc.DrawLine(x1, y1, x1, y); dc.DrawLine(x1, y, x2, y); dc.DrawLine(x2, y, x2, y1); - } - else { - if (i) - dc.DrawLine(x, y, prev_x, prev_y); - prev_x = x; prev_y = y; - } - } - } - - drawGraticule(dc); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotScalar::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - float t, a; - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float sec_to_px; - float a_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - sec_to_px = (float)m_rGrid.GetWidth()/m_t_secs; - a_to_py = (float)m_rGrid.GetHeight()/(m_a_max - m_a_min); - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Vertical gridlines - - dc.SetPen(m_penShortDash); - for(t=0; t<=m_t_secs; t+=m_graticule_t_step) { - x = t*sec_to_px; - if (m_mini) { - dc.DrawLine(x, m_rGrid.GetHeight(), x, 0); - } - else { - x += PLOT_BORDER + XLEFT_OFFSET; - dc.DrawLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, PLOT_BORDER); - } - if (!m_mini) { - sprintf(buf, "%2.1fs", t); - GetTextExtent(buf, &text_w, &text_h); - dc.DrawText(buf, x - text_w/2, m_rGrid.GetHeight() + PLOT_BORDER + YBOTTOM_TEXT_OFFSET); - } - } - - // Horizontal gridlines - - dc.SetPen(m_penDotDash); - for(a=m_a_min; a. -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SCALAR__ -#define __FDMDV2_PLOT_SCALAR__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotScalar -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotScalar: public PlotPanel -{ - public: - - PlotScalar(wxFrame* parent, - int channels, - float t_secs, - float sample_period_secs, - float a_min, - float a_max, - float graticule_t_step, - float graticule_a_step, - const char a_fmt[], - int mini - ); - ~PlotScalar(); - void add_new_sample(int channel, float sample); - void add_new_samples(int channel, float samples[], int length); - void add_new_short_samples(int channel, short samples[], int length, float scale_factor); - void setBarGraph(int bar_graph) { m_bar_graph = bar_graph; } - void setLogY(int logy) { m_logy = logy; } - - protected: - - int m_channels; - float m_t_secs; - float m_sample_period_secs; - float m_a_min; - float m_a_max; - float m_graticule_t_step; - float m_graticule_a_step; - char m_a_fmt[15]; - int m_mini; - int m_samples; - float *m_mem; - int m_bar_graph; // non zero to plot bar graphs - int m_logy; // plot graph on log scale - - void draw(wxAutoBufferedPaintDC& dc); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - - DECLARE_EVENT_TABLE() -}; - -#endif // __FDMDV2_PLOT_SCALAR__ - diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scatter.cpp b/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scatter.cpp deleted file mode 100644 index d7e88c70..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_scatter.cpp +++ /dev/null @@ -1,289 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_scatter.cpp -// Purpose: A scatter plot derivative of fdmdv2_plot. -// Created: June 24, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_plot_scatter.h" - -BEGIN_EVENT_TABLE(PlotScatter, PlotPanel) - EVT_PAINT (PlotScatter::OnPaint) - EVT_MOTION (PlotScatter::OnMouseMove) - EVT_MOUSEWHEEL (PlotScatter::OnMouseWheelMoved) - EVT_SIZE (PlotScatter::OnSize) - EVT_SHOW (PlotScatter::OnShow) -// EVT_ERASE_BACKGROUND(PlotScatter::OnErase) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// PlotScatter -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotScatter::PlotScatter(wxFrame* parent) : PlotPanel(parent) -{ - int i; - - for(i=0; i < SCATTER_MEM_SYMS_MAX; i++) - { - m_mem[i].real = 0.0; - m_mem[i].imag = 0.0; - } - - m_filter_max_xy = m_filter_max_y = 0.1; - - // defaults so we start off with something sensible - - Nsym = 14+1; - scatterMemSyms = ((int)(SCATTER_MEM_SECS*(Nsym/DT))); - assert(scatterMemSyms <= SCATTER_MEM_SYMS_MAX); - - Ncol = 0; - memset(eye_mem, 0, sizeof(eye_mem)); - - mode = PLOT_SCATTER_MODE_SCATTER; -} - -// changing number of carriers changes number of symbols to plot -void PlotScatter::setNc(int Nc) { - Nsym = Nc; - assert(Nsym <= (MODEM_STATS_NC_MAX+1)); - scatterMemSyms = ((int)(SCATTER_MEM_SECS*(Nsym/DT))); - assert(scatterMemSyms <= SCATTER_MEM_SYMS_MAX); -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotScatter::draw(wxAutoBufferedPaintDC& dc) -{ - float x_scale; - float y_scale; - int i,j; - int x; - int y; - wxColour sym_to_colour[] = {wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255), - wxColor(255,255,0), - wxColor(255,255,255), - wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255), - wxColor(255,255,0), - wxColor(255,255,255), - wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255) - }; - - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - // black background - - dc.Clear(); - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - wxPen pen; - pen.SetWidth(1); // note this is ignored by DrawPoint - - if (mode == PLOT_SCATTER_MODE_SCATTER) { - - // automatically scale, first measure the maximum value - - float max_xy = 1E-12; - float real,imag; - for(i=0; i< scatterMemSyms; i++) { - real = fabs(m_mem[i].real); - imag = fabs(m_mem[i].imag); - if (real > max_xy) - max_xy = real; - if (imag > max_xy) - max_xy = imag; - } - - // smooth it out and set a lower limit to prevent divide by 0 issues - - m_filter_max_xy = BETA*m_filter_max_xy + (1 - BETA)*2.5*max_xy; - if (m_filter_max_xy < 0.001) - m_filter_max_xy = 0.001; - - // quantise to log steps to prevent scatter scaling bobbing about too - // much as scaling varies - - float quant_m_filter_max_xy = exp(floor(0.5+log(m_filter_max_xy))); - //printf("max_xy: %f m_filter_max_xy: %f quant_m_filter_max_xy: %f\n", max_xy, m_filter_max_xy, quant_m_filter_max_xy); - - x_scale = (float)m_rGrid.GetWidth()/quant_m_filter_max_xy; - y_scale = (float)m_rGrid.GetHeight()/quant_m_filter_max_xy; - - // draw all samples - - for(i = 0; i < scatterMemSyms; i++) { - x = x_scale * m_mem[i].real + m_rGrid.GetWidth()/2; - y = y_scale * m_mem[i].imag + m_rGrid.GetHeight()/2; - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - pen.SetColour(sym_to_colour[i%Nsym]); - dc.SetPen(pen); - dc.DrawPoint(x, y); - } - } - - if (mode == PLOT_SCATTER_MODE_EYE) { - - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - // automatically scale, first measure the maximum Y value - - float max_y = 1E-12; - float min_y = 1E+12; - for(i=0; i max_y) { - max_y = eye_mem[i][j]; - } - if (eye_mem[i][j] < min_y) { - min_y = eye_mem[i][j]; - } - } - } - - // smooth it out and set a lower limit to prevent divide by 0 issues - - m_filter_max_y = BETA*m_filter_max_y + (1 - BETA)*2.5*max_y; - if (m_filter_max_y < 0.001) - m_filter_max_y = 0.001; - - // quantise to log steps to prevent scatter scaling bobbing about too - // much as scaling varies - - float quant_m_filter_max_y = exp(floor(0.5+log(m_filter_max_y))); - //printf("min_y: %4.3f max_y: %4.3f quant_m_filter_max_y: %4.3f\n", min_y, max_y, quant_m_filter_max_y); - - x_scale = (float)m_rGrid.GetWidth()/Ncol; - y_scale = (float)m_rGrid.GetHeight()/quant_m_filter_max_y; - //printf("GetWidth(): %d GetHeight(): %d\n", m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - // plot eye traces row by row - - int prev_x, prev_y; - prev_x = prev_y = 0; - for(i=0; i. -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SCATTER__ -#define __FDMDV2_PLOT_SCATTER__ - -#include "comp.h" -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -#define PLOT_SCATTER_MODE_SCATTER 0 -#define PLOT_SCATTER_MODE_EYE 1 -#define PLOT_SCATTER_EYE_MAX_SAMPLES_ROW 80 - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotScatter -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotScatter : public PlotPanel -{ - public: - PlotScatter(wxFrame* parent); - ~PlotScatter(){}; - void add_new_samples_scatter(COMP samples[]); - void add_new_samples_eye(float samples[], int n); - void setNc(int Nc); - void setEyeScatter(int eye_mode) {mode = eye_mode;} - - protected: - int mode; - COMP m_mem[SCATTER_MEM_SYMS_MAX]; - COMP m_new_samples[MODEM_STATS_NC_MAX+1]; - float eye_mem[SCATTER_EYE_MEM_ROWS][PLOT_SCATTER_EYE_MAX_SAMPLES_ROW]; - - void draw(wxAutoBufferedPaintDC& dc); - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - - DECLARE_EVENT_TABLE() - - private: - int Nsym; - int Ncol; - int scatterMemSyms; - float m_filter_max_xy, m_filter_max_y; -}; - -#endif //__FDMDV2_PLOT_SCATTER__ diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_spectrum.cpp b/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_spectrum.cpp deleted file mode 100644 index 1f5be59b..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_spectrum.cpp +++ /dev/null @@ -1,267 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.cpp -// Purpose: Implements a waterfall plot derivative of fdmdv2_plot. -// Created: June 23, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" - -#include "fdmdv2_main.h" - -extern float g_avmag[]; // average mag data passed to draw() -void fdmdv2_clickTune(float frequency); // callback to pass new click freq - -BEGIN_EVENT_TABLE(PlotSpectrum, PlotPanel) - EVT_MOTION (PlotSpectrum::OnMouseMove) - EVT_LEFT_DOWN (PlotSpectrum::OnMouseLeftDown) - EVT_LEFT_DCLICK (PlotSpectrum::OnMouseLeftDoubleClick) - EVT_LEFT_UP (PlotSpectrum::OnMouseLeftUp) - EVT_MOUSEWHEEL (PlotSpectrum::OnMouseWheelMoved) - EVT_PAINT (PlotSpectrum::OnPaint) - EVT_SHOW (PlotSpectrum::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotSpectrum -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotSpectrum::PlotSpectrum(wxFrame* parent, float *magdB, int n_magdB, - float min_mag_db, float max_mag_db, bool clickTune): PlotPanel(parent) -{ - m_greyscale = 0; - m_Bufsz = GetMaxClientSize(); - m_newdata = false; - m_firstPass = true; - m_line_color = 0; - SetLabelSize(10.0); - - m_magdB = magdB; - m_n_magdB = n_magdB; // number of points in magdB that covers 0 ... MAX_F_HZ of spectrum - m_max_mag_db = max_mag_db; - m_min_mag_db = min_mag_db; - m_rxFreq = 0.0; - m_clickTune = clickTune; -} - -//---------------------------------------------------------------- -// ~PlotSpectrum() -//---------------------------------------------------------------- -PlotSpectrum::~PlotSpectrum() -{ -} - -//---------------------------------------------------------------- -// OnSize() -//---------------------------------------------------------------- -void PlotSpectrum::OnSize(wxSizeEvent& event) { -} - -//---------------------------------------------------------------- -// OnPaint() -//---------------------------------------------------------------- -void PlotSpectrum::OnPaint(wxPaintEvent& event) -{ - wxAutoBufferedPaintDC dc(this); - draw(dc); -} - -//---------------------------------------------------------------- -// OnShow() -//---------------------------------------------------------------- -void PlotSpectrum::OnShow(wxShowEvent& event) -{ -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotSpectrum::draw(wxAutoBufferedPaintDC& dc) -{ - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. We need to work this out every time we draw - // as OnSize() may not be called before OnPaint(), for example when a new tab - // is selected - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - dc.Clear(); - - // black background - - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - // draw spectrum - - int x, y, prev_x, prev_y, index; - float index_to_px, mag_dB_to_py, mag; - - m_newdata = false; - - wxPen pen; - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - index_to_px = (float)m_rGrid.GetWidth()/m_n_magdB; - mag_dB_to_py = (float)m_rGrid.GetHeight()/(m_max_mag_db - m_min_mag_db); - - prev_x = PLOT_BORDER + XLEFT_OFFSET; - prev_y = PLOT_BORDER; - for(index = 0; index < m_n_magdB; index++) - { - x = index*index_to_px; - mag = m_magdB[index]; - if (mag > m_max_mag_db) mag = m_max_mag_db; - if (mag < m_min_mag_db) mag = m_min_mag_db; - y = -(mag - m_max_mag_db) * mag_dB_to_py; - - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - - if (index) - dc.DrawLine(x, y, prev_x, prev_y); - prev_x = x; prev_y = y; - } - - // and finally draw Graticule - - drawGraticule(dc); - -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotSpectrum::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float f, mag, freq_hz_to_px, mag_dB_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - mag_dB_to_py = (float)m_rGrid.GetHeight()/(m_max_mag_db - m_min_mag_db); - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Check if small screen size means text will overlap - - int textXStep = STEP_F_HZ*freq_hz_to_px; - int textYStep = STEP_MAG_DB*mag_dB_to_py; - sprintf(buf, "%4.0fHz", (float)MAX_F_HZ - STEP_F_HZ); - GetTextExtent(buf, &text_w, &text_h); - int overlappedText = (text_w > textXStep) || (text_h > textYStep); - //printf("text_w: %d textXStep: %d text_h: %d textYStep: %d overlappedText: %d\n", text_w, textXStep, - // text_h, textYStep, overlappedText); - - // Vertical gridlines - - for(f=STEP_F_HZ; f= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0) && m_clickTune) { - float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - float clickFreq = (float)pt.x/freq_hz_to_px; - - // see PlotWaterfall::OnMouseDown() - - fdmdv2_clickTune(clickFreq); - } -} diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_spectrum.h b/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_spectrum.h deleted file mode 100644 index 271eeb98..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_spectrum.h +++ /dev/null @@ -1,58 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_spectrum.h -// Purpose: Defines a spectrum plot derived from fdmdv2_plot class. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SPECTRUM__ -#define __FDMDV2_PLOT_SPECTRUM__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class Waterfall -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotSpectrum : public PlotPanel -{ - public: - PlotSpectrum(wxFrame* parent, float *magdB, int n_magdB, - float min_mag_db=MIN_MAG_DB, float max_mag_db=MAX_MAG_DB, bool clickTune=true); - ~PlotSpectrum(); - void setRxFreq(float rxFreq) { m_rxFreq = rxFreq; } - void setFreqScale(int n_magdB) { m_n_magdB = n_magdB; } - - protected: - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void draw(wxAutoBufferedPaintDC& dc); - void OnMouseLeftDoubleClick(wxMouseEvent& event); - - private: - float m_rxFreq; - float m_max_mag_db; - float m_min_mag_db; - float *m_magdB; - int m_n_magdB; - bool m_clickTune; - - DECLARE_EVENT_TABLE() -}; - -#endif //__FDMDV2_PLOT_SPECTRUM__ diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_waterfall.cpp b/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_waterfall.cpp deleted file mode 100644 index cdbe01e0..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_waterfall.cpp +++ /dev/null @@ -1,483 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.cpp -// Purpose: Implements a waterfall plot derivative of fdmdv2_plot. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_main.h" - -extern float g_avmag[]; // av mag spec passed in to draw() -void fdmdv2_clickTune(float frequency); // callback to pass new click freq - -BEGIN_EVENT_TABLE(PlotWaterfall, PlotPanel) - EVT_PAINT (PlotWaterfall::OnPaint) - EVT_MOTION (PlotWaterfall::OnMouseMove) - EVT_LEFT_DCLICK (PlotWaterfall::OnMouseLeftDoubleClick) - EVT_RIGHT_DOWN (PlotWaterfall::OnMouseRightDown) - EVT_LEFT_UP (PlotWaterfall::OnMouseLeftUp) - EVT_MOUSEWHEEL (PlotWaterfall::OnMouseWheelMoved) - EVT_SIZE (PlotWaterfall::OnSize) - EVT_SHOW (PlotWaterfall::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class WaterfallPlot -// -// @class WaterfallPlot -// @author David Witten -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotWaterfall::PlotWaterfall(wxFrame* parent, bool graticule, int colour): PlotPanel(parent) -{ - - for(int i = 0; i < 255; i++) - { - m_heatmap_lut[i] = heatmap((float)i, 0.0, 255.0); - } - m_graticule = graticule; - m_colour = colour; - m_Bufsz = GetMaxClientSize(); - m_newdata = false; - m_firstPass = true; - m_line_color = 0; - m_modem_stats_max_f_hz = MODEM_STATS_MAX_F_HZ; - - SetLabelSize(10.0); - - m_pBmp = NULL; - m_max_mag = MAX_MAG_DB; - m_min_mag = MIN_MAG_DB; -} - -// When the window size gets set we can work outthe size of the window -// we plot in and allocate a bit map of the correct size -void PlotWaterfall::OnSize(wxSizeEvent& event) -{ - // resize bit map - - delete m_pBmp; - - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - // we want a bit map the size of m_rGrid - - m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24); - - m_dT = DT; -} - -//---------------------------------------------------------------- -// paintEvent() -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -// Called by the system of by wxWidgets when the panel needs -// to be redrawn. You can also trigger this call by calling -// Refresh()/Update(). -//---------------------------------------------------------------- -void PlotWaterfall::OnPaint(wxPaintEvent & evt) -{ - wxAutoBufferedPaintDC dc(this); - draw(dc); -} - -//---------------------------------------------------------------- -// OnShow() -//---------------------------------------------------------------- -void PlotWaterfall::OnShow(wxShowEvent& event) -{ -} - -//---------------------------------------------------------------- -// ~PlotWaterfall() -//---------------------------------------------------------------- -PlotWaterfall::~PlotWaterfall() -{ -} - -//---------------------------------------------------------------- -// heatmap() -// map val to a rgb colour -// from http://eddiema.ca/2011/01/21/c-sharp-heatmaps/ -//---------------------------------------------------------------- -unsigned PlotWaterfall::heatmap(float val, float min, float max) -{ - unsigned r = 0; - unsigned g = 0; - unsigned b = 0; - - val = (val - min) / (max - min); - if(val <= 0.2) - { - b = (unsigned)((val / 0.2) * 255); - } - else if(val > 0.2 && val <= 0.7) - { - b = (unsigned)((1.0 - ((val - 0.2) / 0.5)) * 255); - } - if(val >= 0.2 && val <= 0.6) - { - g = (unsigned)(((val - 0.2) / 0.4) * 255); - } - else if(val > 0.6 && val <= 0.9) - { - g = (unsigned)((1.0 - ((val - 0.6) / 0.3)) * 255); - } - if(val >= 0.5) - { - r = (unsigned)(((val - 0.5) / 0.5) * 255); - } - //printf("%f %x %x %x\n", val, r, g, b); - return (b << 16) + (g << 8) + r; -} - -bool PlotWaterfall::checkDT(void) -{ - // Check dY is > 1 pixel before proceeding. For small screens - // and large WATERFALL_SECS_Y we might have less than one - // block per pixel. In this case increase m_dT and perform draw - // less often - - float px_per_sec = (float)m_rGrid.GetHeight() / WATERFALL_SECS_Y; - float dy = m_dT * px_per_sec; - - if (dy < 1.0) { - m_dT += DT; - return false; - } - else - return true; -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotWaterfall::draw(wxAutoBufferedPaintDC& dc) -{ - - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - if (m_pBmp == NULL) - { - // we want a bit map the size of m_rGrid - m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24); - } - - dc.Clear(); - - if(m_newdata) - { - m_newdata = false; - plotPixelData(); - dc.DrawBitmap(*m_pBmp, PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER); - m_dT = DT; - } - else - { - - // no data to plot so just erase to black. Blue looks nicer - // but is same colour as low amplitude signal - - // Bug on Linux: When Stop is pressed this code doesn't erase - // the lower 25% of the Waterfall Window - - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - } - drawGraticule(dc); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotWaterfall::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float f, time, freq_hz_to_px, time_s_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - time_s_to_py = (float)m_rGrid.GetHeight()/WATERFALL_SECS_Y; - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Check if small screen size means text will overlap - - int textXStep = STEP_F_HZ*freq_hz_to_px; - int textYStep = WATERFALL_SECS_STEP*time_s_to_py; - sprintf(buf, "%4.0fHz", (float)MAX_F_HZ - STEP_F_HZ); - GetTextExtent(buf, &text_w, &text_h); - int overlappedText = (text_w > textXStep) || (text_h > textYStep); - - // Major Vertical gridlines and legend - //dc.SetPen(m_penShortDash); - for(f=STEP_F_HZ; f max_mag) - { - max_mag = g_avmag[i]; - } - } - - m_max_mag = BETA*m_max_mag + (1 - BETA)*max_mag; - m_min_mag = max_mag - 20.0; - //printf("max_mag: %f m_max_mag: %f\n", max_mag, m_max_mag); - //intensity_per_dB = (float)256 /(MAX_MAG_DB - MIN_MAG_DB); - intensity_per_dB = (float)256 /(m_max_mag - m_min_mag); - spec_index_per_px = ((float)(MAX_F_HZ)/(float)m_modem_stats_max_f_hz)*(float)MODEM_STATS_NSPEC / (float) m_rGrid.GetWidth(); - - /* - printf("h %d w %d px_per_sec %d dy %d dy_blocks %d spec_index_per_px: %f\n", - m_rGrid.GetHeight(), m_rGrid.GetWidth(), px_per_sec, - dy, dy_blocks, spec_index_per_px); - */ - - // Shift previous bit map up one row of blocks ---------------------------- - wxNativePixelData data(*m_pBmp); - wxNativePixelData::Iterator bitMapStart(data); - wxNativePixelData::Iterator p = bitMapStart; - - for(b = 0; b < dy_blocks - 1; b++) - { - wxNativePixelData::Iterator psrc = bitMapStart; - wxNativePixelData::Iterator pdest = bitMapStart; - pdest.OffsetY(data, dy * b); - psrc.OffsetY(data, dy * (b+1)); - - // copy one line of blocks - - for(py = 0; py < dy; py++) - { - wxNativePixelData::Iterator pdestRowStart = pdest; - wxNativePixelData::Iterator psrcRowStart = psrc; - - for(px = 0; px < m_rGrid.GetWidth(); px++) - { - pdest.Red() = psrc.Red(); - pdest.Green() = psrc.Green(); - pdest.Blue() = psrc.Blue(); - pdest++; - psrc++; - } - pdest = pdestRowStart; - pdest.OffsetY(data, 1); - psrc = psrcRowStart; - psrc.OffsetY(data, 1); - } - } - - // Draw last line of blocks using latest amplitude data ------------------ - p = bitMapStart; - p.OffsetY(data, dy *(dy_blocks - 1)); - for(py = 0; py < dy; py++) - { - wxNativePixelData::Iterator rowStart = p; - - for(px = 0; px < m_rGrid.GetWidth(); px++) - { - index = px * spec_index_per_px; - assert(index < MODEM_STATS_NSPEC); - - intensity = intensity_per_dB * (g_avmag[index] - m_min_mag); - if(intensity > 255) intensity = 255; - if (intensity < 0) intensity = 0; - //printf("%d %f %d \n", index, g_avmag[index], intensity); - - switch (m_colour) { - case 0: - p.Red() = m_heatmap_lut[intensity] & 0xff; - p.Green() = (m_heatmap_lut[intensity] >> 8) & 0xff; - p.Blue() = (m_heatmap_lut[intensity] >> 16) & 0xff; - break; - case 1: - p.Red() = intensity; - p.Green() = intensity; - p.Blue() = intensity; - break; - case 2: - p.Red() = intensity; - p.Green() = intensity; - if (intensity < 127) - p.Blue() = intensity*2; - else - p.Blue() = 255; - - break; - } - ++p; - } - p = rowStart; - p.OffsetY(data, 1); - } - -} - -//------------------------------------------------------------------------- -// OnMouseLeftDown() -//------------------------------------------------------------------------- -void PlotWaterfall::OnMouseLeftDoubleClick(wxMouseEvent& event) -{ - m_mouseDown = true; - wxClientDC dc(this); - - wxPoint pt(event.GetLogicalPosition(dc)); - - // map x coord to edges of actual plot - pt.x -= PLOT_BORDER + XLEFT_OFFSET; - pt.y -= PLOT_BORDER; - - // valid click if inside of plot - if ((pt.x >= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0)) - { - float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - float clickFreq = (float)pt.x/freq_hz_to_px; - - // communicate back to other threads - fdmdv2_clickTune(clickFreq); - } -} - -//------------------------------------------------------------------------- -// OnMouseRightDown() -//------------------------------------------------------------------------- -void PlotWaterfall::OnMouseRightDown(wxMouseEvent& event) -{ - m_colour++; - if (m_colour == 3) - m_colour = 0; -} - diff --git a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_waterfall.h b/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_waterfall.h deleted file mode 100644 index f4896c6b..00000000 --- a/freedv/branches/1.2/freedv-dev/src/fdmdv2_plot_waterfall.h +++ /dev/null @@ -1,73 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.h -// Purpose: Defines a waterfall plot derivative of fdmdv2_plot. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_PLOT_WATERFALL__ -#define __FDMDV2_PLOT_WATERFALL__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotWaterfall -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotWaterfall : public PlotPanel -{ - public: - PlotWaterfall(wxFrame* parent, bool graticule, int colour); - ~PlotWaterfall(); - bool checkDT(void); - void setGreyscale(bool greyscale) { m_greyscale = greyscale; } - void setRxFreq(float rxFreq) { m_rxFreq = rxFreq; } - void setFs(int fs) { m_modem_stats_max_f_hz = fs/2; } - - protected: - unsigned m_heatmap_lut[256]; - - unsigned heatmap(float val, float min, float max); - - void OnPaint(wxPaintEvent & evt); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void draw(wxAutoBufferedPaintDC& dc); - void plotPixelData(); - void OnMouseLeftDoubleClick(wxMouseEvent& event); - void OnMouseRightDown(wxMouseEvent& event); - - private: - float m_dT; - float m_rxFreq; - bool m_graticule; - float m_min_mag; - float m_max_mag; - int m_colour; - int m_modem_stats_max_f_hz; - - DECLARE_EVENT_TABLE() -}; - -#endif //__FDMDV2_PLOT_WATERFALL__ diff --git a/freedv/branches/1.2/freedv-dev/src/freedv.icns b/freedv/branches/1.2/freedv-dev/src/freedv.icns deleted file mode 100644 index 5190e7951ffd9152576d6569f0c7ca64927649ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92136 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

-_`&2*Z5cR;kk!g) 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 bcU%5HHl?3v;lGWz00000NkvXXu0mjfpGTzJ diff --git a/freedv/branches/1.2/freedv-dev/src/hamlib.cpp b/freedv/branches/1.2/freedv-dev/src/hamlib.cpp deleted file mode 100644 index ff80b24a..00000000 --- a/freedv/branches/1.2/freedv-dev/src/hamlib.cpp +++ /dev/null @@ -1,160 +0,0 @@ -//========================================================================== -// Name: hamlib.cpp -// -// Purpose: Hamlib integration for FreeDV -// Created: May 2013 -// Authors: Joel Stanley -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include - -#include -#include - -using namespace std; - -typedef std::vector riglist_t; - -static bool rig_cmp(const struct rig_caps *rig1, const struct rig_caps *rig2); -static int build_list(const struct rig_caps *rig, rig_ptr_t); - -Hamlib::Hamlib() : m_rig(NULL) { - /* Stop hamlib from spewing info to stderr. */ - rig_set_debug(RIG_DEBUG_NONE); - - /* Create sorted list of rigs. */ - rig_load_all_backends(); - rig_list_foreach(build_list, &m_rigList); - sort(m_rigList.begin(), m_rigList.end(), rig_cmp); - - /* Reset debug output. */ - rig_set_debug(RIG_DEBUG_VERBOSE); - - m_rig = NULL; -} - -Hamlib::~Hamlib() { - if(m_rig) - close(); -} - -static int build_list(const struct rig_caps *rig, rig_ptr_t rigList) { - ((riglist_t *)rigList)->push_back(rig); - return 1; -} - -static bool rig_cmp(const struct rig_caps *rig1, const struct rig_caps *rig2) { - /* Compare manufacturer. */ - int r = strcasecmp(rig1->mfg_name, rig2->mfg_name); - if (r != 0) - return r < 0; - - /* Compare model. */ - r = strcasecmp(rig1->model_name, rig2->model_name); - if (r != 0) - return r < 0; - - /* Compare rig ID. */ - return rig1->rig_model < rig2->rig_model; -} - -void Hamlib::populateComboBox(wxComboBox *cb) { - - riglist_t::const_iterator rig = m_rigList.begin(); - for (; rig !=m_rigList.end(); rig++) { - char name[128]; - snprintf(name, 128, "%s %s", (*rig)->mfg_name, (*rig)->model_name); - cb->Append(name); - } -} - -bool Hamlib::connect(unsigned int rig_index, const char *serial_port, const int serial_rate) { - /* Look up model from index. */ - if (rig_index >= m_rigList.size()) { - return false; - } - fprintf(stderr, "rig: %s %s (%d)\n", m_rigList[rig_index]->mfg_name, - m_rigList[rig_index]->model_name, m_rigList[rig_index]->rig_model); - - if(m_rig) { - printf("Closing old hamlib instance!\n"); - close(); - } - - /* Initialise, configure and open. */ - - m_rig = rig_init(m_rigList[rig_index]->rig_model); - - if (!m_rig) - return false; - - /* TODO we may also need civaddr for Icom */ - - strncpy(m_rig->state.rigport.pathname, serial_port, FILPATHLEN - 1); - if (serial_rate) { - m_rig->state.rigport.parm.serial.rate = serial_rate; - } - fprintf(stderr, "hamlib: setting serial rate: %d\n", m_rig->state.rigport.parm.serial.rate); - - if (rig_open(m_rig) == RIG_OK) { - return true; - } - - return false; -} - -int Hamlib::get_serial_rate(void) { - return m_rig->state.rigport.parm.serial.rate; -} - -int Hamlib::get_data_bits(void) { - return m_rig->state.rigport.parm.serial.data_bits; -} - -int Hamlib::get_stop_bits(void) { - return m_rig->state.rigport.parm.serial.stop_bits; -} - -bool Hamlib::ptt(bool press, wxString &hamlibError) { - fprintf(stderr,"Hamlib::ptt: %d\n", press); - hamlibError = ""; - - if(!m_rig) - return false; - - /* TODO(Joel): make ON_DATA and ON configurable. */ - - ptt_t on = press ? RIG_PTT_ON : RIG_PTT_OFF; - - /* TODO(Joel): what should the VFO option be? */ - - int retcode = rig_set_ptt(m_rig, RIG_VFO_CURR, on); - fprintf(stderr,"Hamlib::ptt: rig_set_ptt returned: %d\n", retcode); - if (retcode != RIG_OK ) { - fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(retcode)); - hamlibError = rigerror(retcode); - } - - return retcode == RIG_OK; -} - -void Hamlib::close(void) { - if(m_rig) { - rig_close(m_rig); - rig_cleanup(m_rig); - m_rig = NULL; - } -} diff --git a/freedv/branches/1.2/freedv-dev/src/hamlib.h b/freedv/branches/1.2/freedv-dev/src/hamlib.h deleted file mode 100644 index 65af2d46..00000000 --- a/freedv/branches/1.2/freedv-dev/src/hamlib.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef HAMLIB_H -#define HAMLIB_H - -extern "C" { -#include -} -#include -#include - -class Hamlib { - - public: - Hamlib(); - ~Hamlib(); - void populateComboBox(wxComboBox *cb); - bool connect(unsigned int rig_index, const char *serial_port, const int serial_rate); - bool ptt(bool press, wxString &hamlibError); - void close(void); - int get_serial_rate(void); - int get_data_bits(void); - int get_stop_bits(void); - - typedef std::vector riglist_t; - - private: - RIG *m_rig; - /* Sorted list of rigs. */ - riglist_t m_rigList; -}; - -#endif /*HAMLIB_H*/ diff --git a/freedv/branches/1.2/freedv-dev/src/info.plist b/freedv/branches/1.2/freedv-dev/src/info.plist deleted file mode 100644 index 8f0d4c34..00000000 --- a/freedv/branches/1.2/freedv-dev/src/info.plist +++ /dev/null @@ -1,104 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - CFBundleIconFile - freedv - NSPrincipalClass - NSApplication - - - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - NSPrincipalClass - NSApplication - - - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - NSPrincipalClass - NSApplication - - \ No newline at end of file diff --git a/freedv/branches/1.2/freedv-dev/src/serialport.cpp b/freedv/branches/1.2/freedv-dev/src/serialport.cpp deleted file mode 100644 index 59dd0c93..00000000 --- a/freedv/branches/1.2/freedv-dev/src/serialport.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include -#include -#include -#include "serialport.h" - -Serialport::Serialport() { - com_handle = COM_HANDLE_INVALID; -} - -Serialport::~Serialport() { - if (isopen()) { - closeport(); - } -} - -// returns true if comm port opened OK, false if there was a problem - -bool Serialport::openport(const char name[], bool useRTS, bool RTSPos, bool useDTR, bool DTRPos) -{ - if (com_handle != COM_HANDLE_INVALID) { - closeport(); - } - - m_useRTS = useRTS; - m_RTSPos = RTSPos; - m_useDTR = useDTR; - m_DTRPos = DTRPos; - -#ifdef _WIN32 - { - COMMCONFIG CC; - DWORD CCsize=sizeof(CC); - COMMTIMEOUTS timeouts; - DCB dcb; - - if(GetDefaultCommConfigA(name, &CC, &CCsize)) { - CC.dcb.fOutxCtsFlow = FALSE; - CC.dcb.fOutxDsrFlow = FALSE; - CC.dcb.fDtrControl = DTR_CONTROL_DISABLE; - CC.dcb.fDsrSensitivity = FALSE; - CC.dcb.fRtsControl = RTS_CONTROL_DISABLE; - SetDefaultCommConfigA(name, &CC, CCsize); - } - - if((com_handle=CreateFileA(name - ,GENERIC_READ|GENERIC_WRITE /* Access */ - ,0 /* Share mode */ - ,NULL /* Security attributes */ - ,OPEN_EXISTING /* Create access */ - ,FILE_ATTRIBUTE_NORMAL /* File attributes */ - ,NULL /* Template */ - ))==INVALID_HANDLE_VALUE) - return false; - - if(GetCommTimeouts(com_handle, &timeouts)) { - timeouts.ReadIntervalTimeout=MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier=0; - timeouts.ReadTotalTimeoutConstant=0; // No-wait read timeout - timeouts.WriteTotalTimeoutMultiplier=0; - timeouts.WriteTotalTimeoutConstant=5000; // 5 seconds - SetCommTimeouts(com_handle,&timeouts); - } - - /* Force N-8-1 mode: */ - if(GetCommState(com_handle, &dcb)==TRUE) { - dcb.ByteSize = 8; - dcb.Parity = NOPARITY; - dcb.StopBits = ONESTOPBIT; - dcb.DCBlength = sizeof(DCB); - dcb.fBinary = TRUE; - dcb.fOutxCtsFlow = FALSE; - dcb.fOutxDsrFlow = FALSE; - dcb.fDtrControl = DTR_CONTROL_DISABLE; - dcb.fDsrSensitivity = FALSE; - dcb.fTXContinueOnXoff= TRUE; - dcb.fOutX = FALSE; - dcb.fInX = FALSE; - dcb.fRtsControl = RTS_CONTROL_DISABLE; - dcb.fAbortOnError = FALSE; - SetCommState(com_handle, &dcb); - } - } -#else - { - struct termios t; - - if((com_handle=open(name, O_NONBLOCK|O_RDWR))== COM_HANDLE_INVALID) - return false; - - if(tcgetattr(com_handle, &t)==-1) { - close(com_handle); - com_handle = COM_HANDLE_INVALID; - return false; - } - - t.c_iflag = ( - IGNBRK /* ignore BREAK condition */ - | IGNPAR /* ignore (discard) parity errors */ - ); - t.c_oflag = 0; /* No output processing */ - t.c_cflag = ( - CS8 /* 8 bits */ - | CREAD /* enable receiver */ - - /* - Fun snippet from the FreeBSD manpage: - - If CREAD is set, the receiver is enabled. Otherwise, no character is - received. Not all hardware supports this bit. In fact, this flag is - pretty silly and if it were not part of the termios specification it - would be omitted. - */ - | CLOCAL /* ignore modem status lines */ - ); - - t.c_lflag = 0; /* No local modes */ - if(tcsetattr(com_handle, TCSANOW, &t)==-1) { - close(com_handle); - com_handle = COM_HANDLE_INVALID; - return false; - } - - } -#endif - return true; -} - - -// fixme: this takes about one second to close under Linux - -void Serialport::closeport() -{ -#ifdef _WIN32 - CloseHandle(com_handle); -#else - close(com_handle); -#endif - com_handle = COM_HANDLE_INVALID; -} - -//---------------------------------------------------------------- -// (raise|lower)(RTS|DTR)() -// -// Raises/lowers the specified signal -//---------------------------------------------------------------- - -void Serialport::raiseDTR(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, SETDTR); -#else - { // For C89 happiness - int flags = TIOCM_DTR; - ioctl(com_handle, TIOCMBIS, &flags); - } -#endif -} - -void Serialport::raiseRTS(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, SETRTS); -#else - { // For C89 happiness - int flags = TIOCM_RTS; - ioctl(com_handle, TIOCMBIS, &flags); - } -#endif -} - -void Serialport::lowerDTR(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, CLRDTR); -#else - { // For C89 happiness - int flags = TIOCM_DTR; - ioctl(com_handle, TIOCMBIC, &flags); - } -#endif -} - -void Serialport::lowerRTS(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, CLRRTS); -#else - { // For C89 happiness - int flags = TIOCM_RTS; - ioctl(com_handle, TIOCMBIC, &flags); - } -#endif -} - -void Serialport::ptt(bool tx) { - - /* Truth table: - - g_tx RTSPos RTS - ------------------- - 0 1 0 - 1 1 1 - 0 0 1 - 1 0 0 - - exclusive NOR - */ - - if (com_handle != COM_HANDLE_INVALID) { - if (m_useRTS) { - //fprintf(stderr, "g_tx: %d m_boolRTSPos: %d serialLine: %d\n", g_tx, wxGetApp().m_boolRTSPos, g_tx == wxGetApp().m_boolRTSPos); - if (tx == m_RTSPos) - raiseRTS(); - else - lowerRTS(); - } - if (m_useDTR) { - //fprintf(stderr, "g_tx: %d m_boolDTRPos: %d serialLine: %d\n", g_tx, wxGetApp().m_boolDTRPos, g_tx == wxGetApp().m_boolDTRPos); - if (tx == m_DTRPos) - raiseDTR(); - else - lowerDTR(); - } - - } -} diff --git a/freedv/branches/1.2/freedv-dev/src/serialport.h b/freedv/branches/1.2/freedv-dev/src/serialport.h deleted file mode 100644 index e5db10b4..00000000 --- a/freedv/branches/1.2/freedv-dev/src/serialport.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef SERIALPORT_H -#define SERIALPORT_H - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -// Serial ports called com port for historic reasons, especially on Windows machines - -#ifdef _WIN32 -#define COM_HANDLE_INVALID INVALID_HANDLE_VALUE -typedef HANDLE com_handle_t; -#else -#define COM_HANDLE_INVALID -1 -typedef int com_handle_t; -#endif - -class Serialport { - - public: - Serialport(); - ~Serialport(); - bool openport(const char port[], bool useRTS, bool RTSPos, bool useDTR, bool DTRPos); - bool isopen() {return (com_handle != COM_HANDLE_INVALID);} - void closeport(); - void ptt(bool tx); - - private: - com_handle_t com_handle; - bool m_useRTS, m_RTSPos, m_useDTR, m_DTRPos; - - void raiseDTR(void); - void lowerDTR(void); - void raiseRTS(void); - void lowerRTS(void); -}; - -#endif /* SERIALPORT_H */ diff --git a/freedv/branches/1.2/freedv-dev/src/sox/band.h b/freedv/branches/1.2/freedv-dev/src/sox/band.h deleted file mode 100644 index 5398ff45..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/band.h +++ /dev/null @@ -1,47 +0,0 @@ -/* libSoX Bandpass effect file. July 5, 1991 - * Copyright 1991 Lance Norskog And Sundry Contributors - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Lance Norskog And Sundry Contributors are not responsible for - * the consequences of using this software. - * - * Algorithm: 2nd order recursive filter. - * Formula stolen from MUSIC56K, a toolkit of 56000 assembler stuff. - * Quote: - * This is a 2nd order recursive band pass filter of the form. - * y(n)= a * x(n) - b * y(n-1) - c * y(n-2) - * where : - * x(n) = "IN" - * "OUT" = y(n) - * c = EXP(-2*pi*cBW/S_RATE) - * b = -4*c/(1+c)*COS(2*pi*cCF/S_RATE) - * if cSCL=2 (i.e. noise input) - * a = SQT(((1+c)*(1+c)-b*b)*(1-c)/(1+c)) - * else - * a = SQT(1-b*b/(4*c))*(1-c) - * endif - * note : cCF is the center frequency in Hertz - * cBW is the band width in Hertz - * cSCL is a scale factor, use 1 for pitched sounds - * use 2 for noise. - * - * - * July 1, 1999 - Jan Paul Schmidt - * - * This looks like the resonator band pass in SPKit. It's a - * second order all-pole (IIR) band-pass filter described - * at the pages 186 - 189 in - * Dodge, Charles & Jerse, Thomas A. 1985: - * Computer Music -- Synthesis, Composition and Performance. - * New York: Schirmer Books. - * Reference from the SPKit manual. - */ - - p->a2 = exp(-2 * M_PI * bw_Hz / effp->in_signal.rate); - p->a1 = -4 * p->a2 / (1 + p->a2) * cos(2 * M_PI * p->fc / effp->in_signal.rate); - p->b0 = sqrt(1 - p->a1 * p->a1 / (4 * p->a2)) * (1 - p->a2); - if (p->filter_type == filter_BPF_SPK_N) { - mult = sqrt(((1+p->a2) * (1+p->a2) - p->a1*p->a1) * (1-p->a2) / (1+p->a2)) / p->b0; - p->b0 *= mult; - } diff --git a/freedv/branches/1.2/freedv-dev/src/sox/biquad.c b/freedv/branches/1.2/freedv-dev/src/sox/biquad.c deleted file mode 100644 index c57f1902..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/biquad.c +++ /dev/null @@ -1,178 +0,0 @@ -/* libSoX Biquad filter common functions (c) 2006-7 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "biquad.h" -#include - -typedef biquad_t priv_t; - -static char const * const width_str[] = { - "band-width(Hz)", - "band-width(kHz)", - "band-width(Hz, no warp)", /* deprecated */ - "band-width(octaves)", - "Q", - "slope", -}; -static char const all_width_types[] = "hkboqs"; - - -int lsx_biquad_getopts(sox_effect_t * effp, int argc, char **argv, - int min_args, int max_args, int fc_pos, int width_pos, int gain_pos, - char const * allowed_width_types, filter_t filter_type) -{ - priv_t * p = (priv_t *)effp->priv; - char width_type = *allowed_width_types; - char dummy, * dummy_p; /* To check for extraneous chars. */ - --argc, ++argv; - - p->filter_type = filter_type; - if (argc < min_args || argc > max_args || - (argc > fc_pos && ((p->fc = lsx_parse_frequency(argv[fc_pos], &dummy_p)) <= 0 || *dummy_p)) || - (argc > width_pos && ((unsigned)(sscanf(argv[width_pos], "%lf%c %c", &p->width, &width_type, &dummy)-1) > 1 || p->width <= 0)) || - (argc > gain_pos && sscanf(argv[gain_pos], "%lf %c", &p->gain, &dummy) != 1) || - !strchr(allowed_width_types, width_type) || (width_type == 's' && p->width > 1)) - return lsx_usage(effp); - p->width_type = strchr(all_width_types, width_type) - all_width_types; - if ((size_t)p->width_type >= strlen(all_width_types)) - p->width_type = 0; - if (p->width_type == width_bw_kHz) { - p->width *= 1000; - p->width_type = width_bw_Hz; - } - return SOX_SUCCESS; -} - - -static int start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - /* Simplify: */ - p->b2 /= p->a0; - p->b1 /= p->a0; - p->b0 /= p->a0; - p->a2 /= p->a0; - p->a1 /= p->a0; - - p->o2 = p->o1 = p->i2 = p->i1 = 0; - return SOX_SUCCESS; -} - - -int lsx_biquad_start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - - start(effp); - - if (effp->global_info->plot == sox_plot_octave) { - printf( - "%% GNU Octave file (may also work with MATLAB(R) )\n" - "Fs=%g;minF=10;maxF=Fs/2;\n" - "sweepF=logspace(log10(minF),log10(maxF),200);\n" - "[h,w]=freqz([%.15e %.15e %.15e],[1 %.15e %.15e],sweepF,Fs);\n" - "semilogx(w,20*log10(h))\n" - "title('SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)')\n" - "xlabel('Frequency (Hz)')\n" - "ylabel('Amplitude Response (dB)')\n" - "axis([minF maxF -35 25])\n" - "grid on\n" - "disp('Hit return to continue')\n" - "pause\n" - , effp->in_signal.rate, p->b0, p->b1, p->b2, p->a1, p->a2 - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate); - return SOX_EOF; - } - if (effp->global_info->plot == sox_plot_gnuplot) { - printf( - "# gnuplot file\n" - "set title 'SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)'\n" - "set xlabel 'Frequency (Hz)'\n" - "set ylabel 'Amplitude Response (dB)'\n" - "Fs=%g\n" - "b0=%.15e; b1=%.15e; b2=%.15e; a1=%.15e; a2=%.15e\n" - "o=2*pi/Fs\n" - "H(f)=sqrt((b0*b0+b1*b1+b2*b2+2.*(b0*b1+b1*b2)*cos(f*o)+2.*(b0*b2)*cos(2.*f*o))/(1.+a1*a1+a2*a2+2.*(a1+a1*a2)*cos(f*o)+2.*a2*cos(2.*f*o)))\n" - "set logscale x\n" - "set samples 250\n" - "set grid xtics ytics\n" - "set key off\n" - "plot [f=10:Fs/2] [-35:25] 20*log10(H(f))\n" - "pause -1 'Hit return to continue'\n" - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate, effp->in_signal.rate - , p->b0, p->b1, p->b2, p->a1, p->a2); - return SOX_EOF; - } - if (effp->global_info->plot == sox_plot_data) { - printf("# SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)\n" - "# IIR filter\n" - "# rate: %g\n" - "# name: b\n" - "# type: matrix\n" - "# rows: 3\n" - "# columns: 1\n" - "%24.16e\n%24.16e\n%24.16e\n" - "# name: a\n" - "# type: matrix\n" - "# rows: 3\n" - "# columns: 1\n" - "%24.16e\n%24.16e\n%24.16e\n" - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate, effp->in_signal.rate - , p->b0, p->b1, p->b2, 1. /* a0 */, p->a1, p->a2); - return SOX_EOF; - } - return SOX_SUCCESS; -} - - -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, - sox_sample_t *obuf, size_t *isamp, size_t *osamp) -{ - priv_t * p = (priv_t *)effp->priv; - size_t len = *isamp = *osamp = min(*isamp, *osamp); - while (len--) { - double o0 = *ibuf*p->b0 + p->i1*p->b1 + p->i2*p->b2 - p->o1*p->a1 - p->o2*p->a2; - p->i2 = p->i1, p->i1 = *ibuf++; - p->o2 = p->o1, p->o1 = o0; - *obuf++ = SOX_ROUND_CLIP_COUNT(o0, effp->clips); - } - return SOX_SUCCESS; -} - -static int create(sox_effect_t * effp, int argc, char * * argv) -{ - priv_t * p = (priv_t *)effp->priv; - double * d = &p->b0; - char c; - - --argc, ++argv; - if (argc == 6) - for (; argc && sscanf(*argv, "%lf%c", d, &c) == 1; --argc, ++argv, ++d); - return argc? lsx_usage(effp) : SOX_SUCCESS; -} - -sox_effect_handler_t const * lsx_biquad_effect_fn(void) -{ - static sox_effect_handler_t handler = { - "biquad", "b0 b1 b2 a0 a1 a2", 0, - create, lsx_biquad_start, lsx_biquad_flow, NULL, NULL, NULL, sizeof(priv_t) - }; - return &handler; -} diff --git a/freedv/branches/1.2/freedv-dev/src/sox/biquad.h b/freedv/branches/1.2/freedv-dev/src/sox/biquad.h deleted file mode 100644 index 8786ac83..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/biquad.h +++ /dev/null @@ -1,78 +0,0 @@ -/* libSoX Biquad filter common definitions (c) 2006-7 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef biquad_included -#define biquad_included - -#define LSX_EFF_ALIAS -#include "sox_i.h" - -typedef enum { - filter_LPF, - filter_HPF, - filter_BPF_CSG, - filter_BPF, - filter_notch, - filter_APF, - filter_peakingEQ, - filter_lowShelf, - filter_highShelf, - filter_LPF_1, - filter_HPF_1, - filter_BPF_SPK, - filter_BPF_SPK_N, - filter_AP1, - filter_AP2, - filter_deemph, - filter_riaa -} filter_t; - -typedef enum { - width_bw_Hz, - width_bw_kHz, - /* The old, non-RBJ, non-freq-warped band-pass/reject response; - * leaving here for now just in case anybody misses it: */ - width_bw_old, - width_bw_oct, - width_Q, - width_slope -} width_t; - -/* Private data for the biquad filter effects */ -typedef struct { - double gain; /* For EQ filters */ - double fc; /* Centre/corner/cutoff frequency */ - double width; /* Filter width; interpreted as per width_type */ - width_t width_type; - - filter_t filter_type; - - double b0, b1, b2; /* Filter coefficients */ - double a0, a1, a2; /* Filter coefficients */ - - sox_sample_t i1, i2; /* Filter memory */ - double o1, o2; /* Filter memory */ -} biquad_t; - -int lsx_biquad_getopts(sox_effect_t * effp, int n, char **argv, - int min_args, int max_args, int fc_pos, int width_pos, int gain_pos, - char const * allowed_width_types, filter_t filter_type); -int lsx_biquad_start(sox_effect_t * effp); -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf, - size_t *isamp, size_t *osamp); - -#endif diff --git a/freedv/branches/1.2/freedv-dev/src/sox/biquads.c b/freedv/branches/1.2/freedv-dev/src/sox/biquads.c deleted file mode 100644 index 19793a6d..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/biquads.c +++ /dev/null @@ -1,400 +0,0 @@ -/* libSoX Biquad filter effects (c) 2006-8 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * - * 2-pole filters designed by Robert Bristow-Johnson - * see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt - * - * 1-pole filters based on code (c) 2000 Chris Bagwell - * Algorithms: Recursive single pole low/high pass filter - * Reference: The Scientist and Engineer's Guide to Digital Signal Processing - * - * low-pass: output[N] = input[N] * A + output[N-1] * B - * X = exp(-2.0 * pi * Fc) - * A = 1 - X - * B = X - * Fc = cutoff freq / sample rate - * - * Mimics an RC low-pass filter: - * - * ---/\/\/\/\-----------> - * | - * --- C - * --- - * | - * | - * V - * - * high-pass: output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1] - * X = exp(-2.0 * pi * Fc) - * A0 = (1 + X) / 2 - * A1 = -(1 + X) / 2 - * B1 = X - * Fc = cutoff freq / sample rate - * - * Mimics an RC high-pass filter: - * - * || C - * ----||---------> - * || | - * < - * > R - * < - * | - * V - */ - - -#include "biquad.h" -#include -#include - -typedef biquad_t priv_t; - - -static int hilo1_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 1, 1, 0, 1, 2, "", - *effp->handler.name == 'l'? filter_LPF_1 : filter_HPF_1); -} - - -static int hilo2_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - if (argc > 1 && strcmp(argv[1], "-1") == 0) - return hilo1_getopts(effp, argc - 1, argv + 1); - if (argc > 1 && strcmp(argv[1], "-2") == 0) - ++argv, --argc; - p->width = sqrt(0.5); /* Default to Butterworth */ - return lsx_biquad_getopts(effp, argc, argv, 1, 2, 0, 1, 2, "qohk", - *effp->handler.name == 'l'? filter_LPF : filter_HPF); -} - - -static int bandpass_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_BPF; - if (argc > 1 && strcmp(argv[1], "-c") == 0) - ++argv, --argc, type = filter_BPF_CSG; - return lsx_biquad_getopts(effp, argc, argv, 2, 2, 0, 1, 2, "hkqob", type); -} - - -static int bandrej_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 2, 2, 0, 1, 2, "hkqob", filter_notch); -} - - -static int allpass_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_APF; - int m; - if (argc > 1 && strcmp(argv[1], "-1") == 0) - ++argv, --argc, type = filter_AP1; - else if (argc > 1 && strcmp(argv[1], "-2") == 0) - ++argv, --argc, type = filter_AP2; - m = 1 + (type == filter_APF); - return lsx_biquad_getopts(effp, argc, argv, m, m, 0, 1, 2, "hkqo", type); -} - - -static int tone_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->width = 0.5; - p->fc = *effp->handler.name == 'b'? 100 : 3000; - return lsx_biquad_getopts(effp, argc, argv, 1, 3, 1, 2, 0, "shkqo", - *effp->handler.name == 'b'? filter_lowShelf: filter_highShelf); -} - - -static int equalizer_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 3, 3, 0, 1, 2, "qohk", filter_peakingEQ); -} - - -static int band_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_BPF_SPK; - if (argc > 1 && strcmp(argv[1], "-n") == 0) - ++argv, --argc, type = filter_BPF_SPK_N; - return lsx_biquad_getopts(effp, argc, argv, 1, 2, 0, 1, 2, "hkqo", type); -} - - -static int deemph_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->fc = 5283; - p->width = 0.4845; - p->gain = -9.477; - return lsx_biquad_getopts(effp, argc, argv, 0, 0, 0, 1, 2, "s", filter_deemph); -} - - -static int riaa_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->filter_type = filter_riaa; - (void)argv; - return --argc? lsx_usage(effp) : SOX_SUCCESS; -} - - -static void make_poly_from_roots( - double const * roots, size_t num_roots, double * poly) -{ - size_t i, j; - poly[0] = 1; - poly[1] = -roots[0]; - memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly)); - for (i = 1; i < num_roots; ++i) - for (j = num_roots; j > 0; --j) - poly[j] -= poly[j - 1] * roots[i]; -} - -static int start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - double w0 = 2 * M_PI * p->fc / effp->in_signal.rate; - double A = exp(p->gain / 40 * log(10.)); - double alpha = 0, mult = dB_to_linear(max(p->gain, 0)); - - if (w0 > M_PI) { - lsx_fail("frequency must be less than half the sample-rate (Nyquist rate)"); - return SOX_EOF; - } - - /* Set defaults: */ - p->b0 = p->b1 = p->b2 = p->a1 = p->a2 = 0; - p->a0 = 1; - - if (p->width) switch (p->width_type) { - case width_slope: - alpha = sin(w0)/2 * sqrt((A + 1/A)*(1/p->width - 1) + 2); - break; - - case width_Q: - alpha = sin(w0)/(2*p->width); - break; - - case width_bw_oct: - alpha = sin(w0)*sinh(log(2.)/2 * p->width * w0/sin(w0)); - break; - - case width_bw_Hz: - alpha = sin(w0)/(2*p->fc/p->width); - break; - - case width_bw_kHz: assert(0); /* Shouldn't get here */ - - case width_bw_old: - alpha = tan(M_PI * p->width / effp->in_signal.rate); - break; - } - switch (p->filter_type) { - case filter_LPF: /* H(s) = 1 / (s^2 + s/Q + 1) */ - p->b0 = (1 - cos(w0))/2; - p->b1 = 1 - cos(w0); - p->b2 = (1 - cos(w0))/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_HPF: /* H(s) = s^2 / (s^2 + s/Q + 1) */ - p->b0 = (1 + cos(w0))/2; - p->b1 = -(1 + cos(w0)); - p->b2 = (1 + cos(w0))/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_BPF_CSG: /* H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q) */ - p->b0 = sin(w0)/2; - p->b1 = 0; - p->b2 = -sin(w0)/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_BPF: /* H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain) */ - p->b0 = alpha; - p->b1 = 0; - p->b2 = -alpha; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_notch: /* H(s) = (s^2 + 1) / (s^2 + s/Q + 1) */ - p->b0 = 1; - p->b1 = -2*cos(w0); - p->b2 = 1; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_APF: /* H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1) */ - p->b0 = 1 - alpha; - p->b1 = -2*cos(w0); - p->b2 = 1 + alpha; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_peakingEQ: /* H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1) */ - if (A == 1) - return SOX_EFF_NULL; - p->b0 = 1 + alpha*A; - p->b1 = -2*cos(w0); - p->b2 = 1 - alpha*A; - p->a0 = 1 + alpha/A; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha/A; - break; - - case filter_lowShelf: /* H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1) */ - if (A == 1) - return SOX_EFF_NULL; - p->b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha ); - p->b1 = 2*A*( (A-1) - (A+1)*cos(w0) ); - p->b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha ); - p->a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha; - p->a1 = -2*( (A-1) + (A+1)*cos(w0) ); - p->a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha; - break; - - case filter_deemph: /* See deemph.plt for documentation */ - if (effp->in_signal.rate != 44100) { - lsx_fail("Sample rate must be 44100 (audio-CD)"); - return SOX_EOF; - } - /* Falls through... */ - - case filter_highShelf: /* H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A) */ - if (!A) - return SOX_EFF_NULL; - p->b0 = A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha ); - p->b1 = -2*A*( (A-1) + (A+1)*cos(w0) ); - p->b2 = A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha ); - p->a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha; - p->a1 = 2*( (A-1) - (A+1)*cos(w0) ); - p->a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha; - break; - - case filter_LPF_1: /* single-pole */ - p->a1 = -exp(-w0); - p->b0 = 1 + p->a1; - break; - - case filter_HPF_1: /* single-pole */ - p->a1 = -exp(-w0); - p->b0 = (1 - p->a1)/2; - p->b1 = -p->b0; - break; - - case filter_BPF_SPK: case filter_BPF_SPK_N: { - double bw_Hz; - if (!p->width) - p->width = p->fc / 2; - bw_Hz = p->width_type == width_Q? p->fc / p->width : - p->width_type == width_bw_Hz? p->width : - p->fc * (pow(2., p->width) - 1) * pow(2., -0.5 * p->width); /* bw_oct */ - #include "band.h" /* Has different licence */ - break; - } - - case filter_AP1: /* Experimental 1-pole all-pass from Tom Erbe @ UCSD */ - p->b0 = exp(-w0); - p->b1 = -1; - p->a1 = -exp(-w0); - break; - - case filter_AP2: /* Experimental 2-pole all-pass from Tom Erbe @ UCSD */ - p->b0 = 1 - sin(w0); - p->b1 = -2 * cos(w0); - p->b2 = 1 + sin(w0); - p->a0 = 1 + sin(w0); - p->a1 = -2 * cos(w0); - p->a2 = 1 - sin(w0); - break; - - case filter_riaa: /* http://www.dsprelated.com/showmessage/73300/3.php */ - if (effp->in_signal.rate == 44100) { - static const double zeros[] = {-0.2014898, 0.9233820}; - static const double poles[] = {0.7083149, 0.9924091}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 48000) { - static const double zeros[] = {-0.1766069, 0.9321590}; - static const double poles[] = {0.7396325, 0.9931330}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 88200) { - static const double zeros[] = {-0.1168735, 0.9648312}; - static const double poles[] = {0.8590646, 0.9964002}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 96000) { - static const double zeros[] = {-0.1141486, 0.9676817}; - static const double poles[] = {0.8699137, 0.9966946}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else { - lsx_fail("Sample rate must be 44.1k, 48k, 88.2k, or 96k"); - return SOX_EOF; - } - { /* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */ - double y = 2 * M_PI * 1000 / effp->in_signal.rate; - double b_re = p->b0 + p->b1 * cos(-y) + p->b2 * cos(-2 * y); - double a_re = p->a0 + p->a1 * cos(-y) + p->a2 * cos(-2 * y); - double b_im = p->b1 * sin(-y) + p->b2 * sin(-2 * y); - double a_im = p->a1 * sin(-y) + p->a2 * sin(-2 * y); - double g = 1 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im))); - p->b0 *= g; p->b1 *= g; p->b2 *= g; - } - mult = (p->b0 + p->b1 + p->b2) / (p->a0 + p->a1 + p->a2); - lsx_debug("gain=%f", linear_to_dB(mult)); - break; - } - if (effp->in_signal.mult) - *effp->in_signal.mult /= mult; - return lsx_biquad_start(effp); -} - - -#define BIQUAD_EFFECT(name,group,usage,flags) \ -sox_effect_handler_t const * lsx_##name##_effect_fn(void) { \ - static sox_effect_handler_t handler = { \ - #name, usage, flags, \ - group##_getopts, start, lsx_biquad_flow, 0, 0, 0, sizeof(biquad_t)\ - }; \ - return &handler; \ -} - -BIQUAD_EFFECT(highpass, hilo2, "[-1|-2] frequency [width[q|o|h|k](0.707q)]", 0) -BIQUAD_EFFECT(lowpass, hilo2, "[-1|-2] frequency [width[q|o|h|k]](0.707q)", 0) -BIQUAD_EFFECT(bandpass, bandpass, "[-c] frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(bandreject,bandrej, "frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(allpass, allpass, "frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(bass, tone, "gain [frequency(100) [width[s|h|k|q|o]](0.5s)]", 0) -BIQUAD_EFFECT(treble, tone, "gain [frequency(3000) [width[s|h|k|q|o]](0.5s)]", 0) -BIQUAD_EFFECT(equalizer, equalizer,"frequency width[q|o|h|k] gain", 0) -BIQUAD_EFFECT(band, band, "[-n] center [width[h|k|q|o]]", 0) -BIQUAD_EFFECT(deemph, deemph, NULL, 0) -BIQUAD_EFFECT(riaa, riaa, NULL, 0) diff --git a/freedv/branches/1.2/freedv-dev/src/sox/effects.c b/freedv/branches/1.2/freedv-dev/src/sox/effects.c deleted file mode 100644 index 435412fa..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/effects.c +++ /dev/null @@ -1,544 +0,0 @@ -/* SoX Effects chain (c) 2007 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define LSX_EFF_ALIAS -#include "sox_i.h" -#include -#include -#ifdef HAVE_STRINGS_H - #include -#endif - -#define DEBUG_EFFECTS_CHAIN 0 - -/* Default effect handler functions for do-nothing situations: */ - -static int default_function(sox_effect_t * effp UNUSED) -{ - return SOX_SUCCESS; -} - -/* Pass through samples verbatim */ -int lsx_flow_copy(sox_effect_t * effp UNUSED, const sox_sample_t * ibuf, - sox_sample_t * obuf, size_t * isamp, size_t * osamp) -{ - *isamp = *osamp = min(*isamp, *osamp); - memcpy(obuf, ibuf, *isamp * sizeof(*obuf)); - return SOX_SUCCESS; -} - -/* Inform no more samples to drain */ -static int default_drain(sox_effect_t * effp UNUSED, sox_sample_t *obuf UNUSED, size_t *osamp) -{ - *osamp = 0; - return SOX_EOF; -} - -/* Check that no parameters have been given */ -static int default_getopts(sox_effect_t * effp, int argc, char **argv UNUSED) -{ - return --argc? lsx_usage(effp) : SOX_SUCCESS; -} - -/* Partially initialise the effect structure; signal info will come later */ -sox_effect_t * sox_create_effect(sox_effect_handler_t const * eh) -{ - sox_effect_t * effp = lsx_calloc(1, sizeof(*effp)); - effp->obuf = NULL; - - effp->global_info = sox_get_effects_globals(); - effp->handler = *eh; - if (!effp->handler.getopts) effp->handler.getopts = default_getopts; - if (!effp->handler.start ) effp->handler.start = default_function; - if (!effp->handler.flow ) effp->handler.flow = lsx_flow_copy; - if (!effp->handler.drain ) effp->handler.drain = default_drain; - if (!effp->handler.stop ) effp->handler.stop = default_function; - if (!effp->handler.kill ) effp->handler.kill = default_function; - - effp->priv = lsx_calloc(1, effp->handler.priv_size); - - return effp; -} /* sox_create_effect */ - -int sox_effect_options(sox_effect_t *effp, int argc, char * const argv[]) -{ - int result; - - char * * argv2 = lsx_malloc((argc + 1) * sizeof(*argv2)); - argv2[0] = (char *)effp->handler.name; - memcpy(argv2 + 1, argv, argc * sizeof(*argv2)); - result = effp->handler.getopts(effp, argc + 1, argv2); - free(argv2); - return result; -} /* sox_effect_options */ - -/* Effects chain: */ - -sox_effects_chain_t * sox_create_effects_chain( - sox_encodinginfo_t const * in_enc, sox_encodinginfo_t const * out_enc) -{ - sox_effects_chain_t * result = lsx_calloc(1, sizeof(sox_effects_chain_t)); - result->global_info = *sox_get_effects_globals(); - result->in_enc = in_enc; - result->out_enc = out_enc; - return result; -} /* sox_create_effects_chain */ - -void sox_delete_effects_chain(sox_effects_chain_t *ecp) -{ - if (ecp && ecp->length) - sox_delete_effects(ecp); - free(ecp->effects); - free(ecp); -} /* sox_delete_effects_chain */ - -/* Effect can call in start() or flow() to set minimum input size to flow() */ -int lsx_effect_set_imin(sox_effect_t * effp, size_t imin) -{ - if (imin > sox_globals.bufsiz / effp->flows) { - lsx_fail("sox_bufsiz not big enough"); - return SOX_EOF; - } - - effp->imin = imin; - return SOX_SUCCESS; -} - -/* Effects table to be extended in steps of EFF_TABLE_STEP */ -#define EFF_TABLE_STEP 8 - -/* Add an effect to the chain. *in is the input signal for this effect. *out is - * a suggestion as to what the output signal should be, but depending on its - * given options and *in, the effect can choose to do differently. Whatever - * output rate and channels the effect does produce are written back to *in, - * ready for the next effect in the chain. - */ -int sox_add_effect(sox_effects_chain_t * chain, sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t const * out) -{ - int ret, (*start)(sox_effect_t * effp) = effp->handler.start; - unsigned f; - sox_effect_t eff0; /* Copy of effect for flow 0 before calling start */ - - effp->global_info = &chain->global_info; - effp->in_signal = *in; - effp->out_signal = *out; - effp->in_encoding = chain->in_enc; - effp->out_encoding = chain->out_enc; - if (!(effp->handler.flags & SOX_EFF_CHAN)) - effp->out_signal.channels = in->channels; - if (!(effp->handler.flags & SOX_EFF_RATE)) - effp->out_signal.rate = in->rate; - if (!(effp->handler.flags & SOX_EFF_PREC)) - effp->out_signal.precision = (effp->handler.flags & SOX_EFF_MODIFY)? - in->precision : SOX_SAMPLE_PRECISION; - if (!(effp->handler.flags & SOX_EFF_GAIN)) - effp->out_signal.mult = in->mult; - - effp->flows = - (effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->in_signal.channels; - effp->clips = 0; - effp->imin = 0; - eff0 = *effp, eff0.priv = lsx_memdup(eff0.priv, eff0.handler.priv_size); - eff0.in_signal.mult = NULL; /* Only used in channel 0 */ - ret = start(effp); - if (ret == SOX_EFF_NULL) { - lsx_report("has no effect in this configuration"); - free(eff0.priv); - free(effp->priv); - effp->priv = NULL; - return SOX_SUCCESS; - } - if (ret != SOX_SUCCESS) { - free(eff0.priv); - return SOX_EOF; - } - if (in->mult) - lsx_debug("mult=%g", *in->mult); - - if (!(effp->handler.flags & SOX_EFF_LENGTH)) { - effp->out_signal.length = in->length; - if (effp->out_signal.length != SOX_UNKNOWN_LEN) { - if (effp->handler.flags & SOX_EFF_CHAN) - effp->out_signal.length = - effp->out_signal.length / in->channels * effp->out_signal.channels; - if (effp->handler.flags & SOX_EFF_RATE) - effp->out_signal.length = - effp->out_signal.length / in->rate * effp->out_signal.rate + .5; - } - } - - *in = effp->out_signal; - - if (chain->length == chain->table_size) { - chain->table_size += EFF_TABLE_STEP; - lsx_debug_more("sox_add_effect: extending effects table, " - "new size = %lu", (unsigned long)chain->table_size); - lsx_revalloc(chain->effects, chain->table_size); - } - - chain->effects[chain->length] = - lsx_calloc(effp->flows, sizeof(chain->effects[chain->length][0])); - chain->effects[chain->length][0] = *effp; - - for (f = 1; f < effp->flows; ++f) { - chain->effects[chain->length][f] = eff0; - chain->effects[chain->length][f].flow = f; - chain->effects[chain->length][f].priv = lsx_memdup(eff0.priv, eff0.handler.priv_size); - if (start(&chain->effects[chain->length][f]) != SOX_SUCCESS) { - free(eff0.priv); - return SOX_EOF; - } - } - - ++chain->length; - free(eff0.priv); - return SOX_SUCCESS; -} - -static int flow_effect(sox_effects_chain_t * chain, size_t n) -{ - sox_effect_t * effp1 = &chain->effects[n - 1][0]; - sox_effect_t * effp = &chain->effects[n][0]; - int effstatus = SOX_SUCCESS, f = 0; - size_t i; - const sox_sample_t *ibuf; - size_t idone = effp1->oend - effp1->obeg; - size_t obeg = sox_globals.bufsiz - effp->oend; -#if DEBUG_EFFECTS_CHAIN - size_t pre_idone = idone; - size_t pre_odone = obeg; -#endif - - if (effp->flows == 1) { /* Run effect on all channels at once */ - idone -= idone % effp->in_signal.channels; - effstatus = effp->handler.flow(effp, &effp1->obuf[effp1->obeg], - &effp->obuf[effp->oend], &idone, &obeg); - if (obeg % effp->out_signal.channels != 0) { - lsx_fail("multi-channel effect flowed asymmetrically!"); - effstatus = SOX_EOF; - } - } else { /* Run effect on each channel individually */ - sox_sample_t *obuf = &effp->obuf[effp->oend]; - size_t idone_last = 0, odone_last = 0; /* Initialised to prevent warning */ - - ibuf = &effp1->obuf[effp1->obeg]; - for (i = 0; i < idone; i += effp->flows) - for (f = 0; f < (int)effp->flows; ++f) - chain->ibufc[f][i / effp->flows] = *ibuf++; - -#ifdef HAVE_OPENMP - if (sox_globals.use_threads && effp->flows > 1) - { - #pragma omp parallel for - for (f = 0; f < (int)effp->flows; ++f) { - size_t idonec = idone / effp->flows; - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.flow(&chain->effects[n][f], - chain->ibufc[f], chain->obufc[f], &idonec, &odonec); - if (!f) { - idone_last = idonec; - odone_last = odonec; - } - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - } - else /* sox_globals.use_threads */ -#endif - { - for (f = 0; f < (int)effp->flows; ++f) { - size_t idonec = idone / effp->flows; - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.flow(&chain->effects[n][f], - chain->ibufc[f], chain->obufc[f], &idonec, &odonec); - if (f && (idonec != idone_last || odonec != odone_last)) { - lsx_fail("flowed asymmetrically!"); - effstatus = SOX_EOF; - } - idone_last = idonec; - odone_last = odonec; - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - } - - for (i = 0; i < odone_last; ++i) - for (f = 0; f < (int)effp->flows; ++f) - *obuf++ = chain->obufc[f][i]; - - idone = effp->flows * idone_last; - obeg = effp->flows * odone_last; - } -#if DEBUG_EFFECTS_CHAIN - lsx_report("flow: %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR, - pre_idone, pre_odone, idone, obeg); -#endif - effp1->obeg += idone; - if (effp1->obeg == effp1->oend) - effp1->obeg = effp1->oend = 0; - else if (effp1->oend - effp1->obeg < effp->imin ) { /* Need to refill? */ - memmove(effp1->obuf, &effp1->obuf[effp1->obeg], (effp1->oend - effp1->obeg) * sizeof(*effp1->obuf)); - effp1->oend -= effp1->obeg; - effp1->obeg = 0; - } - - effp->oend += obeg; - - return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF; -} - -/* The same as flow_effect but with no input */ -static int drain_effect(sox_effects_chain_t * chain, size_t n) -{ - sox_effect_t * effp = &chain->effects[n][0]; - int effstatus = SOX_SUCCESS; - size_t i, f; - size_t obeg = sox_globals.bufsiz - effp->oend; -#if DEBUG_EFFECTS_CHAIN - size_t pre_odone = obeg; -#endif - - if (effp->flows == 1) { /* Run effect on all channels at once */ - effstatus = effp->handler.drain(effp, &effp->obuf[effp->oend], &obeg); - if (obeg % effp->out_signal.channels != 0) { - lsx_fail("multi-channel effect drained asymmetrically!"); - effstatus = SOX_EOF; - } - } else { /* Run effect on each channel individually */ - sox_sample_t *obuf = &effp->obuf[effp->oend]; - size_t odone_last = 0; /* Initialised to prevent warning */ - - for (f = 0; f < effp->flows; ++f) { - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.drain(&chain->effects[n][f], chain->obufc[f], &odonec); - if (f && (odonec != odone_last)) { - lsx_fail("drained asymmetrically!"); - effstatus = SOX_EOF; - } - odone_last = odonec; - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - - for (i = 0; i < odone_last; ++i) - for (f = 0; f < effp->flows; ++f) - *obuf++ = chain->obufc[f][i]; - obeg = f * odone_last; - } -#if DEBUG_EFFECTS_CHAIN - lsx_report("drain: %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR, - (size_t)0, pre_odone, (size_t)0, obeg); -#endif - if (!obeg) /* This is the only thing that drain has and flow hasn't */ - effstatus = SOX_EOF; - - effp->oend += obeg; - - return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF; -} - -/* Flow data through the effects chain until an effect or callback gives EOF */ -int sox_flow_effects(sox_effects_chain_t * chain, int (* callback)(sox_bool all_done, void * client_data), void * client_data) -{ - int flow_status = SOX_SUCCESS; - size_t e, source_e = 0; /* effect indices */ - size_t f, max_flows = 0; - sox_bool draining = sox_true; - - for (e = 0; e < chain->length; ++e) { - chain->effects[e][0].obuf = lsx_realloc(chain->effects[e][0].obuf, - sox_globals.bufsiz * sizeof(chain->effects[e][0].obuf[0])); - /* Possibly there is already a buffer, if this is a used effect; - it may still contain samples in that case. */ - /* Memory will be freed by sox_delete_effect() later. */ - max_flows = max(max_flows, chain->effects[e][0].flows); - } - if (max_flows == 1) /* don't need interleave buffers */ - max_flows = 0; - chain->ibufc = lsx_calloc(max_flows, sizeof(*chain->ibufc)); - chain->obufc = lsx_calloc(max_flows, sizeof(*chain->obufc)); - for (f = 0; f < max_flows; ++f) { - chain->ibufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->ibufc[f][0])); - chain->obufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->obufc[f][0])); - } - - e = chain->length - 1; - while (source_e < chain->length) { -#define have_imin (e > 0 && e < chain->length && chain->effects[e - 1][0].oend - chain->effects[e - 1][0].obeg >= chain->effects[e][0].imin) - size_t osize = chain->effects[e][0].oend - chain->effects[e][0].obeg; - if (e == source_e && (draining || !have_imin)) { - if (drain_effect(chain, e) == SOX_EOF) { - ++source_e; - draining = sox_false; - } - } else if (have_imin && flow_effect(chain, e) == SOX_EOF) { - flow_status = SOX_EOF; - if (e == chain->length - 1) - break; - source_e = e; - draining = sox_true; - } - if (e < chain->length && chain->effects[e][0].oend - chain->effects[e][0].obeg > osize) /* False for output */ - ++e; - else if (e == source_e) - draining = sox_true; - else if ((int)--e < (int)source_e) - e = source_e; - - if (callback && callback(source_e == chain->length, client_data) != SOX_SUCCESS) { - flow_status = SOX_EOF; /* Client has requested to stop the flow. */ - break; - } - } - - for (f = 0; f < max_flows; ++f) { - free(chain->ibufc[f]); - free(chain->obufc[f]); - } - free(chain->obufc); - free(chain->ibufc); - - return flow_status; -} - -sox_uint64_t sox_effects_clips(sox_effects_chain_t * chain) -{ - unsigned i, f; - uint64_t clips = 0; - for (i = 1; i < chain->length - 1; ++i) - for (f = 0; f < chain->effects[i][0].flows; ++f) - clips += chain->effects[i][f].clips; - return clips; -} - -sox_uint64_t sox_stop_effect(sox_effect_t *effp) -{ - unsigned f; - uint64_t clips = 0; - - for (f = 0; f < effp->flows; ++f) { - effp[f].handler.stop(&effp[f]); - clips += effp[f].clips; - } - return clips; -} - -void sox_push_effect_last(sox_effects_chain_t *chain, sox_effect_t *effp) -{ - if (chain->length == chain->table_size) { - chain->table_size += EFF_TABLE_STEP; - lsx_debug_more("sox_push_effect_last: extending effects table, " - "new size = %lu", (unsigned long)chain->table_size); - lsx_revalloc(chain->effects, chain->table_size); - } - - chain->effects[chain->length++] = effp; -} /* sox_push_effect_last */ - -sox_effect_t *sox_pop_effect_last(sox_effects_chain_t *chain) -{ - if (chain->length > 0) - { - sox_effect_t *effp; - chain->length--; - effp = chain->effects[chain->length]; - chain->effects[chain->length] = NULL; - return effp; - } - else - return NULL; -} /* sox_pop_effect_last */ - -/* Free resources related to effect. - * Note: This currently closes down the effect which might - * not be obvious from name. - */ -void sox_delete_effect(sox_effect_t *effp) -{ - uint64_t clips; - unsigned f; - - if ((clips = sox_stop_effect(effp)) != 0) - lsx_warn("%s clipped %" PRIu64 " samples; decrease volume?", - effp->handler.name, clips); - if (effp->obeg != effp->oend) - lsx_debug("output buffer still held %" PRIuPTR " samples; dropped.", - (effp->oend - effp->obeg)/effp->out_signal.channels); - /* May or may not indicate a problem; it is normal if the user aborted - processing, or if an effect like "trim" stopped early. */ - effp->handler.kill(effp); /* N.B. only one kill; not one per flow */ - for (f = 0; f < effp->flows; ++f) - free(effp[f].priv); - free(effp->obuf); - free(effp); -} - -void sox_delete_effect_last(sox_effects_chain_t *chain) -{ - if (chain->length > 0) - { - chain->length--; - sox_delete_effect(chain->effects[chain->length]); - chain->effects[chain->length] = NULL; - } -} /* sox_delete_effect_last */ - -/* Remove all effects from the chain. - * Note: This currently closes down the effect which might - * not be obvious from name. - */ -void sox_delete_effects(sox_effects_chain_t * chain) -{ - size_t e; - - for (e = 0; e < chain->length; ++e) { - sox_delete_effect(chain->effects[e]); - chain->effects[e] = NULL; - } - chain->length = 0; -} - -/*----------------------------- Effects library ------------------------------*/ - -static sox_effect_fn_t s_sox_effect_fns[] = { -#define EFFECT(f) lsx_##f##_effect_fn, -#include "effects.h" -#undef EFFECT - NULL -}; - -const sox_effect_fn_t* -sox_get_effect_fns(void) -{ - return s_sox_effect_fns; -} - -/* Find a named effect in the effects library */ -sox_effect_handler_t const * sox_find_effect(char const * name) -{ - int e; - sox_effect_fn_t const * fns = sox_get_effect_fns(); - for (e = 0; fns[e]; ++e) { - const sox_effect_handler_t *eh = fns[e] (); - if (eh && eh->name && strcasecmp(eh->name, name) == 0) - return eh; /* Found it. */ - } - return NULL; -} diff --git a/freedv/branches/1.2/freedv-dev/src/sox/effects.h b/freedv/branches/1.2/freedv-dev/src/sox/effects.h deleted file mode 100644 index 8d7025c8..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/effects.h +++ /dev/null @@ -1,22 +0,0 @@ -/* This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* Manually edited for FreeDV to contain just the effects we need */ - - EFFECT(bass) - EFFECT(highpass) - EFFECT(treble) - EFFECT(equalizer) - diff --git a/freedv/branches/1.2/freedv-dev/src/sox/effects_i.c b/freedv/branches/1.2/freedv-dev/src/sox/effects_i.c deleted file mode 100644 index e5770a94..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/effects_i.c +++ /dev/null @@ -1,379 +0,0 @@ -/* Implements a libSoX internal interface for implementing effects. - * All public functions & data are prefixed with lsx_ . - * - * Copyright (c) 2005-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define LSX_EFF_ALIAS -#include "sox_i.h" -#include -#include - -int lsx_usage(sox_effect_t * effp) -{ - if (effp->handler.usage) - lsx_fail("usage: %s", effp->handler.usage); - else - lsx_fail("this effect takes no parameters"); - return SOX_EOF; -} - -char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n) -{ - if (!*usage) { - size_t i, len; - for (len = i = 0; i < n; len += strlen(lines[i++]) + 1); - *usage = lsx_malloc(len); /* FIXME: this memory will never be freed */ - strcpy(*usage, lines[0]); - for (i = 1; i < n; ++i) { - strcat(*usage, "\n"); - strcat(*usage, lines[i]); - } - } - return *usage; -} - -static lsx_enum_item const s_lsx_wave_enum[] = { - LSX_ENUM_ITEM(SOX_WAVE_,SINE) - LSX_ENUM_ITEM(SOX_WAVE_,TRIANGLE) - {0, 0}}; - -lsx_enum_item const * lsx_get_wave_enum(void) -{ - return s_lsx_wave_enum; -} - -void lsx_generate_wave_table( - lsx_wave_t wave_type, - sox_data_t data_type, - void *table, - size_t table_size, - double min, - double max, - double phase) -{ - uint32_t t; - uint32_t phase_offset = phase / M_PI / 2 * table_size + 0.5; - - for (t = 0; t < table_size; t++) - { - uint32_t point = (t + phase_offset) % table_size; - double d; - switch (wave_type) - { - case SOX_WAVE_SINE: - d = (sin((double)point / table_size * 2 * M_PI) + 1) / 2; - break; - - case SOX_WAVE_TRIANGLE: - d = (double)point * 2 / table_size; - switch (4 * point / table_size) - { - case 0: d = d + 0.5; break; - case 1: case 2: d = 1.5 - d; break; - case 3: d = d - 1.5; break; - } - break; - - default: /* Oops! FIXME */ - d = 0.0; /* Make sure we have a value */ - break; - } - d = d * (max - min) + min; - switch (data_type) - { - case SOX_FLOAT: - { - float *fp = (float *)table; - *fp++ = (float)d; - table = fp; - continue; - } - case SOX_DOUBLE: - { - double *dp = (double *)table; - *dp++ = d; - table = dp; - continue; - } - default: break; - } - d += d < 0? -0.5 : +0.5; - switch (data_type) - { - case SOX_SHORT: - { - short *sp = table; - *sp++ = (short)d; - table = sp; - continue; - } - case SOX_INT: - { - int *ip = table; - *ip++ = (int)d; - table = ip; - continue; - } - default: break; - } - } -} - -/* - * lsx_parsesamples - * - * Parse a string for # of samples. If string ends with a 's' - * then the string is interpreted as a user calculated # of samples. - * If string contains ':' or '.' or if it ends with a 't' then its - * treated as an amount of time. This is converted into seconds and - * fraction of seconds and then use the sample rate to calculate - * # of samples. - * Returns NULL on error, pointer to next char to parse otherwise. - */ -char const * lsx_parsesamples(sox_rate_t rate, const char *str0, uint64_t *samples, int def) -{ - int i, found_samples = 0, found_time = 0; - char const * end; - char const * pos; - sox_bool found_colon, found_dot; - char * str = (char *)str0; - - for (;*str == ' '; ++str); - for (end = str; *end && strchr("0123456789:.ets", *end); ++end); - if (end == str) - return NULL; - - pos = strchr(str, ':'); - found_colon = pos && pos < end; - - pos = strchr(str, '.'); - found_dot = pos && pos < end; - - if (found_colon || found_dot || *(end-1) == 't') - found_time = 1; - else if (*(end-1) == 's') - found_samples = 1; - - if (found_time || (def == 't' && !found_samples)) { - for (*samples = 0, i = 0; *str != '.' && i < 3; ++i) { - char * last_str = str; - long part = strtol(str, &str, 10); - if (!i && str == last_str) - return NULL; - *samples += rate * part; - if (i < 2) { - if (*str != ':') - break; - ++str; - *samples *= 60; - } - } - if (*str == '.') { - char * last_str = str; - double part = strtod(str, &str); - if (str == last_str) - return NULL; - *samples += rate * part + .5; - } - return *str == 't'? str + 1 : str; - } - { - char * last_str = str; - double part = strtod(str, &str); - if (str == last_str) - return NULL; - *samples = part + .5; - return *str == 's'? str + 1 : str; - } -} - -#if 0 - -#include - -#define TEST(st, samp, len) \ - str = st; \ - next = lsx_parsesamples(10000, str, &samples, 't'); \ - assert(samples == samp && next == str + len); - -int main(int argc, char * * argv) -{ - char const * str, * next; - uint64_t samples; - - TEST("0" , 0, 1) - TEST("1" , 10000, 1) - - TEST("0s" , 0, 2) - TEST("0s,", 0, 2) - TEST("0s/", 0, 2) - TEST("0s@", 0, 2) - - TEST("0t" , 0, 2) - TEST("0t,", 0, 2) - TEST("0t/", 0, 2) - TEST("0t@", 0, 2) - - TEST("1s" , 1, 2) - TEST("1s,", 1, 2) - TEST("1s/", 1, 2) - TEST("1s@", 1, 2) - TEST(" 01s" , 1, 4) - TEST("1e6s" , 1000000, 4) - - TEST("1t" , 10000, 2) - TEST("1t,", 10000, 2) - TEST("1t/", 10000, 2) - TEST("1t@", 10000, 2) - TEST("1.1t" , 11000, 4) - TEST("1.1t,", 11000, 4) - TEST("1.1t/", 11000, 4) - TEST("1.1t@", 11000, 4) - TEST("1e6t" , 10000, 1) - - TEST(".0", 0, 2) - TEST("0.0", 0, 3) - TEST("0:0.0", 0, 5) - TEST("0:0:0.0", 0, 7) - - TEST(".1", 1000, 2) - TEST(".10", 1000, 3) - TEST("0.1", 1000, 3) - TEST("1.1", 11000, 3) - TEST("1:1.1", 611000, 5) - TEST("1:1:1.1", 36611000, 7) - TEST("1:1", 610000, 3) - TEST("1:01", 610000, 4) - TEST("1:1:1", 36610000, 5) - TEST("1:", 600000, 2) - TEST("1::", 36000000, 3) - - TEST("0.444444", 4444, 8) - TEST("0.555555", 5556, 8) - - assert(!lsx_parsesamples(10000, "x", &samples, 't')); - return 0; -} -#endif - -/* a note is given as an int, - * 0 => 440 Hz = A - * >0 => number of half notes 'up', - * <0 => number of half notes down, - * example 12 => A of next octave, 880Hz - * - * calculated by freq = 440Hz * 2**(note/12) - */ -static double calc_note_freq(double note, int key) -{ - if (key != INT_MAX) { /* Just intonation. */ - static const int n[] = {16, 9, 6, 5, 4, 7}; /* Numerator. */ - static const int d[] = {15, 8, 5, 4, 3, 5}; /* Denominator. */ - static double j[13]; /* Just semitones */ - int i, m = floor(note); - - if (!j[1]) for (i = 1; i <= 12; ++i) - j[i] = i <= 6? log((double)n[i - 1] / d[i - 1]) / log(2.) : 1 - j[12 - i]; - note -= m; - m -= key = m - ((INT_MAX / 2 - ((INT_MAX / 2) % 12) + m - key) % 12); - return 440 * pow(2., key / 12. + j[m] + (j[m + 1] - j[m]) * note); - } - return 440 * pow(2., note / 12); -} - -int lsx_parse_note(char const * text, char * * end_ptr) -{ - int result = INT_MAX; - - if (*text >= 'A' && *text <= 'G') { - result = (int)(5/3. * (*text++ - 'A') + 9.5) % 12 - 9; - if (*text == 'b') {--result; ++text;} - else if (*text == '#') {++result; ++text;} - if (isdigit((unsigned char)*text)) - result += 12 * (*text++ - '4'); - } - *end_ptr = (char *)text; - return result; -} - -/* Read string 'text' and convert to frequency. - * 'text' can be a positive number which is the frequency in Hz. - * If 'text' starts with a '%' and a following number the corresponding - * note is calculated. - * Return -1 on error. - */ -double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key) -{ - double result; - - if (*text == '%') { - result = strtod(text + 1, end_ptr); - if (*end_ptr == text + 1) - return -1; - return calc_note_freq(result, key); - } - if (*text >= 'A' && *text <= 'G') { - int result2 = lsx_parse_note(text, end_ptr); - return result2 == INT_MAX? - 1 : calc_note_freq((double)result2, key); - } - result = strtod(text, end_ptr); - if (end_ptr) { - if (*end_ptr == text) - return -1; - if (**end_ptr == 'k') { - result *= 1000; - ++*end_ptr; - } - } - return result < 0 ? -1 : result; -} - -FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename) -{ - FILE * file; - - if (!filename || !strcmp(filename, "-")) { - if (effp->global_info->global_info->stdin_in_use_by) { - lsx_fail("stdin already in use by `%s'", effp->global_info->global_info->stdin_in_use_by); - return NULL; - } - effp->global_info->global_info->stdin_in_use_by = effp->handler.name; - file = stdin; - } - else if (!(file = fopen(filename, "r"))) { - lsx_fail("couldn't open file %s: %s", filename, strerror(errno)); - return NULL; - } - return file; -} - -int lsx_effects_init(void) -{ - #ifndef __FREEDV__ - init_fft_cache(); - #endif - return SOX_SUCCESS; -} - -int lsx_effects_quit(void) -{ - #ifndef __FREEDV__ - clear_fft_cache(); - #endif - return SOX_SUCCESS; -} diff --git a/freedv/branches/1.2/freedv-dev/src/sox/formats_i.c b/freedv/branches/1.2/freedv-dev/src/sox/formats_i.c deleted file mode 100644 index 17c40615..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/formats_i.c +++ /dev/null @@ -1,487 +0,0 @@ -/* Implements a libSoX internal interface for use in implementing file formats. - * All public functions & data are prefixed with lsx_ . - * - * (c) 2005-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include -#include -#include - -void lsx_fail_errno(sox_format_t * ft, int sox_errno, const char *fmt, ...) -{ - va_list args; - - ft->sox_errno = sox_errno; - - va_start(args, fmt); -#ifdef HAVE_VSNPRINTF - vsnprintf(ft->sox_errstr, sizeof(ft->sox_errstr), fmt, args); -#else - vsprintf(ft->sox_errstr, fmt, args); -#endif - va_end(args); - ft->sox_errstr[255] = '\0'; -} - -void lsx_set_signal_defaults(sox_format_t * ft) -{ - if (!ft->signal.rate ) ft->signal.rate = SOX_DEFAULT_RATE; - if (!ft->signal.precision) ft->signal.precision = SOX_DEFAULT_PRECISION; - if (!ft->signal.channels ) ft->signal.channels = SOX_DEFAULT_CHANNELS; - - if (!ft->encoding.bits_per_sample) - ft->encoding.bits_per_sample = ft->signal.precision; - if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN) - ft->encoding.encoding = SOX_ENCODING_SIGN2; -} - -#ifndef __FREEDV__ -int lsx_check_read_params(sox_format_t * ft, unsigned channels, - sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, - uint64_t num_samples, sox_bool check_length) -{ - ft->signal.length = ft->signal.length == SOX_IGNORE_LENGTH? SOX_UNSPEC : num_samples; - - if (ft->seekable) - ft->data_start = lsx_tell(ft); - - if (channels && ft->signal.channels && ft->signal.channels != channels) - lsx_warn("`%s': overriding number of channels", ft->filename); - else ft->signal.channels = channels; - - if (rate && ft->signal.rate && ft->signal.rate != rate) - lsx_warn("`%s': overriding sample rate", ft->filename); - else ft->signal.rate = rate; - - if (encoding && ft->encoding.encoding && ft->encoding.encoding != encoding) - lsx_warn("`%s': overriding encoding type", ft->filename); - else ft->encoding.encoding = encoding; - - if (bits_per_sample && ft->encoding.bits_per_sample && ft->encoding.bits_per_sample != bits_per_sample) - lsx_warn("`%s': overriding encoding size", ft->filename); - ft->encoding.bits_per_sample = bits_per_sample; - - if (check_length && ft->encoding.bits_per_sample && lsx_filelength(ft)) { - uint64_t calculated_length = div_bits(lsx_filelength(ft) - ft->data_start, ft->encoding.bits_per_sample); - if (!ft->signal.length) - ft->signal.length = calculated_length; - else if (num_samples != calculated_length) - lsx_warn("`%s': file header gives the total number of samples as %" PRIu64 " but file length indicates the number is in fact %" PRIu64, ft->filename, num_samples, calculated_length); - } - - if (sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample)) - return SOX_SUCCESS; - lsx_fail_errno(ft, EINVAL, "invalid format for this file type"); - return SOX_EOF; -} -#endif - -/* Read in a buffer of data of length len bytes. - * Returns number of bytes read. - */ -size_t lsx_readbuf(sox_format_t * ft, void *buf, size_t len) -{ - size_t ret = fread(buf, (size_t) 1, len, (FILE*)ft->fp); - if (ret != len && ferror((FILE*)ft->fp)) - lsx_fail_errno(ft, errno, "lsx_readbuf"); - ft->tell_off += ret; - return ret; -} - -/* Skip input without seeking. */ -int lsx_skipbytes(sox_format_t * ft, size_t n) -{ - unsigned char trash; - - while (n--) - if (lsx_readb(ft, &trash) == SOX_EOF) - return (SOX_EOF); - - return (SOX_SUCCESS); -} - -/* Pad output. */ -int lsx_padbytes(sox_format_t * ft, size_t n) -{ - while (n--) - if (lsx_writeb(ft, '\0') == SOX_EOF) - return (SOX_EOF); - - return (SOX_SUCCESS); -} - -/* Write a buffer of data of length bytes. - * Returns number of bytes written. - */ -size_t lsx_writebuf(sox_format_t * ft, void const * buf, size_t len) -{ - size_t ret = fwrite(buf, (size_t) 1, len, (FILE*)ft->fp); - if (ret != len) { - lsx_fail_errno(ft, errno, "error writing output file"); - clearerr((FILE*)ft->fp); /* Allows us to seek back to write header */ - } - ft->tell_off += ret; - return ret; -} - -uint64_t lsx_filelength(sox_format_t * ft) -{ - struct stat st; - int ret = fstat(fileno((FILE*)ft->fp), &st); - - return (!ret && (st.st_mode & S_IFREG))? (uint64_t)st.st_size : 0; -} - -int lsx_flush(sox_format_t * ft) -{ - return fflush((FILE*)ft->fp); -} - -off_t lsx_tell(sox_format_t * ft) -{ - return ft->seekable? (off_t)ftello((FILE*)ft->fp) : (off_t)ft->tell_off; -} - -int lsx_eof(sox_format_t * ft) -{ - return feof((FILE*)ft->fp); -} - -int lsx_error(sox_format_t * ft) -{ - return ferror((FILE*)ft->fp); -} - -void lsx_rewind(sox_format_t * ft) -{ - rewind((FILE*)ft->fp); - ft->tell_off = 0; -} - -void lsx_clearerr(sox_format_t * ft) -{ - clearerr((FILE*)ft->fp); - ft->sox_errno = 0; -} - -int lsx_unreadb(sox_format_t * ft, unsigned b) -{ - return ungetc((int)b, ft->fp); -} - -/* Implements traditional fseek() behavior. Meant to abstract out - * file operations so that they could one day also work on memory - * buffers. - * - * N.B. Can only seek forwards on non-seekable streams! - */ -int lsx_seeki(sox_format_t * ft, off_t offset, int whence) -{ - if (ft->seekable == 0) { - /* If a stream peel off chars else EPERM */ - if (whence == SEEK_CUR) { - while (offset > 0 && !feof((FILE*)ft->fp)) { - getc((FILE*)ft->fp); - offset--; - ++ft->tell_off; - } - if (offset) - lsx_fail_errno(ft,SOX_EOF, "offset past EOF"); - else - ft->sox_errno = SOX_SUCCESS; - } else - lsx_fail_errno(ft,SOX_EPERM, "file not seekable"); - } else { - if (fseeko((FILE*)ft->fp, offset, whence) == -1) - lsx_fail_errno(ft,errno, "%s", strerror(errno)); - else - ft->sox_errno = SOX_SUCCESS; - } - return ft->sox_errno; -} - -int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, off_t to_sample) -{ - double wide_sample = to_sample - (to_sample % ft->signal.channels); - double to_d = wide_sample * ft->encoding.bits_per_sample / 8; - off_t to = to_d; - return (to != to_d)? SOX_EOF : lsx_seeki(ft, (byte_offset + to), SEEK_SET); -} - -/* Read and write known datatypes in "machine format". Swap if indicated. - * They all return SOX_EOF on error and SOX_SUCCESS on success. - */ -/* Read n-char string (and possibly null-terminating). - * Stop reading and null-terminate string if either a 0 or \n is reached. - */ -int lsx_reads(sox_format_t * ft, char *c, size_t len) -{ - char *sc; - char in; - - sc = c; - do - { - if (lsx_readbuf(ft, &in, (size_t)1) != 1) - { - *sc = 0; - return (SOX_EOF); - } - if (in == 0 || in == '\n') - break; - - *sc = in; - sc++; - } while (sc - c < (ptrdiff_t)len); - *sc = 0; - return(SOX_SUCCESS); -} - -/* Write null-terminated string (without \0). */ -int lsx_writes(sox_format_t * ft, char const * c) -{ - if (lsx_writebuf(ft, c, strlen(c)) != strlen(c)) - return(SOX_EOF); - return(SOX_SUCCESS); -} - -/* return swapped 32-bit float */ -static void lsx_swapf(float * f) -{ - union { - uint32_t dw; - float f; - } u; - - u.f= *f; - u.dw= (u.dw>>24) | ((u.dw>>8)&0xff00) | ((u.dw<<8)&0xff0000) | (u.dw<<24); - *f = u.f; -} - -static void swap(void * data, size_t len) -{ - uint8_t * bytes = (uint8_t *)data; - size_t i; - - for (i = 0; i < len / 2; ++i) { - char tmp = bytes[i]; - bytes[i] = bytes[len - 1 - i]; - bytes[len - 1 - i] = tmp; - } -} - -static double lsx_swapdf(double data) -{ - swap(&data, sizeof(data)); - return data; -} - -static uint64_t lsx_swapqw(uint64_t data) -{ - swap(&data, sizeof(data)); - return data; -} - -/* Lookup table to reverse the bit order of a byte. ie MSB become LSB */ -static uint8_t const cswap[256] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, - 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, - 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, - 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, - 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, - 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, - 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, - 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, - 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, - 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, - 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, - 0x3F, 0xBF, 0x7F, 0xFF -}; - -/* Utilities to byte-swap values, use libc optimized macros if possible */ -#define TWIDDLE_BYTE(ub, type) \ - do { \ - if (ft->encoding.reverse_bits) \ - ub = cswap[ub]; \ - if (ft->encoding.reverse_nibbles) \ - ub = ((ub & 15) << 4) | (ub >> 4); \ - } while (0); - -#define TWIDDLE_WORD(uw, type) \ - if (ft->encoding.reverse_bytes) \ - uw = lsx_swap ## type(uw); - -#define TWIDDLE_FLOAT(f, type) \ - if (ft->encoding.reverse_bytes) \ - lsx_swapf(&f); - -/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte - types). */ -#define READ_FUNC(type, size, ctype, twiddle) \ - size_t lsx_read_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nread; \ - nread = lsx_readbuf(ft, buf, len * size) / size; \ - for (n = 0; n < nread; n++) \ - twiddle(buf[n], type); \ - return nread; \ - } - -/* Unpack a 3-byte value from a uint8_t * */ -#define sox_unpack3(p) (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN? \ - ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16)) : \ - ((p)[2] | ((p)[1] << 8) | ((p)[0] << 16))) - -/* This (slower) macro works for unaligned types (e.g. 3-byte types) - that need to be unpacked. */ -#define READ_FUNC_UNPACK(type, size, ctype, twiddle) \ - size_t lsx_read_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nread; \ - uint8_t *data = lsx_malloc(size * len); \ - nread = lsx_readbuf(ft, data, len * size) / size; \ - for (n = 0; n < nread; n++) \ - buf[n] = sox_unpack ## size(data + n * size); \ - free(data); \ - return n; \ - } - -READ_FUNC(b, 1, uint8_t, TWIDDLE_BYTE) -READ_FUNC(w, 2, uint16_t, TWIDDLE_WORD) -READ_FUNC_UNPACK(3, 3, sox_uint24_t, TWIDDLE_WORD) -READ_FUNC(dw, 4, uint32_t, TWIDDLE_WORD) -READ_FUNC(qw, 8, uint64_t, TWIDDLE_WORD) -READ_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT) -READ_FUNC(df, sizeof(double), double, TWIDDLE_WORD) - -#define READ1_FUNC(type, ctype) \ -int lsx_read ## type(sox_format_t * ft, ctype * datum) { \ - if (lsx_read_ ## type ## _buf(ft, datum, (size_t)1) == 1) \ - return SOX_SUCCESS; \ - if (!lsx_error(ft)) \ - lsx_fail_errno(ft, errno, premature_eof); \ - return SOX_EOF; \ -} - -static char const premature_eof[] = "premature EOF"; - -READ1_FUNC(b, uint8_t) -READ1_FUNC(w, uint16_t) -READ1_FUNC(3, sox_uint24_t) -READ1_FUNC(dw, uint32_t) -READ1_FUNC(qw, uint64_t) -READ1_FUNC(f, float) -READ1_FUNC(df, double) - -int lsx_readchars(sox_format_t * ft, char * chars, size_t len) -{ - size_t ret = lsx_readbuf(ft, chars, len); - if (ret == len) - return SOX_SUCCESS; - if (!lsx_error(ft)) - lsx_fail_errno(ft, errno, premature_eof); - return SOX_EOF; -} - -/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte - types). */ -#define WRITE_FUNC(type, size, ctype, twiddle) \ - size_t lsx_write_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nwritten; \ - for (n = 0; n < len; n++) \ - twiddle(buf[n], type); \ - nwritten = lsx_writebuf(ft, buf, len * size); \ - return nwritten / size; \ - } - -/* Pack a 3-byte value to a uint8_t * */ -#define sox_pack3(p, v) do {if (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN)\ -{(p)[0] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[2] = (v >> 16) & 0xff;} else \ -{(p)[2] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[0] = (v >> 16) & 0xff;} \ -} while (0) - -/* This (slower) macro works for unaligned types (e.g. 3-byte types) - that need to be packed. */ -#define WRITE_FUNC_PACK(type, size, ctype, twiddle) \ - size_t lsx_write_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nwritten; \ - uint8_t *data = lsx_malloc(size * len); \ - for (n = 0; n < len; n++) \ - sox_pack ## size(data + n * size, buf[n]); \ - nwritten = lsx_writebuf(ft, data, len * size); \ - free(data); \ - return nwritten / size; \ - } - -WRITE_FUNC(b, 1, uint8_t, TWIDDLE_BYTE) -WRITE_FUNC(w, 2, uint16_t, TWIDDLE_WORD) -WRITE_FUNC_PACK(3, 3, sox_uint24_t, TWIDDLE_WORD) -WRITE_FUNC(dw, 4, uint32_t, TWIDDLE_WORD) -WRITE_FUNC(qw, 8, uint64_t, TWIDDLE_WORD) -WRITE_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT) -WRITE_FUNC(df, sizeof(double), double, TWIDDLE_WORD) - -#define WRITE1U_FUNC(type, ctype) \ - int lsx_write ## type(sox_format_t * ft, unsigned d) \ - { ctype datum = (ctype)d; \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -#define WRITE1S_FUNC(type, ctype) \ - int lsx_writes ## type(sox_format_t * ft, signed d) \ - { ctype datum = (ctype)d; \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -#define WRITE1_FUNC(type, ctype) \ - int lsx_write ## type(sox_format_t * ft, ctype datum) \ - { \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -WRITE1U_FUNC(b, uint8_t) -WRITE1U_FUNC(w, uint16_t) -WRITE1U_FUNC(3, sox_uint24_t) -WRITE1U_FUNC(dw, uint32_t) -WRITE1_FUNC(qw, uint64_t) -WRITE1S_FUNC(b, uint8_t) -WRITE1S_FUNC(w, uint16_t) -WRITE1_FUNC(df, double) - -int lsx_writef(sox_format_t * ft, double datum) -{ - float f = datum; - return lsx_write_f_buf(ft, &f, (size_t) 1) == 1 ? SOX_SUCCESS : SOX_EOF; -} diff --git a/freedv/branches/1.2/freedv-dev/src/sox/libsox.c b/freedv/branches/1.2/freedv-dev/src/sox/libsox.c deleted file mode 100644 index 43620250..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/libsox.c +++ /dev/null @@ -1,225 +0,0 @@ -/* Implements the public API for libSoX general functions - * All public functions & data are prefixed with sox_ . - * - * (c) 2006-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include - -const char *sox_version(void) -{ - static char versionstr[20]; - - sprintf(versionstr, "%d.%d.%d", - (SOX_LIB_VERSION_CODE & 0xff0000) >> 16, - (SOX_LIB_VERSION_CODE & 0x00ff00) >> 8, - (SOX_LIB_VERSION_CODE & 0x0000ff)); - return(versionstr); -} - -sox_version_info_t const * sox_version_info(void) -{ -#define STRINGIZE1(x) #x -#define STRINGIZE(x) STRINGIZE1(x) - static char arch[30]; - static sox_version_info_t info = { - /* size */ - sizeof(sox_version_info_t), - /* flags */ - (sox_version_flags_t)( -#if HAVE_POPEN - sox_version_have_popen + -#endif -#if HAVE_MAGIC - sox_version_have_magic + -#endif -#if HAVE_OPENMP - sox_version_have_threads + -#endif -#ifdef HAVE_FMEMOPEN - sox_version_have_memopen + -#endif - sox_version_none), - /* version_code */ - SOX_LIB_VERSION_CODE, - /* version */ - NULL, - /* sox_version_extra */ -#ifdef PACKAGE_EXTRA - PACKAGE_EXTRA, -#else - NULL, -#endif - /* sox_time */ - __DATE__ " " __TIME__, - /* sox_distro */ -#ifdef DISTRO - DISTRO, -#else - NULL, -#endif - /* sox_compiler */ -#if defined __GNUC__ - "gcc " __VERSION__, -#elif defined _MSC_VER - "msvc " STRINGIZE(_MSC_FULL_VER), -#elif defined __SUNPRO_C - fprintf(file, "sun c " STRINGIZE(__SUNPRO_C), -#else - NULL, -#endif - /* sox_arch */ - NULL - }; - - if (!info.version) - { - info.version = sox_version(); - } - - if (!info.arch) - { - snprintf(arch, sizeof(arch), - "%" PRIuPTR "%" PRIuPTR "%" PRIuPTR "%" PRIuPTR - " %" PRIuPTR "%" PRIuPTR " %" PRIuPTR "%" PRIuPTR " %c %s", - sizeof(char), sizeof(short), sizeof(long), sizeof(off_t), - sizeof(float), sizeof(double), sizeof(int *), sizeof(int (*)(void)), - MACHINE_IS_BIGENDIAN ? 'B' : 'L', - (info.flags & sox_version_have_threads) ? "OMP" : ""); - arch[sizeof(arch) - 1] = 0; - info.arch = arch; - } - - return &info; -} - -/* Default routine to output messages; can be overridden */ -static void output_message( - unsigned level, const char *filename, const char *fmt, va_list ap) -{ - if (sox_globals.verbosity >= level) { - char base_name[128]; - sox_basename(base_name, sizeof(base_name), filename); - fprintf(stderr, "%s: ", base_name); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - } -} - -static sox_globals_t s_sox_globals = { - 2, /* unsigned verbosity */ - output_message, /* sox_output_message_handler */ - sox_false, /* sox_bool repeatable */ - 8192, /* size_t bufsiz */ - 0, /* size_t input_bufsiz */ - 0, /* int32_t ranqd1 */ - NULL, /* char const * stdin_in_use_by */ - NULL, /* char const * stdout_in_use_by */ - NULL, /* char const * subsystem */ - NULL, /* char * tmp_path */ - sox_false, /* sox_bool use_magic */ - sox_false /* sox_bool use_threads */ -}; - -sox_globals_t * sox_get_globals(void) -{ - return &s_sox_globals; -} - -/* FIXME: Not thread safe using globals */ -static sox_effects_globals_t s_sox_effects_globals = - {sox_plot_off, &s_sox_globals}; - -sox_effects_globals_t * -sox_get_effects_globals(void) -{ - return &s_sox_effects_globals; -} - -char const * sox_strerror(int sox_errno) -{ - static char const * const errors[] = { - "Invalid Audio Header", - "Unsupported data format", - "Can't allocate memory", - "Operation not permitted", - "Operation not supported", - "Invalid argument", - }; - if (sox_errno < SOX_EHDR) - return strerror(sox_errno); - sox_errno -= SOX_EHDR; - if (sox_errno < 0 || (size_t)sox_errno >= array_length(errors)) - return "Unknown error"; - return errors[sox_errno]; -} - -size_t sox_basename(char * base_buffer, size_t base_buffer_len, const char * filename) -{ - if (!base_buffer || !base_buffer_len) - { - return 0; - } - else - { - char const * slash_pos = LAST_SLASH(filename); - char const * base_name = slash_pos ? slash_pos + 1 : filename; - char const * dot_pos = strrchr(base_name, '.'); - size_t i, len; - dot_pos = dot_pos ? dot_pos : base_name + strlen(base_name); - len = dot_pos - base_name; - len = min(len, base_buffer_len - 1); - for (i = 0; i < len; i++) - { - base_buffer[i] = base_name[i]; - } - base_buffer[i] = 0; - return i; - } -} - -#define SOX_MESSAGE_FUNCTION(name,level) \ -void name(char const * fmt, ...) { \ - va_list ap; \ - va_start(ap, fmt); \ - if (sox_globals.output_message_handler) \ - (*sox_globals.output_message_handler)(level,sox_globals.subsystem,fmt,ap); \ - va_end(ap); \ -} - -SOX_MESSAGE_FUNCTION(lsx_fail_impl , 1) -SOX_MESSAGE_FUNCTION(lsx_warn_impl , 2) -SOX_MESSAGE_FUNCTION(lsx_report_impl, 3) -SOX_MESSAGE_FUNCTION(lsx_debug_impl , 4) -SOX_MESSAGE_FUNCTION(lsx_debug_more_impl , 5) -SOX_MESSAGE_FUNCTION(lsx_debug_most_impl , 6) - -#undef SOX_MESSAGE_FUNCTION - -int sox_init(void) -{ - return lsx_effects_init(); -} - -int sox_quit(void) -{ - #ifndef __FREEDV__ - sox_format_quit(); - #endif - return lsx_effects_quit(); -} diff --git a/freedv/branches/1.2/freedv-dev/src/sox/sox.h b/freedv/branches/1.2/freedv-dev/src/sox/sox.h deleted file mode 100644 index 05372558..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/sox.h +++ /dev/null @@ -1,2608 +0,0 @@ -/* libSoX Library Public Interface - * - * Copyright 1999-2011 Chris Bagwell and SoX Contributors. - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Chris Bagwell And SoX Contributors are not responsible for - * the consequences of using this software. - */ - -/** @file -Contains the interface exposed to clients of the libSoX library. -Symbols starting with "sox_" or "SOX_" are part of the public interface for -libSoX clients (applications that consume libSoX). Symbols starting with -"lsx_" or "LSX_" are internal use by libSoX and plugins. -LSX_ and lsx_ symbols should not be used by libSoX-based applications. -*/ - -#ifndef SOX_H -#define SOX_H /**< Client API: This macro is defined if sox.h has been included. */ - -#include -#include -#include - -#if defined(__cplusplus) -extern "C" { -#endif - -/* Suppress warnings from use of type long long. */ -#if defined __GNUC__ -#pragma GCC system_header -#endif - -/***************************************************************************** -API decoration macros: -Mostly for documentation purposes. For some compilers, decorations also affect -code generation, influence compiler warnings or activate compiler -optimizations. -*****************************************************************************/ - -/** -Plugins API: -Attribute required on all functions exported by libSoX and on all function -pointer types used by the libSoX API. -*/ -#ifdef __GNUC__ -#define LSX_API __attribute__ ((cdecl)) /* libSoX function */ -#elif _MSC_VER -#define LSX_API __cdecl /* libSoX function */ -#else -#define LSX_API /* libSoX function */ -#endif - -/** -Plugins API: -Attribute applied to a parameter or local variable to suppress warnings about -the variable being unused (especially in macro-generated code). -*/ -#ifdef __GNUC__ -#define LSX_UNUSED __attribute__ ((unused)) /* Parameter or local variable is intentionally unused. */ -#else -#define LSX_UNUSED /* Parameter or local variable is intentionally unused. */ -#endif - -/** -Plugins API: -LSX_PRINTF12: Attribute applied to a function to indicate that it requires -a printf-style format string for arg1 and that printf parameters start at -arg2. -*/ -#ifdef __GNUC__ -#define LSX_PRINTF12 __attribute__ ((format (printf, 1, 2))) /* Function has printf-style arguments. */ -#else -#define LSX_PRINTF12 /* Function has printf-style arguments. */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that it has no side effects and -depends only its input parameters and global memory. If called repeatedly, it -returns the same result each time. -*/ -#ifdef __GNUC__ -#define LSX_RETURN_PURE __attribute__ ((pure)) /* Function is pure. */ -#else -#define LSX_RETURN_PURE /* Function is pure. */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the -return value is always a pointer to a valid object (never NULL). -*/ -#ifdef _Ret_ -#define LSX_RETURN_VALID _Ret_ /* Function always returns a valid object (never NULL). */ -#else -#define LSX_RETURN_VALID /* Function always returns a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the return value is always a -pointer to a valid array (never NULL). -*/ -#ifdef _Ret_valid_ -#define LSX_RETURN_ARRAY _Ret_valid_ /* Function always returns a valid array (never NULL). */ -#else -#define LSX_RETURN_ARRAY /* Function always returns a valid array (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the return value is always a -pointer to a valid 0-terminated array (never NULL). -*/ -#ifdef _Ret_z_ -#define LSX_RETURN_VALID_Z _Ret_z_ /* Function always returns a 0-terminated array (never NULL). */ -#else -#define LSX_RETURN_VALID_Z /* Function always returns a 0-terminated array (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the returned pointer may be -null. -*/ -#ifdef _Ret_opt_ -#define LSX_RETURN_OPT _Ret_opt_ /* Function may return NULL. */ -#else -#define LSX_RETURN_OPT /* Function may return NULL. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to one const element of the pointed-to type (never NULL). -*/ -#ifdef _In_ -#define LSX_PARAM_IN _In_ /* Required const pointer to a valid object (never NULL). */ -#else -#define LSX_PARAM_IN /* Required const pointer to a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to a const 0-terminated string (never NULL). -*/ -#ifdef _In_z_ -#define LSX_PARAM_IN_Z _In_z_ /* Required const pointer to 0-terminated string (never NULL). */ -#else -#define LSX_PARAM_IN_Z /* Required const pointer to 0-terminated string (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a const -pointer to a 0-terminated printf format string. -*/ -#ifdef _Printf_format_string_ -#define LSX_PARAM_IN_PRINTF _Printf_format_string_ /* Required const pointer to 0-terminated printf format string (never NULL). */ -#else -#define LSX_PARAM_IN_PRINTF /* Required const pointer to 0-terminated printf format string (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) const initialized elements of the pointed-to type, where -(len) is the name of another parameter. -@param len The parameter that contains the number of elements in the array. -*/ -#ifdef _In_count_ -#define LSX_PARAM_IN_COUNT(len) _In_count_(len) /* Required const pointer to (len) valid objects (never NULL). */ -#else -#define LSX_PARAM_IN_COUNT(len) /* Required const pointer to (len) valid objects (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) const bytes of initialized data, where (len) is the name of -another parameter. -@param len The parameter that contains the number of bytes in the array. -*/ -#ifdef _In_bytecount_ -#define LSX_PARAM_IN_BYTECOUNT(len) _In_bytecount_(len) /* Required const pointer to (len) bytes of data (never NULL). */ -#else -#define LSX_PARAM_IN_BYTECOUNT(len) /* Required const pointer to (len) bytes of data (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to one const element of the pointed-to type. -*/ -#ifdef _In_opt_ -#define LSX_PARAM_IN_OPT _In_opt_ /* Optional const pointer to a valid object (may be NULL). */ -#else -#define LSX_PARAM_IN_OPT /* Optional const pointer to a valid object (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to a const 0-terminated string. -*/ -#ifdef _In_opt_z_ -#define LSX_PARAM_IN_OPT_Z _In_opt_z_ /* Optional const pointer to 0-terminated string (may be NULL). */ -#else -#define LSX_PARAM_IN_OPT_Z /* Optional const pointer to 0-terminated string (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to one initialized element of the pointed-to type (never NULL). The -function may modify the element. -*/ -#ifdef _Inout_ -#define LSX_PARAM_INOUT _Inout_ /* Required pointer to a valid object (never NULL). */ -#else -#define LSX_PARAM_INOUT /* Required pointer to a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) initialized elements of the pointed-to type (never NULL). The -function may modify the elements. -@param len The parameter that contains the number of elements in the array. -*/ -#ifdef _Inout_count_x_ -#define LSX_PARAM_INOUT_COUNT(len) _Inout_count_x_(len) /* Required pointer to (len) valid objects (never NULL). */ -#else -#define LSX_PARAM_INOUT_COUNT(len) /* Required pointer to (len) valid objects (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for one element of the pointed-to type (never -NULL). The function will initialize the element. -*/ -#ifdef _Out_ -#define LSX_PARAM_OUT _Out_ /* Required pointer to an object to be initialized (never NULL). */ -#else -#define LSX_PARAM_OUT /* Required pointer to an object to be initialized (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) bytes of data (never NULL), where (len) -is the name of another parameter. The function may write up to len bytes of -data to this memory. -@param len The parameter that contains the number of bytes in the array. -*/ -#ifdef _Out_bytecap_ -#define LSX_PARAM_OUT_BYTECAP(len) _Out_bytecap_(len) /* Required pointer to writable buffer with room for len bytes. */ -#else -#define LSX_PARAM_OUT_BYTECAP(len) /* Required pointer to writable buffer with room for len bytes. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) elements of the pointed-to type (never -NULL), where (len) is the name of another parameter. On return, (filled) -elements will have been initialized, where (filled) is either the dereference -of another pointer parameter (for example "*written") or the "return" -parameter (indicating that the function returns the number of elements -written). -@param len The parameter that contains the number of elements in the array. -@param filled The dereference of the parameter that receives the number of elements written to the array, or "return" if the value is returned. -*/ -#ifdef _Out_cap_post_count_ -#define LSX_PARAM_OUT_CAP_POST_COUNT(len,filled) _Out_cap_post_count_(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled) elements will have been initialized. */ -#else -#define LSX_PARAM_OUT_CAP_POST_COUNT(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled) elements will have been initialized. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) elements of the pointed-to type (never -NULL), where (len) is the name of another parameter. On return, (filled+1) -elements will have been initialized, with the last element having been -initialized to 0, where (filled) is either the dereference of another pointer -parameter (for example, "*written") or the "return" parameter (indicating that -the function returns the number of elements written). -@param len The parameter that contains the number of elements in the array. -@param filled The dereference of the parameter that receives the number of elements written to the array (not counting the terminating null), or "return" if the value is returned. -*/ -#ifdef _Out_z_cap_post_count_ -#define LSX_PARAM_OUT_Z_CAP_POST_COUNT(len,filled) _Out_z_cap_post_count_(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled+1) elements will have been initialized, and the array will be 0-terminated. */ -#else -#define LSX_PARAM_OUT_Z_CAP_POST_COUNT(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled+1) elements will have been initialized, and the array will be 0-terminated. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to memory sufficient for one element of the pointed-to -type. The function will initialize the element. -*/ -#ifdef _Out_opt_ -#define LSX_PARAM_OUT_OPT _Out_opt_ /* Optional pointer to an object to be initialized (may be NULL). */ -#else -#define LSX_PARAM_OUT_OPT /* Optional pointer to an object to be initialized (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which may be NULL when the function is -invoked. -*/ -#ifdef _Deref_pre_maybenull_ -#define LSX_PARAM_DEREF_PRE_MAYBENULL _Deref_pre_maybenull_ /* Required pointer (never NULL) to another pointer (may be NULL). */ -#else -#define LSX_PARAM_DEREF_PRE_MAYBENULL /* Required pointer (never NULL) to another pointer (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which will be NULL when the function -returns. -*/ -#ifdef _Deref_post_null_ -#define LSX_PARAM_DEREF_POST_NULL _Deref_post_null_ /* Required pointer (never NULL) to another pointer, which will be NULL on exit. */ -#else -#define LSX_PARAM_DEREF_POST_NULL /* Required pointer (never NULL) to another pointer, which will be NULL on exit. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which will be non-NULL when the -function returns. -*/ -#ifdef _Deref_post_notnull_ -#define LSX_PARAM_DEREF_POST_NOTNULL _Deref_post_notnull_ /* Required pointer (never NULL) to another pointer, which will be valid (not NULL) on exit. */ -#else -#define LSX_PARAM_DEREF_POST_NOTNULL /* Required pointer (never NULL) to another pointer, which will be valid (not NULL) on exit. */ -#endif - -/** -Plugins API: -Expression that "uses" a potentially-unused variable to avoid compiler -warnings (especially in macro-generated code). -*/ -#ifdef _PREFAST_ -#define LSX_USE_VAR(x) ((void)(x=0)) /* During static analysis, initialize unused variables to 0. */ -#else -#define LSX_USE_VAR(x) ((void)(x)) /* Parameter or variable is intentionally unused. */ -#endif - -/** -Plugins API: -Compile-time assertion. Causes a compile error if the expression is false. -@param e The expression to test. If expression is false, compilation will fail. -@param f A unique identifier for the test, for example foo_must_not_be_zero. -*/ -#define lsx_static_assert(e,f) enum {lsx_static_assert_##f = 1/((e) ? 1 : 0)} - -/***************************************************************************** -Basic typedefs: -*****************************************************************************/ - -/** -Client API: -Signed twos-complement 8-bit type. Typically defined as signed char. -*/ -#if SCHAR_MAX==127 && SCHAR_MIN==(-128) -typedef signed char sox_int8_t; -#elif CHAR_MAX==127 && CHAR_MIN==(-128) -typedef char sox_int8_t; -#else -#error Unable to determine an appropriate definition for sox_int8_t. -#endif - -/** -Client API: -Unsigned 8-bit type. Typically defined as unsigned char. -*/ -#if UCHAR_MAX==0xff -typedef unsigned char sox_uint8_t; -#elif CHAR_MAX==0xff && CHAR_MIN==0 -typedef char sox_uint8_t; -#else -#error Unable to determine an appropriate definition for sox_uint8_t. -#endif - -/** -Client API: -Signed twos-complement 16-bit type. Typically defined as short. -*/ -#if SHRT_MAX==32767 && SHRT_MIN==(-32768) -typedef short sox_int16_t; -#elif INT_MAX==32767 && INT_MIN==(-32768) -typedef int sox_int16_t; -#else -#error Unable to determine an appropriate definition for sox_int16_t. -#endif - -/** -Client API: -Unsigned 16-bit type. Typically defined as unsigned short. -*/ -#if USHRT_MAX==0xffff -typedef unsigned short sox_uint16_t; -#elif UINT_MAX==0xffff -typedef unsigned int sox_uint16_t; -#else -#error Unable to determine an appropriate definition for sox_uint16_t. -#endif - -/** -Client API: -Signed twos-complement 32-bit type. Typically defined as int. -*/ -#if INT_MAX==2147483647 && INT_MIN==(-2147483647-1) -typedef int sox_int32_t; -#elif LONG_MAX==2147483647 && LONG_MIN==(-2147483647-1) -typedef long sox_int32_t; -#else -#error Unable to determine an appropriate definition for sox_int32_t. -#endif - -/** -Client API: -Unsigned 32-bit type. Typically defined as unsigned int. -*/ -#if UINT_MAX==0xffffffff -typedef unsigned int sox_uint32_t; -#elif ULONG_MAX==0xffffffff -typedef unsigned long sox_uint32_t; -#else -#error Unable to determine an appropriate definition for sox_uint32_t. -#endif - -/** -Client API: -Signed twos-complement 64-bit type. Typically defined as long or long long. -*/ -#if LONG_MAX==9223372036854775807 && LONG_MIN==(-9223372036854775807-1) -typedef long sox_int64_t; -#elif defined(_MSC_VER) -typedef __int64 sox_int64_t; -#else -typedef long long sox_int64_t; -#endif - -/** -Client API: -Unsigned 64-bit type. Typically defined as unsigned long or unsigned long long. -*/ -#if ULONG_MAX==0xffffffffffffffff -typedef unsigned long sox_uint64_t; -#elif defined(_MSC_VER) -typedef unsigned __int64 sox_uint64_t; -#else -typedef unsigned long long sox_uint64_t; -#endif - -#ifndef _DOXYGEN_ -lsx_static_assert(sizeof(sox_int8_t)==1, sox_int8_size); -lsx_static_assert(sizeof(sox_uint8_t)==1, sox_uint8_size); -lsx_static_assert(sizeof(sox_int16_t)==2, sox_int16_size); -lsx_static_assert(sizeof(sox_uint16_t)==2, sox_uint16_size); -lsx_static_assert(sizeof(sox_int32_t)==4, sox_int32_size); -lsx_static_assert(sizeof(sox_uint32_t)==4, sox_uint32_size); -lsx_static_assert(sizeof(sox_int64_t)==8, sox_int64_size); -lsx_static_assert(sizeof(sox_uint64_t)==8, sox_uint64_size); -#endif - -/** -Client API: -Alias for sox_int32_t (beware of the extra byte). -*/ -typedef sox_int32_t sox_int24_t; - -/** -Client API: -Alias for sox_uint32_t (beware of the extra byte). -*/ -typedef sox_uint32_t sox_uint24_t; - -/** -Client API: -Native SoX audio sample type (alias for sox_int32_t). -*/ -typedef sox_int32_t sox_sample_t; - -/** -Client API: -Samples per second is stored as a double. -*/ -typedef double sox_rate_t; - -/** -Client API: -File's metadata, access via sox_*_comments functions. -*/ -typedef char * * sox_comments_t; - -/***************************************************************************** -Enumerations: -*****************************************************************************/ - -/** -Client API: -Boolean type, assignment (but not necessarily binary) compatible with C++ bool. -*/ -typedef enum sox_bool { - sox_false, /**< False = 0. */ - sox_true /**< True = 1. */ -} sox_bool; - -/** -Client API: -no, yes, or default (default usually implies some kind of auto-detect logic). -*/ -typedef enum sox_option_t { - sox_option_no, /**< Option specified as no = 0. */ - sox_option_yes, /**< Option specified as yes = 1. */ - sox_option_default /**< Option unspecified = 2. */ -} sox_option_t; - -/** -Client API: -The libSoX-specific error codes. -libSoX functions may return these codes or others that map from errno codes. -*/ -enum sox_error_t { - SOX_SUCCESS = 0, /**< Function succeeded = 0 */ - SOX_EOF = -1, /**< End Of File or other error = -1 */ - SOX_EHDR = 2000, /**< Invalid Audio Header = 2000 */ - SOX_EFMT, /**< Unsupported data format = 2001 */ - SOX_ENOMEM, /**< Can't alloc memory = 2002 */ - SOX_EPERM, /**< Operation not permitted = 2003 */ - SOX_ENOTSUP, /**< Operation not supported = 2004 */ - SOX_EINVAL /**< Invalid argument = 2005 */ -}; - -/** -Client API: -Flags indicating whether optional features are present in this build of libSoX. -*/ -typedef enum sox_version_flags_t { - sox_version_none = 0, /**< No special features = 0. */ - sox_version_have_popen = 1, /**< popen = 1. */ - sox_version_have_magic = 2, /**< magic = 2. */ - sox_version_have_threads = 4, /**< threads = 4. */ - sox_version_have_memopen = 8 /**< memopen = 8. */ -} sox_version_flags_t; - -/** -Client API: -Format of sample data. -*/ -typedef enum sox_encoding_t { - SOX_ENCODING_UNKNOWN , /**< encoding has not yet been determined */ - - SOX_ENCODING_SIGN2 , /**< signed linear 2's comp: Mac */ - SOX_ENCODING_UNSIGNED , /**< unsigned linear: Sound Blaster */ - SOX_ENCODING_FLOAT , /**< floating point (binary format) */ - SOX_ENCODING_FLOAT_TEXT, /**< floating point (text format) */ - SOX_ENCODING_FLAC , /**< FLAC compression */ - SOX_ENCODING_HCOM , /**< Mac FSSD files with Huffman compression */ - SOX_ENCODING_WAVPACK , /**< WavPack with integer samples */ - SOX_ENCODING_WAVPACKF , /**< WavPack with float samples */ - SOX_ENCODING_ULAW , /**< u-law signed logs: US telephony, SPARC */ - SOX_ENCODING_ALAW , /**< A-law signed logs: non-US telephony, Psion */ - SOX_ENCODING_G721 , /**< G.721 4-bit ADPCM */ - SOX_ENCODING_G723 , /**< G.723 3 or 5 bit ADPCM */ - SOX_ENCODING_CL_ADPCM , /**< Creative Labs 8 --> 2,3,4 bit Compressed PCM */ - SOX_ENCODING_CL_ADPCM16, /**< Creative Labs 16 --> 4 bit Compressed PCM */ - SOX_ENCODING_MS_ADPCM , /**< Microsoft Compressed PCM */ - SOX_ENCODING_IMA_ADPCM , /**< IMA Compressed PCM */ - SOX_ENCODING_OKI_ADPCM , /**< Dialogic/OKI Compressed PCM */ - SOX_ENCODING_DPCM , /**< Differential PCM: Fasttracker 2 (xi) */ - SOX_ENCODING_DWVW , /**< Delta Width Variable Word */ - SOX_ENCODING_DWVWN , /**< Delta Width Variable Word N-bit */ - SOX_ENCODING_GSM , /**< GSM 6.10 33byte frame lossy compression */ - SOX_ENCODING_MP3 , /**< MP3 compression */ - SOX_ENCODING_VORBIS , /**< Vorbis compression */ - SOX_ENCODING_AMR_WB , /**< AMR-WB compression */ - SOX_ENCODING_AMR_NB , /**< AMR-NB compression */ - SOX_ENCODING_CVSD , /**< Continuously Variable Slope Delta modulation */ - SOX_ENCODING_LPC10 , /**< Linear Predictive Coding */ - - SOX_ENCODINGS /**< End of list marker */ -} sox_encoding_t; - -/** -Client API: -Flags for sox_encodings_info_t: lossless/lossy1/lossy2. -*/ -typedef enum sox_encodings_flags_t { - sox_encodings_none = 0, /**< no flags specified (implies lossless encoding) = 0. */ - sox_encodings_lossy1 = 1, /**< encode, decode: lossy once = 1. */ - sox_encodings_lossy2 = 2 /**< encode, decode, encode, decode: lossy twice = 2. */ -} sox_encodings_flags_t; - -/** -Client API: -Type of plot. -*/ -typedef enum sox_plot_t { - sox_plot_off, /**< No plot = 0. */ - sox_plot_octave, /**< Octave plot = 1. */ - sox_plot_gnuplot, /**< Gnuplot plot = 2. */ - sox_plot_data /**< Plot data = 3. */ -} sox_plot_t; - -/** -Client API: -Loop modes: upper 4 bits mask the loop blass, lower 4 bits describe -the loop behaviour, for example single shot, bidirectional etc. -*/ -enum sox_loop_flags_t { - sox_loop_none = 0, /**< single-shot = 0 */ - sox_loop_forward = 1, /**< forward loop = 1 */ - sox_loop_forward_back = 2, /**< forward/back loop = 2 */ - sox_loop_8 = 32, /**< 8 loops (??) = 32 */ - sox_loop_sustain_decay = 64 /**< AIFF style, one sustain & one decay loop = 64 */ -}; - -/** -Plugins API: -Is file a real file, a pipe, or a url? -*/ -typedef enum lsx_io_type -{ - lsx_io_file, /**< File is a real file = 0. */ - lsx_io_pipe, /**< File is a pipe (no seeking) = 1. */ - lsx_io_url /**< File is a URL (no seeking) = 2. */ -} lsx_io_type; - -/***************************************************************************** -Macros: -*****************************************************************************/ - -/** -Client API: -Compute a 32-bit integer API version from three 8-bit parts. -@param a Major version. -@param b Minor version. -@param c Revision or build number. -@returns 32-bit integer API version 0x000a0b0c. -*/ -#define SOX_LIB_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) - -/** -Client API: -The API version of the sox.h file. It is not meant to follow the version -number of SoX but it has historically. Please do not count on -SOX_LIB_VERSION_CODE staying in sync with the libSoX version. -*/ -#define SOX_LIB_VERSION_CODE SOX_LIB_VERSION(14, 4, 1) - -/** -Client API: -Returns the smallest (negative) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer; -for example, SOX_INT_MIN(8) = 0x80, SOX_INT_MIN(16) = 0x8000, etc. -@param bits Size of value for which to calculate minimum. -@returns the smallest (negative) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer. -*/ -#define SOX_INT_MIN(bits) (1 <<((bits)-1)) - -/** -Client API: -Returns the largest (positive) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer; -for example, SOX_INT_MAX(8) = 0x7F, SOX_INT_MAX(16) = 0x7FFF, etc. -@param bits Size of value for which to calculate maximum. -@returns the largest (positive) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer. -*/ -#define SOX_INT_MAX(bits) (((unsigned)-1)>>(33-(bits))) - -/** -Client API: -Returns the largest value storable in an unsigned integer with the specified -number of bits; for example, SOX_UINT_MAX(8) = 0xFF, -SOX_UINT_MAX(16) = 0xFFFF, etc. -@param bits Size of value for which to calculate maximum. -@returns the largest value storable in an unsigned integer with the specified -number of bits. -*/ -#define SOX_UINT_MAX(bits) (SOX_INT_MIN(bits)|SOX_INT_MAX(bits)) - -/** -Client API: -Returns 0x7F. -*/ -#define SOX_INT8_MAX SOX_INT_MAX(8) - -/** -Client API: -Returns 0x7FFF. -*/ -#define SOX_INT16_MAX SOX_INT_MAX(16) - -/** -Client API: -Returns 0x7FFFFF. -*/ -#define SOX_INT24_MAX SOX_INT_MAX(24) - -/** -Client API: -Returns 0x7FFFFFFF. -*/ -#define SOX_INT32_MAX SOX_INT_MAX(32) - -/** -Client API: -Bits in a sox_sample_t = 32. -*/ -#define SOX_SAMPLE_PRECISION 32 - -/** -Client API: -Max value for sox_sample_t = 0x7FFFFFFF. -*/ -#define SOX_SAMPLE_MAX (sox_sample_t)SOX_INT_MAX(32) - -/** -Client API: -Min value for sox_sample_t = 0x80000000. -*/ -#define SOX_SAMPLE_MIN (sox_sample_t)SOX_INT_MIN(32) - - -/* Conversions: Linear PCM <--> sox_sample_t - * - * I/O Input sox_sample_t Clips? Input sox_sample_t Clips? - * Format Minimum Minimum I O Maximum Maximum I O - * ------ --------- ------------ -- -- -------- ------------ -- -- - * Float -inf -1 y n inf 1 - 5e-10 y n - * Int8 -128 -128 n n 127 127.9999999 n y - * Int16 -32768 -32768 n n 32767 32767.99998 n y - * Int24 -8388608 -8388608 n n 8388607 8388607.996 n y - * Int32 -2147483648 -2147483648 n n 2147483647 2147483647 n n - * - * Conversions are as accurate as possible (with rounding). - * - * Rounding: halves toward +inf, all others to nearest integer. - * - * Clips? shows whether on not there is the possibility of a conversion - * clipping to the minimum or maximum value when inputing from or outputing - * to a given type. - * - * Unsigned integers are converted to and from signed integers by flipping - * the upper-most bit then treating them as signed integers. - */ - -/** -Client API: -Declares the temporary local variables that are required when using SOX -conversion macros. -*/ -#define SOX_SAMPLE_LOCALS sox_sample_t sox_macro_temp_sample LSX_UNUSED; \ - double sox_macro_temp_double LSX_UNUSED - -/** -Client API: -Sign bit for sox_sample_t = 0x80000000. -*/ -#define SOX_SAMPLE_NEG SOX_INT_MIN(32) - -/** -Client API: -Converts sox_sample_t to an unsigned integer of width (bits). -@param bits Width of resulting sample (1 through 32). -@param d Input sample to be converted. -@param clips Variable that is incremented if the result is too big. -@returns Unsigned integer of width (bits). -*/ -#define SOX_SAMPLE_TO_UNSIGNED(bits,d,clips) \ - (sox_uint##bits##_t)(SOX_SAMPLE_TO_SIGNED(bits,d,clips)^SOX_INT_MIN(bits)) - -/** -Client API: -Converts sox_sample_t to a signed integer of width (bits). -@param bits Width of resulting sample (1 through 32). -@param d Input sample to be converted. -@param clips Variable that is incremented if the result is too big. -@returns Signed integer of width (bits). -*/ -#define SOX_SAMPLE_TO_SIGNED(bits,d,clips) \ - (sox_int##bits##_t)(LSX_USE_VAR(sox_macro_temp_double),sox_macro_temp_sample=(d),sox_macro_temp_sample>SOX_SAMPLE_MAX-(1<<(31-bits))?++(clips),SOX_INT_MAX(bits):((sox_uint32_t)(sox_macro_temp_sample+(1<<(31-bits))))>>(32-bits)) - -/** -Client API: -Converts signed integer of width (bits) to sox_sample_t. -@param bits Width of input sample (1 through 32). -@param d Input sample to be converted. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_TO_SAMPLE(bits,d)((sox_sample_t)(d)<<(32-bits)) - -/** -Client API: -Converts unsigned integer of width (bits) to sox_sample_t. -@param bits Width of input sample (1 through 32). -@param d Input sample to be converted. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_TO_SAMPLE(bits,d)(SOX_SIGNED_TO_SAMPLE(bits,d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts unsigned 8-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_8BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(8,d) - -/** -Client API: -Converts signed 8-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_8BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(8,d) - -/** -Client API: -Converts unsigned 16-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_16BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(16,d) - -/** -Client API: -Converts signed 16-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_16BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(16,d) - -/** -Client API: -Converts unsigned 24-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_24BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(24,d) - -/** -Client API: -Converts signed 24-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_24BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(24,d) - -/** -Client API: -Converts unsigned 32-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_32BIT_TO_SAMPLE(d,clips) ((sox_sample_t)(d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts signed 32-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_32BIT_TO_SAMPLE(d,clips) (sox_sample_t)(d) - -/** -Client API: -Converts 32-bit float to sox_sample_t. -@param d Input sample to be converted, range [-1, 1). -@param clips Variable to increment if the input sample is too large or too small. -@returns SoX native sample value. -*/ -#define SOX_FLOAT_32BIT_TO_SAMPLE(d,clips) (sox_sample_t)(LSX_USE_VAR(sox_macro_temp_sample),sox_macro_temp_double=(d)*(SOX_SAMPLE_MAX+1.),sox_macro_temp_double=SOX_SAMPLE_MAX+1.?sox_macro_temp_double>SOX_SAMPLE_MAX+1.?++(clips),SOX_SAMPLE_MAX:SOX_SAMPLE_MAX:sox_macro_temp_double) - -/** -Client API: -Converts 64-bit float to sox_sample_t. -@param d Input sample to be converted, range [-1, 1). -@param clips Variable to increment if the input sample is too large or too small. -@returns SoX native sample value. -*/ -#define SOX_FLOAT_64BIT_TO_SAMPLE(d,clips) (sox_sample_t)(LSX_USE_VAR(sox_macro_temp_sample),sox_macro_temp_double=(d)*(SOX_SAMPLE_MAX+1.),sox_macro_temp_double<0?sox_macro_temp_double<=SOX_SAMPLE_MIN-.5?++(clips),SOX_SAMPLE_MIN:sox_macro_temp_double-.5:sox_macro_temp_double>=SOX_SAMPLE_MAX+.5?sox_macro_temp_double>SOX_SAMPLE_MAX+1.?++(clips),SOX_SAMPLE_MAX:SOX_SAMPLE_MAX:sox_macro_temp_double+.5) - -/** -Client API: -Converts SoX native sample to an unsigned 8-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_8BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(8,d,clips) - -/** -Client API: -Converts SoX native sample to an signed 8-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_8BIT(d,clips) SOX_SAMPLE_TO_SIGNED(8,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 16-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_16BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(16,d,clips) - -/** -Client API: -Converts SoX native sample to a signed 16-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_16BIT(d,clips) SOX_SAMPLE_TO_SIGNED(16,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 24-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_24BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(24,d,clips) - -/** -Client API: -Converts SoX native sample to a signed 24-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_24BIT(d,clips) SOX_SAMPLE_TO_SIGNED(24,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 32-bit integer. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_32BIT(d,clips) (sox_uint32_t)((d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts SoX native sample to a signed 32-bit integer. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_SIGNED_32BIT(d,clips) (sox_int32_t)(d) - -/** -Client API: -Converts SoX native sample to a 32-bit float. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_FLOAT_32BIT(d,clips) (LSX_USE_VAR(sox_macro_temp_double),sox_macro_temp_sample=(d),sox_macro_temp_sample>SOX_SAMPLE_MAX-128?++(clips),1:(((sox_macro_temp_sample+128)&~255)*(1./(SOX_SAMPLE_MAX+1.)))) - -/** -Client API: -Converts SoX native sample to a 64-bit float. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_FLOAT_64BIT(d,clips) ((d)*(1./(SOX_SAMPLE_MAX+1.))) - -/** -Client API: -Clips a value of a type that is larger then sox_sample_t (for example, int64) -to sox_sample_t's limits and increment a counter if clipping occurs. -@param samp Value (lvalue) to be clipped, updated as necessary. -@param clips Value (lvalue) that is incremented if clipping is needed. -*/ -#define SOX_SAMPLE_CLIP_COUNT(samp, clips) \ - do { \ - if (samp > SOX_SAMPLE_MAX) \ - { samp = SOX_SAMPLE_MAX; clips++; } \ - else if (samp < SOX_SAMPLE_MIN) \ - { samp = SOX_SAMPLE_MIN; clips++; } \ - } while (0) - -/** -Client API: -Clips a value of a type that is larger then sox_sample_t (for example, int64) -to sox_sample_t's limits and increment a counter if clipping occurs. -@param d Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_ROUND_CLIP_COUNT(d, clips) \ - ((d) < 0? (d) <= SOX_SAMPLE_MIN - 0.5? ++(clips), SOX_SAMPLE_MIN: (d) - 0.5 \ - : (d) >= SOX_SAMPLE_MAX + 0.5? ++(clips), SOX_SAMPLE_MAX: (d) + 0.5) - -/** -Client API: -Clips a value to the limits of a signed integer of the specified width -and increment a counter if clipping occurs. -@param bits Width (in bits) of target integer type. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_INTEGER_CLIP_COUNT(bits,i,clips) ( \ - (i) >(1 << ((bits)-1))- 1? ++(clips),(1 << ((bits)-1))- 1 : \ - (i) <-1 << ((bits)-1) ? ++(clips),-1 << ((bits)-1) : (i)) - -/** -Client API: -Clips a value to the limits of a 16-bit signed integer and increment a counter -if clipping occurs. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_16BIT_CLIP_COUNT(i,clips) SOX_INTEGER_CLIP_COUNT(16,i,clips) - -/** -Client API: -Clips a value to the limits of a 24-bit signed integer and increment a counter -if clipping occurs. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_24BIT_CLIP_COUNT(i,clips) SOX_INTEGER_CLIP_COUNT(24,i,clips) - -#define SOX_SIZE_MAX ((size_t)(-1)) /**< Client API: Maximum value of size_t. */ - -#define SOX_UNSPEC 0 /**< Client API: Members of sox_signalinfo_t are set to SOX_UNSPEC (= 0) if the actual value is not yet known. */ -#define SOX_UNKNOWN_LEN (sox_uint64_t)(-1) /**< Client API: sox_signalinfo_t.length is set to SOX_UNKNOWN_LEN (= -1) within the effects chain if the actual length is not known. Format handlers currently use SOX_UNSPEC instead. */ -#define SOX_IGNORE_LENGTH (sox_uint64_t)(-2) /**< Client API: sox_signalinfo_t.length is set to SOX_IGNORE_LENGTH (= -2) to indicate that a format handler should ignore length information in file headers. */ - -#define SOX_DEFAULT_CHANNELS 2 /**< Client API: Default channel count is 2 (stereo). */ -#define SOX_DEFAULT_RATE 48000 /**< Client API: Default rate is 48000Hz. */ -#define SOX_DEFAULT_PRECISION 16 /**< Client API: Default precision is 16 bits per sample. */ -#define SOX_DEFAULT_ENCODING SOX_ENCODING_SIGN2 /**< Client API: Default encoding is SIGN2 (linear 2's complement PCM). */ - -#define SOX_LOOP_NONE ((unsigned char)sox_loop_none) /**< Client API: single-shot = 0 */ -#define SOX_LOOP_8 ((unsigned char)sox_loop_8) /**< Client API: 8 loops = 32 */ -#define SOX_LOOP_SUSTAIN_DECAY ((unsigned char)sox_loop_sustain_decay) /**< Client API: AIFF style, one sustain & one decay loop = 64 */ - -#define SOX_MAX_NLOOPS 8 /**< Client API: Maximum number of loops supported by sox_oob_t = 8. */ - -#define SOX_FILE_NOSTDIO 0x0001 /**< Client API: Does not use stdio routines */ -#define SOX_FILE_DEVICE 0x0002 /**< Client API: File is an audio device */ -#define SOX_FILE_PHONY 0x0004 /**< Client API: Phony file/device (for example /dev/null) */ -#define SOX_FILE_REWIND 0x0008 /**< Client API: File should be rewound to write header */ -#define SOX_FILE_BIT_REV 0x0010 /**< Client API: Is file bit-reversed? */ -#define SOX_FILE_NIB_REV 0x0020 /**< Client API: Is file nibble-reversed? */ -#define SOX_FILE_ENDIAN 0x0040 /**< Client API: Is file format endian? */ -#define SOX_FILE_ENDBIG 0x0080 /**< Client API: For endian file format, is it big endian? */ -#define SOX_FILE_MONO 0x0100 /**< Client API: Do channel restrictions allow mono? */ -#define SOX_FILE_STEREO 0x0200 /**< Client API: Do channel restrictions allow stereo? */ -#define SOX_FILE_QUAD 0x0400 /**< Client API: Do channel restrictions allow quad? */ - -#define SOX_FILE_CHANS (SOX_FILE_MONO | SOX_FILE_STEREO | SOX_FILE_QUAD) /**< Client API: No channel restrictions */ -#define SOX_FILE_LIT_END (SOX_FILE_ENDIAN | 0) /**< Client API: File is little-endian */ -#define SOX_FILE_BIG_END (SOX_FILE_ENDIAN | SOX_FILE_ENDBIG) /**< Client API: File is big-endian */ - -#define SOX_EFF_CHAN 1 /**< Client API: Effect might alter the number of channels */ -#define SOX_EFF_RATE 2 /**< Client API: Effect might alter sample rate */ -#define SOX_EFF_PREC 4 /**< Client API: Effect does its own calculation of output sample precision (otherwise a default value is taken, depending on the presence of SOX_EFF_MODIFY) */ -#define SOX_EFF_LENGTH 8 /**< Client API: Effect might alter audio length (as measured in time units, not necessarily in samples) */ -#define SOX_EFF_MCHAN 16 /**< Client API: Effect handles multiple channels internally */ -#define SOX_EFF_NULL 32 /**< Client API: Effect does nothing (can be optimized out of chain) */ -#define SOX_EFF_DEPRECATED 64 /**< Client API: Effect will soon be removed from SoX */ -#define SOX_EFF_GAIN 128 /**< Client API: Effect does not support gain -r */ -#define SOX_EFF_MODIFY 256 /**< Client API: Effect does not modify sample values (but might remove or duplicate samples or insert zeros) */ -#define SOX_EFF_ALPHA 512 /**< Client API: Effect is experimental/incomplete */ -#define SOX_EFF_INTERNAL 1024 /**< Client API: Effect present in libSoX but not valid for use by SoX command-line tools */ - -/** -Client API: -When used as the "whence" parameter of sox_seek, indicates that the specified -offset is relative to the beginning of the file. -*/ -#define SOX_SEEK_SET 0 - -/***************************************************************************** -Forward declarations: -*****************************************************************************/ - -typedef struct sox_format_t sox_format_t; -typedef struct sox_effect_t sox_effect_t; -typedef struct sox_effect_handler_t sox_effect_handler_t; -typedef struct sox_format_handler_t sox_format_handler_t; - -/***************************************************************************** -Function pointers: -*****************************************************************************/ - -/** -Client API: -Callback to write a message to an output device (console or log file), -used by sox_globals_t.output_message_handler. -*/ -typedef void (LSX_API * sox_output_message_handler_t)( - unsigned level, /* 1 = FAIL, 2 = WARN, 3 = INFO, 4 = DEBUG, 5 = DEBUG_MORE, 6 = DEBUG_MOST. */ - LSX_PARAM_IN_Z char const * filename, /* Source code __FILENAME__ from which message originates. */ - LSX_PARAM_IN_PRINTF char const * fmt, /* Message format string. */ - LSX_PARAM_IN va_list ap /* Message format parameters. */ - ); - -/** -Client API: -Callback to retrieve information about a format handler, -used by sox_format_tab_t.fn. -@returns format handler information. -*/ -typedef sox_format_handler_t const * (LSX_API * sox_format_fn_t)(void); - -/** -Client API: -Callback to get information about an effect handler, -used by the table returned from sox_get_effect_fns(void). -@returns Pointer to information about an effect handler. -*/ -typedef sox_effect_handler_t const * (LSX_API *sox_effect_fn_t)(void); - -/** -Client API: -Callback to initialize reader (decoder), used by -sox_format_handler.startread. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_startread)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to read (decode) a block of samples, -used by sox_format_handler.read. -@returns number of samples read, or 0 if unsuccessful. -*/ -typedef size_t (LSX_API * sox_format_handler_read)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(len,return) sox_sample_t *buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Callback to close reader (decoder), -used by sox_format_handler.stopread. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_stopread)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to initialize writer (encoder), -used by sox_format_handler.startwrite. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_startwrite)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to write (encode) a block of samples, -used by sox_format_handler.write. -@returns number of samples written, or 0 if unsuccessful. -*/ -typedef size_t (LSX_API * sox_format_handler_write)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_IN_COUNT(len) sox_sample_t const * buf, /**< Buffer to which samples are written. */ - size_t len /**< Capacity of buf, measured in samples. */ - ); - -/** -Client API: -Callback to close writer (decoder), -used by sox_format_handler.stopwrite. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_stopwrite)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to reposition reader, -used by sox_format_handler.seek. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_seek)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - sox_uint64_t offset /**< Sample offset to which reader should be positioned. */ - ); - -/** -Client API: -Callback to parse command-line arguments (called once per effect), -used by sox_effect_handler.getopts. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_getopts)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - int argc, /**< Number of arguments in argv. */ - LSX_PARAM_IN_COUNT(argc) char *argv[] /**< Array of command-line arguments. */ - ); - -/** -Client API: -Callback to initialize effect (called once per flow), -used by sox_effect_handler.start. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_start)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback to process samples, -used by sox_effect_handler.flow. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_flow)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - LSX_PARAM_IN_COUNT(*isamp) sox_sample_t const * ibuf, /**< Buffer from which to read samples. */ - LSX_PARAM_OUT_CAP_POST_COUNT(*osamp,*osamp) sox_sample_t * obuf, /**< Buffer to which samples are written. */ - LSX_PARAM_INOUT size_t *isamp, /**< On entry, contains capacity of ibuf; on exit, contains number of samples consumed. */ - LSX_PARAM_INOUT size_t *osamp /**< On entry, contains capacity of obuf; on exit, contains number of samples written. */ - ); - -/** -Client API: -Callback to finish getting output after input is complete, -used by sox_effect_handler.drain. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_drain)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(*osamp,*osamp) sox_sample_t *obuf, /**< Buffer to which samples are written. */ - LSX_PARAM_INOUT size_t *osamp /**< On entry, contains capacity of obuf; on exit, contains number of samples written. */ - ); - -/** -Client API: -Callback to shut down effect (called once per flow), -used by sox_effect_handler.stop. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_stop)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback to shut down effect (called once per effect), -used by sox_effect_handler.kill. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_kill)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback called while flow is running (called once per buffer), -used by sox_flow_effects.callback. -@returns SOX_SUCCESS to continue, other value to abort flow. -*/ -typedef int (LSX_API * sox_flow_effects_callback)( - sox_bool all_done, - void * client_data - ); - -/** -Client API: -Callback for enumerating the contents of a playlist, -used by the sox_parse_playlist function. -@returns SOX_SUCCESS if successful, any other value to abort playlist enumeration. -*/ -typedef int (LSX_API * sox_playlist_callback_t)( - void * callback_data, - LSX_PARAM_IN_Z char const * filename - ); - -/***************************************************************************** -Structures: -*****************************************************************************/ - -/** -Client API: -Information about a build of libSoX, returned from the sox_version_info -function. -*/ -typedef struct sox_version_info_t { - size_t size; /**< structure size = sizeof(sox_version_info_t) */ - sox_version_flags_t flags; /**< feature flags = popen | magic | threads | memopen */ - sox_uint32_t version_code; /**< version number = 0x140400 */ - char const * version; /**< version string = sox_version(), for example, "14.4.0" */ - char const * version_extra;/**< version extra info or null = "PACKAGE_EXTRA", for example, "beta" */ - char const * time; /**< build time = "__DATE__ __TIME__", for example, "Jan 7 2010 03:31:50" */ - char const * distro; /**< distro or null = "DISTRO", for example, "Debian" */ - char const * compiler; /**< compiler info or null, for example, "msvc 160040219" */ - char const * arch; /**< arch, for example, "1248 48 44 L OMP" */ - /* new info should be added at the end for version backwards-compatibility. */ -} sox_version_info_t; - -/** -Client API: -Global parameters (for effects & formats), returned from the sox_get_globals -function. -*/ -typedef struct sox_globals_t { -/* public: */ - unsigned verbosity; /**< messages are only written if globals.verbosity >= message.level */ - sox_output_message_handler_t output_message_handler; /**< client-specified message output callback */ - sox_bool repeatable; /**< true to use pre-determined timestamps and PRNG seed */ - - /** - Default size (in bytes) used by libSoX for blocks of sample data. - Plugins should use similarly-sized buffers to get best performance. - */ - size_t bufsiz; - - /** - Default size (in bytes) used by libSoX for blocks of input sample data. - Plugins should use similarly-sized buffers to get best performance. - */ - size_t input_bufsiz; - - sox_int32_t ranqd1; /**< Can be used to re-seed libSoX's PRNG */ - - char const * stdin_in_use_by; /**< Private: tracks the name of the handler currently using stdin */ - char const * stdout_in_use_by; /**< Private: tracks the name of the handler currently using stdout */ - char const * subsystem; /**< Private: tracks the name of the handler currently writing an output message */ - char * tmp_path; /**< Private: client-configured path to use for temporary files */ - sox_bool use_magic; /**< Private: true if client has requested use of 'magic' file-type detection */ - sox_bool use_threads; /**< Private: true if client has requested parallel effects processing */ -} sox_globals_t; - -/** -Client API: -Signal parameters; members should be set to SOX_UNSPEC (= 0) if unknown. -*/ -typedef struct sox_signalinfo_t { - sox_rate_t rate; /**< samples per second, 0 if unknown */ - unsigned channels; /**< number of sound channels, 0 if unknown */ - unsigned precision; /**< bits per sample, 0 if unknown */ - sox_uint64_t length; /**< samples * chans in file, 0 if unknown, -1 if unspecified */ - double * mult; /**< Effects headroom multiplier; may be null */ -} sox_signalinfo_t; - -/** -Client API: -Basic information about an encoding. -*/ -typedef struct sox_encodings_info_t { - sox_encodings_flags_t flags; /**< lossy once (lossy1), lossy twice (lossy2), or lossless (none). */ - char const * name; /**< encoding name. */ - char const * desc; /**< encoding description. */ -} sox_encodings_info_t; - -/** -Client API: -Encoding parameters. -*/ -typedef struct sox_encodinginfo_t { - sox_encoding_t encoding; /**< format of sample numbers */ - unsigned bits_per_sample;/**< 0 if unknown or variable; uncompressed value if lossless; compressed value if lossy */ - double compression; /**< compression factor (where applicable) */ - - /** - Should bytes be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_bytes; - - /** - Should nibbles be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_nibbles; - - /** - Should bits be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_bits; - - /** - If set to true, the format should reverse its default endianness. - */ - sox_bool opposite_endian; -} sox_encodinginfo_t; - -/** -Client API: -Looping parameters (out-of-band data). -*/ -typedef struct sox_loopinfo_t { - sox_uint64_t start; /**< first sample */ - sox_uint64_t length; /**< length */ - unsigned count; /**< number of repeats, 0=forever */ - unsigned char type; /**< 0=no, 1=forward, 2=forward/back (see sox_loop_* for valid values). */ -} sox_loopinfo_t; - -/** -Client API: -Instrument information. -*/ -typedef struct sox_instrinfo_t{ - signed char MIDInote; /**< for unity pitch playback */ - signed char MIDIlow; /**< MIDI pitch-bend low range */ - signed char MIDIhi; /**< MIDI pitch-bend high range */ - unsigned char loopmode; /**< 0=no, 1=forward, 2=forward/back (see sox_loop_* values) */ - unsigned nloops; /**< number of active loops (max SOX_MAX_NLOOPS). */ -} sox_instrinfo_t; - -/** -Client API: -File buffer info. Holds info so that data can be read in blocks. -*/ -typedef struct sox_fileinfo_t { - char *buf; /**< Pointer to data buffer */ - size_t size; /**< Size of buffer in bytes */ - size_t count; /**< Count read into buffer */ - size_t pos; /**< Position in buffer */ -} sox_fileinfo_t; - -/** -Client API: -Handler structure defined by each format. -*/ -struct sox_format_handler_t { - unsigned sox_lib_version_code; /**< Checked on load; must be 1st in struct*/ - char const * description; /**< short description of format */ - char const * const * names; /**< null-terminated array of filename extensions that are handled by this format */ - unsigned int flags; /**< File flags (SOX_FILE_* values). */ - sox_format_handler_startread startread; /**< called to initialize reader (decoder) */ - sox_format_handler_read read; /**< called to read (decode) a block of samples */ - sox_format_handler_stopread stopread; /**< called to close reader (decoder); may be null if no closing necessary */ - sox_format_handler_startwrite startwrite; /**< called to initialize writer (encoder) */ - sox_format_handler_write write; /**< called to write (encode) a block of samples */ - sox_format_handler_stopwrite stopwrite; /**< called to close writer (decoder); may be null if no closing necessary */ - sox_format_handler_seek seek; /**< called to reposition reader; may be null if not supported */ - - /** - Array of values indicating the encodings and precisions supported for - writing (encoding). Precisions specified with default precision first. - Encoding, precision, precision, ..., 0, repeat. End with one more 0. - Example: - unsigned const * formats = { - SOX_ENCODING_SIGN2, 16, 24, 0, // Support SIGN2 at 16 and 24 bits, default to 16 bits. - SOX_ENCODING_UNSIGNED, 8, 0, // Support UNSIGNED at 8 bits, default to 8 bits. - 0 // No more supported encodings. - }; - */ - unsigned const * write_formats; - - /** - Array of sample rates (samples per second) supported for writing (encoding). - NULL if all (or almost all) rates are supported. End with 0. - */ - sox_rate_t const * write_rates; - - /** - SoX will automatically allocate a buffer in which the handler can store data. - Specify the size of the buffer needed here. Usually this will be sizeof(your_struct). - The buffer will be allocated and zeroed before the call to startread/startwrite. - The buffer will be freed after the call to stopread/stopwrite. - The buffer will be provided via format.priv in each call to the handler. - */ - size_t priv_size; -}; - -/** -Client API: -Comments, instrument info, loop info (out-of-band data). -*/ -typedef struct sox_oob_t{ - /* Decoded: */ - sox_comments_t comments; /**< Comment strings in id=value format. */ - sox_instrinfo_t instr; /**< Instrument specification */ - sox_loopinfo_t loops[SOX_MAX_NLOOPS]; /**< Looping specification */ - - /* TBD: Non-decoded chunks, etc: */ -} sox_oob_t; - -/** -Client API: -Data passed to/from the format handler -*/ -struct sox_format_t { - char * filename; /**< File name */ - - /** - Signal specifications for reader (decoder) or writer (encoder): - sample rate, number of channels, precision, length, headroom multiplier. - Any info specified by the user is here on entry to startread or - startwrite. Info will be SOX_UNSPEC if the user provided no info. - At exit from startread, should be completely filled in, using - either data from the file's headers (if available) or whatever - the format is guessing/assuming (if header data is not available). - At exit from startwrite, should be completely filled in, using - either the data that was specified, or values chosen by the format - based on the format's defaults or capabilities. - */ - sox_signalinfo_t signal; - - /** - Encoding specifications for reader (decoder) or writer (encoder): - encoding (sample format), bits per sample, compression rate, endianness. - Should be filled in by startread. Values specified should be used - by startwrite when it is configuring the encoding parameters. - */ - sox_encodinginfo_t encoding; - - char * filetype; /**< Type of file, as determined by header inspection or libmagic. */ - sox_oob_t oob; /**< comments, instrument info, loop info (out-of-band data) */ - sox_bool seekable; /**< Can seek on this file */ - char mode; /**< Read or write mode ('r' or 'w') */ - sox_uint64_t olength; /**< Samples * chans written to file */ - sox_uint64_t clips; /**< Incremented if clipping occurs */ - int sox_errno; /**< Failure error code */ - char sox_errstr[256]; /**< Failure error text */ - void * fp; /**< File stream pointer */ - lsx_io_type io_type; /**< Stores whether this is a file, pipe or URL */ - sox_uint64_t tell_off; /**< Current offset within file */ - sox_uint64_t data_start; /**< Offset at which headers end and sound data begins (set by lsx_check_read_params) */ - sox_format_handler_t handler; /**< Format handler for this file */ - void * priv; /**< Format handler's private data area */ -}; - -/** -Client API: -Information about a loaded format handler, including the format name and a -function pointer that can be invoked to get additional information about the -format. -*/ -typedef struct sox_format_tab_t { - char *name; /**< Name of format handler */ - sox_format_fn_t fn; /**< Function to call to get format handler's information */ -} sox_format_tab_t; - -/** -Client API: -Global parameters for effects. -*/ -typedef struct sox_effects_globals_t { - sox_plot_t plot; /**< To help the user choose effect & options */ - sox_globals_t * global_info; /**< Pointer to associated SoX globals */ -} sox_effects_globals_t; - -/** -Client API: -Effect handler information. -*/ -struct sox_effect_handler_t { - char const * name; /**< Effect name */ - char const * usage; /**< Short explanation of parameters accepted by effect */ - unsigned int flags; /**< Combination of SOX_EFF_* flags */ - sox_effect_handler_getopts getopts; /**< Called to parse command-line arguments (called once per effect). */ - sox_effect_handler_start start; /**< Called to initialize effect (called once per flow). */ - sox_effect_handler_flow flow; /**< Called to process samples. */ - sox_effect_handler_drain drain; /**< Called to finish getting output after input is complete. */ - sox_effect_handler_stop stop; /**< Called to shut down effect (called once per flow). */ - sox_effect_handler_kill kill; /**< Called to shut down effect (called once per effect). */ - size_t priv_size; /**< Size of private data SoX should pre-allocate for effect */ -}; - -/** -Client API: -Effect information. -*/ -struct sox_effect_t { - sox_effects_globals_t * global_info; /**< global effect parameters */ - sox_signalinfo_t in_signal; /**< Information about the incoming data stream */ - sox_signalinfo_t out_signal; /**< Information about the outgoing data stream */ - sox_encodinginfo_t const * in_encoding; /**< Information about the incoming data encoding */ - sox_encodinginfo_t const * out_encoding; /**< Information about the outgoing data encoding */ - sox_effect_handler_t handler; /**< The handler for this effect */ - sox_sample_t * obuf; /**< output buffer */ - size_t obeg; /**< output buffer: start of valid data section */ - size_t oend; /**< output buffer: one past valid data section (oend-obeg is length of current content) */ - size_t imin; /**< minimum input buffer content required for calling this effect's flow function; set via lsx_effect_set_imin() */ - sox_uint64_t clips; /**< increment if clipping occurs */ - size_t flows; /**< 1 if MCHAN, number of chans otherwise */ - size_t flow; /**< flow number */ - void * priv; /**< Effect's private data area (each flow has a separate copy) */ -}; - -/** -Client API: -Chain of effects to be applied to a stream. -*/ -typedef struct sox_effects_chain_t { - sox_effect_t **effects; /**< Table of effects to be applied to a stream */ - unsigned table_size; /**< Number of entries in effects table */ - unsigned length; /**< Number of effects to be applied */ - sox_sample_t **ibufc; /**< Channel interleave buffer */ - sox_sample_t **obufc; /**< Channel interleave buffer */ - sox_effects_globals_t global_info; /**< Copy of global effects settings */ - sox_encodinginfo_t const * in_enc; /**< Input encoding */ - sox_encodinginfo_t const * out_enc; /**< Output encoding */ -} sox_effects_chain_t; - -/***************************************************************************** -Functions: -*****************************************************************************/ - -/** -Client API: -Returns version number string of libSoX, for example, "14.4.0". -@returns The version number string of libSoX, for example, "14.4.0". -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -sox_version(void); - -/** -Client API: -Returns information about this build of libsox. -@returns Pointer to a version information structure. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_version_info_t const * -LSX_API -sox_version_info(void); - -/** -Client API: -Returns a pointer to the structure with libSoX's global settings. -@returns a pointer to the structure with libSoX's global settings. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_globals_t * -LSX_API -sox_get_globals(void); - -/** -Client API: -Deprecated macro that returns the structure with libSoX's global settings -as an lvalue. -*/ -#define sox_globals (*sox_get_globals()) - -/** -Client API: -Returns a pointer to the list of available encodings. -End of list indicated by name == NULL. -@returns pointer to the list of available encodings. -*/ -LSX_RETURN_ARRAY LSX_RETURN_PURE -sox_encodings_info_t const * -LSX_API -sox_get_encodings_info(void); - -/** -Client API: -Deprecated macro that returns the list of available encodings. -End of list indicated by name == NULL. -*/ -#define sox_encodings_info (sox_get_encodings_info()) - -/** -Client API: -Fills in an encodinginfo with default values. -*/ -void -LSX_API -sox_init_encodinginfo( - LSX_PARAM_OUT sox_encodinginfo_t * e /**< Pointer to uninitialized encoding info structure to be initialized. */ - ); - -/** -Client API: -Given an encoding (for example, SIGN2) and the encoded bits_per_sample (for -example, 16), returns the number of useful bits per sample in the decoded data -(for example, 16), or returns 0 to indicate that the value returned by the -format handler should be used instead of a pre-determined precision. -@returns the number of useful bits per sample in the decoded data (for example -16), or returns 0 to indicate that the value returned by the format handler -should be used instead of a pre-determined precision. -*/ -LSX_RETURN_PURE -unsigned -LSX_API -sox_precision( - sox_encoding_t encoding, /**< Encoding for which to lookup precision information. */ - unsigned bits_per_sample /**< The number of encoded bits per sample. */ - ); - -/** -Client API: -Returns the number of items in the metadata block. -@returns the number of items in the metadata block. -*/ -size_t -LSX_API -sox_num_comments( - LSX_PARAM_IN_OPT sox_comments_t comments /**< Metadata block. */ - ); - -/** -Client API: -Adds an "id=value" item to the metadata block. -*/ -void -LSX_API -sox_append_comment( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NOTNULL sox_comments_t * comments, /**< Metadata block. */ - LSX_PARAM_IN_Z char const * item /**< Item to be added in "id=value" format. */ - ); - -/** -Client API: -Adds a newline-delimited list of "id=value" items to the metadata block. -*/ -void -LSX_API -sox_append_comments( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NOTNULL sox_comments_t * comments, /**< Metadata block. */ - LSX_PARAM_IN_Z char const * items /**< Newline-separated list of items to be added, for example "id1=value1\\nid2=value2". */ - ); - -/** -Client API: -Duplicates the metadata block. -@returns the copied metadata block. -*/ -LSX_RETURN_OPT -sox_comments_t -LSX_API -sox_copy_comments( - LSX_PARAM_IN_OPT sox_comments_t comments /**< Metadata block to copy. */ - ); - -/** -Client API: -Frees the metadata block. -*/ -void -LSX_API -sox_delete_comments( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NULL sox_comments_t * comments /**< Metadata block. */ - ); - -/** -Client API: -If "id=value" is found, return value, else return null. -@returns value, or null if value not found. -*/ -LSX_RETURN_OPT -char const * -LSX_API -sox_find_comment( - LSX_PARAM_IN_OPT sox_comments_t comments, /**< Metadata block in which to search. */ - LSX_PARAM_IN_Z char const * id /**< Id for which to search */ - ); - -/** -Client API: -Find and load format handler plugins. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_format_init(void); - -/** -Client API: -Unload format handler plugins. -*/ -void -LSX_API -sox_format_quit(void); - -/** -Client API: -Initialize effects library. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_init(void); - -/** -Client API: -Close effects library and unload format handler plugins. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_quit(void); - -/** -Client API: -Returns the table of format handler names and functions. -@returns the table of format handler names and functions. -*/ -LSX_RETURN_ARRAY LSX_RETURN_PURE -sox_format_tab_t const * -LSX_API -sox_get_format_fns(void); - -/** -Client API: -Deprecated macro that returns the table of format handler names and functions. -*/ -#define sox_format_fns (sox_get_format_fns()) - -/** -Client API: -Opens a decoding session for a file. Returned handle must be closed with sox_close(). -@returns The handle for the new session, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_read( - LSX_PARAM_IN_Z char const * path, /**< Path to file to be opened (required). */ - LSX_PARAM_IN_OPT sox_signalinfo_t const * signal, /**< Information already known about audio stream, or NULL if none. */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information already known about sample encoding, or NULL if none. */ - LSX_PARAM_IN_OPT_Z char const * filetype /**< Previously-determined file type, or NULL to auto-detect. */ - ); - -/** -Client API: -Opens a decoding session for a memory buffer. Returned handle must be closed with sox_close(). -@returns The handle for the new session, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_mem_read( - LSX_PARAM_IN_BYTECOUNT(buffer_size) void * buffer, /**< Pointer to audio data buffer (required). */ - size_t buffer_size,/**< Number of bytes to read from audio data buffer. */ - LSX_PARAM_IN_OPT sox_signalinfo_t const * signal, /**< Information already known about audio stream, or NULL if none. */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information already known about sample encoding, or NULL if none. */ - LSX_PARAM_IN_OPT_Z char const * filetype /**< Previously-determined file type, or NULL to auto-detect. */ - ); - -/** -Client API: -Returns true if the format handler for the specified file type supports the specified encoding. -@returns true if the format handler for the specified file type supports the specified encoding. -*/ -sox_bool -LSX_API -sox_format_supports_encoding( - LSX_PARAM_IN_OPT_Z char const * path, /**< Path to file to be examined (required if filetype is NULL). */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to use extension from path. */ - LSX_PARAM_IN sox_encodinginfo_t const * encoding /**< Encoding for which format handler should be queried. */ - ); - -/** -Client API: -Gets the format handler for a specified file type. -@returns The found format handler, or null if not found. -*/ -LSX_RETURN_OPT -sox_format_handler_t const * -LSX_API -sox_write_handler( - LSX_PARAM_IN_OPT_Z char const * path, /**< Path to file (required if filetype is NULL). */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Filetype for which handler is needed, or NULL to use extension from path. */ - LSX_PARAM_OUT_OPT char const * * filetype1 /**< Receives the filetype that was detected. Pass NULL if not needed. */ - ); - -/** -Client API: -Opens an encoding session for a file. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_write( - LSX_PARAM_IN_Z char const * path, /**< Path to file to be written (required). */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob, /**< Out-of-band data to add to file, or NULL if none. */ - LSX_PARAM_IN_OPT sox_bool (LSX_API * overwrite_permitted)(LSX_PARAM_IN_Z char const * filename) /**< Called if file exists to determine whether overwrite is ok. */ - ); - -/** -Client API: -Opens an encoding session for a memory buffer. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_mem_write( - LSX_PARAM_OUT_BYTECAP(buffer_size) void * buffer, /**< Pointer to audio data buffer that receives data (required). */ - LSX_PARAM_IN size_t buffer_size, /**< Maximum number of bytes to write to audio data buffer. */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob /**< Out-of-band data to add to file, or NULL if none. */ - ); - -/** -Client API: -Opens an encoding session for a memstream buffer. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_memstream_write( - LSX_PARAM_OUT char * * buffer_ptr, /**< Receives pointer to audio data buffer that receives data (required). */ - LSX_PARAM_OUT size_t * buffer_size_ptr, /**< Receives size of data written to audio data buffer (required). */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob /**< Out-of-band data to add to file, or NULL if none. */ - ); - -/** -Client API: -Reads samples from a decoding session into a sample buffer. -@returns Number of samples decoded, or 0 for EOF. -*/ -size_t -LSX_API -sox_read( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(len,return) sox_sample_t *buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Writes samples to an encoding session from a sample buffer. -@returns Number of samples encoded. -*/ -size_t -LSX_API -sox_write( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_IN_COUNT(len) sox_sample_t const * buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Closes an encoding or decoding session. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_close( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Sets the location at which next samples will be decoded. Returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_seek( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - sox_uint64_t offset, /**< Sample offset at which to position reader. */ - int whence /**< Set to SOX_SEEK_SET. */ - ); - -/** -Client API: -Finds a format handler by name. -@returns Format handler data, or null if not found. -*/ -LSX_RETURN_OPT -sox_format_handler_t const * -LSX_API -sox_find_format( - LSX_PARAM_IN_Z char const * name, /**< Name of format handler to find. */ - sox_bool ignore_devices /**< Set to true to ignore device names. */ - ); - -/** -Client API: -Returns global parameters for effects -@returns global parameters for effects. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_effects_globals_t * -LSX_API -sox_get_effects_globals(void); - -/** -Client API: -Deprecated macro that returns global parameters for effects. -*/ -#define sox_effects_globals (*sox_get_effects_globals()) - -/** -Client API: -Finds the effect handler with the given name. -@returns Effect pointer, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -sox_effect_handler_t const * -LSX_API -sox_find_effect( - LSX_PARAM_IN_Z char const * name /**< Name of effect to find. */ - ); - -/** -Client API: -Creates an effect using the given handler. -@returns The new effect, or null if not found. -*/ -LSX_RETURN_OPT -sox_effect_t * -LSX_API -sox_create_effect( - LSX_PARAM_IN sox_effect_handler_t const * eh /**< Handler to use for effect. */ - ); - -/** -Client API: -Applies the command-line options to the effect. -@returns the number of arguments consumed. -*/ -int -LSX_API -sox_effect_options( - LSX_PARAM_IN sox_effect_t *effp, /**< Effect pointer on which to set options. */ - int argc, /**< Number of arguments in argv. */ - LSX_PARAM_IN_COUNT(argc) char * const argv[] /**< Array of command-line options. */ - ); - -/** -Client API: -Returns an array containing the known effect handlers. -@returns An array containing the known effect handlers. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -sox_effect_fn_t const * -LSX_API -sox_get_effect_fns(void); - -/** -Client API: -Deprecated macro that returns an array containing the known effect handlers. -*/ -#define sox_effect_fns (sox_get_effect_fns()) - -/** -Client API: -Initializes an effects chain. Returned handle must be closed with sox_delete_effects_chain(). -@returns Handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_effects_chain_t * -LSX_API -sox_create_effects_chain( - LSX_PARAM_IN sox_encodinginfo_t const * in_enc, /**< Input encoding. */ - LSX_PARAM_IN sox_encodinginfo_t const * out_enc /**< Output encoding. */ - ); - -/** -Client API: -Closes an effects chain. -*/ -void -LSX_API -sox_delete_effects_chain( - LSX_PARAM_INOUT sox_effects_chain_t *ecp /**< Effects chain pointer. */ - ); - -/** -Client API: -Adds an effect to the effects chain, returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_add_effect( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to which effect should be added . */ - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect to be added. */ - LSX_PARAM_INOUT sox_signalinfo_t * in, /**< Input format. */ - LSX_PARAM_IN sox_signalinfo_t const * out /**< Output format. */ - ); - -/** -Client API: -Runs the effects chain, returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_flow_effects( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to run. */ - LSX_PARAM_IN_OPT sox_flow_effects_callback callback, /**< Callback for monitoring flow progress. */ - LSX_PARAM_IN_OPT void * client_data /**< Data to pass into callback. */ - ); - -/** -Client API: -Gets the number of clips that occurred while running an effects chain. -@returns the number of clips that occurred while running an effects chain. -*/ -sox_uint64_t -LSX_API -sox_effects_clips( - LSX_PARAM_IN sox_effects_chain_t * chain /**< Effects chain from which to read clip information. */ - ); - -/** -Client API: -Shuts down an effect (calls stop on each of its flows). -@returns the number of clips from all flows. -*/ -sox_uint64_t -LSX_API -sox_stop_effect( - LSX_PARAM_INOUT_COUNT(effp->flows) sox_effect_t * effp /**< Effect to stop. */ - ); - -/** -Client API: -Adds an already-initialized effect to the end of the chain. -*/ -void -LSX_API -sox_push_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to which effect should be added. */ - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect to be added. */ - ); - -/** -Client API: -Removes and returns an effect from the end of the chain. -@returns the removed effect, or null if no effects. -*/ -LSX_RETURN_OPT -sox_effect_t * -LSX_API -sox_pop_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to remove an effect. */ - ); - -/** -Client API: -Shut down and delete an effect. -*/ -void -LSX_API -sox_delete_effect( - LSX_PARAM_INOUT_COUNT(effp->flows) sox_effect_t *effp /**< Effect to be deleted. */ - ); - -/** -Client API: -Shut down and delete the last effect in the chain. -*/ -void -LSX_API -sox_delete_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to remove the last effect. */ - ); - -/** -Client API: -Shut down and delete all effects in the chain. -*/ -void -LSX_API -sox_delete_effects( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to delete effects. */ - ); - -/** -Client API: -Gets the sample offset of the start of the trim, useful for efficiently -skipping the part that will be trimmed anyway (get trim start, seek, then -clear trim start). -@returns the sample offset of the start of the trim. -*/ -sox_uint64_t -LSX_API -sox_trim_get_start( - LSX_PARAM_IN sox_effect_t * effp /**< Trim effect. */ - ); - -/** -Client API: -Clears the start of the trim to 0. -*/ -void -LSX_API -sox_trim_clear_start( - LSX_PARAM_INOUT sox_effect_t * effp /**< Trim effect. */ - ); - -/** -Client API: -Returns true if the specified file is a known playlist file type. -@returns true if the specified file is a known playlist file type. -*/ -sox_bool -LSX_API -sox_is_playlist( - LSX_PARAM_IN_Z char const * filename /**< Name of file to examine. */ - ); - -/** -Client API: -Parses the specified playlist file. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_parse_playlist( - LSX_PARAM_IN sox_playlist_callback_t callback, /**< Callback to call for each item in the playlist. */ - void * p, /**< Data to pass to callback. */ - LSX_PARAM_IN char const * const listname /**< Filename of playlist file. */ - ); - -/** -Client API: -Converts a SoX error code into an error string. -@returns error string corresponding to the specified error code, -or a generic message if the error code is not recognized. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -sox_strerror( - int sox_errno /**< Error code to look up. */ - ); - -/** -Client API: -Gets the basename of the specified file; for example, the basename of -"/a/b/c.d" would be "c". -@returns the number of characters written to base_buffer, excluding the null, -or 0 on failure. -*/ -size_t -LSX_API -sox_basename( - LSX_PARAM_OUT_Z_CAP_POST_COUNT(base_buffer_len,return) char * base_buffer, /**< Buffer into which basename should be written. */ - size_t base_buffer_len, /**< Size of base_buffer, in bytes. */ - LSX_PARAM_IN_Z char const * filename /**< Filename from which to extract basename. */ - ); - -/***************************************************************************** -Internal API: -WARNING - The items in this section are subject to instability. They only -exist in the public header because sox (the application) currently uses them. -These may be changed or removed in future versions of libSoX. -*****************************************************************************/ - -/** -Plugins API: -Print a fatal error in libSoX. -*/ -void -LSX_API -lsx_fail_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print a warning in libSoX. -*/ -void -LSX_API -lsx_warn_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print an informational message in libSoX. -*/ -void -LSX_API -lsx_report_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print a debug message in libSoX. -*/ -void -LSX_API -lsx_debug_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Report a fatal error in libSoX; printf-style arguments must follow. -*/ -#define lsx_fail sox_get_globals()->subsystem=__FILE__,lsx_fail_impl - -/** -Plugins API: -Report a warning in libSoX; printf-style arguments must follow. -*/ -#define lsx_warn sox_get_globals()->subsystem=__FILE__,lsx_warn_impl - -/** -Plugins API: -Report an informational message in libSoX; printf-style arguments must follow. -*/ -#define lsx_report sox_get_globals()->subsystem=__FILE__,lsx_report_impl - -/** -Plugins API: -Report a debug message in libSoX; printf-style arguments must follow. -*/ -#define lsx_debug sox_get_globals()->subsystem=__FILE__,lsx_debug_impl - -/** -Plugins API: -String name and integer values for enumerated types (type metadata), for use -with LSX_ENUM_ITEM, lsx_find_enum_text, and lsx_find_enum_value. -*/ -typedef struct lsx_enum_item { - char const *text; /**< String name of enumeration. */ - unsigned value; /**< Integer value of enumeration. */ -} lsx_enum_item; - -/** -Plugins API: -Declares a static instance of an lsx_enum_item structure in format -{ "item", prefixitem }, for use in declaring lsx_enum_item[] arrays. -@param prefix The prefix to prepend to the item in the enumeration symbolic name. -@param item The user-visible text name of the item (must also be a valid C symbol name). -*/ -#define LSX_ENUM_ITEM(prefix, item) {#item, prefix##item}, - -/** -Plugins API: -Flags for use with lsx_find_enum_item. -*/ -enum -{ - lsx_find_enum_item_none = 0, /**< Default parameters (case-insensitive). */ - lsx_find_enum_item_case_sensitive = 1 /**< Enable case-sensitive search. */ -}; - -/** -Plugins API: -Looks up an enumeration by name in an array of lsx_enum_items. -@returns the corresponding item, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -lsx_enum_item const * -LSX_API -lsx_find_enum_text( - LSX_PARAM_IN_Z char const * text, /**< Name of enumeration to find. */ - LSX_PARAM_IN lsx_enum_item const * lsx_enum_items, /**< Array of items to search, with text == NULL for last item. */ - int flags /**< Search flags: 0 (case-insensitive) or lsx_find_enum_item_case_sensitive (case-sensitive). */ - ); - -/** -Plugins API: -Looks up an enumeration by value in an array of lsx_enum_items. -@returns the corresponding item, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -lsx_enum_item const * -LSX_API -lsx_find_enum_value( - unsigned value, /**< Enumeration value to find. */ - LSX_PARAM_IN lsx_enum_item const * lsx_enum_items /**< Array of items to search, with text == NULL for last item. */ - ); - -/** -Plugins API: -Looks up a command-line argument in a set of enumeration names, showing an -error message if the argument is not found in the set of names. -@returns The enumeration value corresponding to the matching enumeration, or -INT_MAX if the argument does not match any enumeration name. -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_enum_option( - int c, /**< Option character to which arg is associated, for example with -a, c would be 'a'. */ - LSX_PARAM_IN_Z char const * arg, /**< Argument to find in enumeration list. */ - LSX_PARAM_IN lsx_enum_item const * items /**< Array of items to search, with text == NULL for last item. */ - ); - -/** -Plugins API: -Determines whether the specified string ends with the specified suffix (case-sensitive). -@returns true if the specified string ends with the specified suffix. -*/ -LSX_RETURN_PURE -sox_bool -LSX_API -lsx_strends( - LSX_PARAM_IN_Z char const * str, /**< String to search. */ - LSX_PARAM_IN_Z char const * end /**< Suffix to search for. */ - ); - -/** -Plugins API: -Finds the file extension for a filename. -@returns the file extension, not including the '.', or null if filename does -not have an extension. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -lsx_find_file_extension( - LSX_PARAM_IN_Z char const * pathname /**< Filename to search for extension. */ - ); - -/** -Plugins API: -Formats the specified number with up to three significant figures and adds a -metric suffix in place of the exponent, such as 1.23G. -@returns A static buffer with the formatted number, valid until the next time -this function is called (note: not thread safe). -*/ -LSX_RETURN_VALID_Z -char const * -LSX_API -lsx_sigfigs3( - double number /**< Number to be formatted. */ - ); - -/** -Plugins API: -Formats the specified number as a percentage, showing up to three significant -figures. -@returns A static buffer with the formatted number, valid until the next time -this function is called (note: not thread safe). -*/ -LSX_RETURN_VALID_Z -char const * -LSX_API -lsx_sigfigs3p( - double percentage /**< Number to be formatted. */ - ); - -/** -Plugins API: -Allocates, deallocates, or resizes; like C's realloc, except that this version -terminates the running application if unable to allocate the requested memory. -@returns New buffer, or null if buffer was freed. -*/ -LSX_RETURN_OPT -void * -LSX_API -lsx_realloc( - LSX_PARAM_IN_OPT void *ptr, /**< Pointer to be freed or resized, or null if allocating a new buffer. */ - size_t newsize /**< New size for buffer, or 0 to free the buffer. */ - ); - -/** -Plugins API: -Like strcmp, except that the characters are compared without regard to case. -@returns 0 (s1 == s2), negative (s1 < s2), or positive (s1 > s2). -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_strcasecmp( - LSX_PARAM_IN_Z char const * s1, /**< First string. */ - LSX_PARAM_IN_Z char const * s2 /**< Second string. */ - ); - - -/** -Plugins API: -Like strncmp, except that the characters are compared without regard to case. -@returns 0 (s1 == s2), negative (s1 < s2), or positive (s1 > s2). -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_strncasecmp( - LSX_PARAM_IN_Z char const * s1, /**< First string. */ - LSX_PARAM_IN_Z char const * s2, /**< Second string. */ - size_t n /**< Maximum number of characters to examine. */ - ); - -/** -Plugins API: -Is option argument unsupported, required, or optional. -*/ -typedef enum lsx_option_arg_t { - lsx_option_arg_none, /**< Option does not have an argument. */ - lsx_option_arg_required, /**< Option requires an argument. */ - lsx_option_arg_optional /**< Option can optionally be followed by an argument. */ -} lsx_option_arg_t; - -/** -Plugins API: -lsx_getopt_init options. -*/ -typedef enum lsx_getopt_flags_t { - lsx_getopt_flag_none = 0, /**< no flags (no output, not long-only) */ - lsx_getopt_flag_opterr = 1, /**< if set, invalid options trigger lsx_warn output */ - lsx_getopt_flag_longonly = 2 /**< if set, recognize -option as a long option */ -} lsx_getopt_flags_t; - -/** -Plugins API: -lsx_getopt long option descriptor. -*/ -typedef struct lsx_option_t { - char const * name; /**< Name of the long option. */ - lsx_option_arg_t has_arg; /**< Whether the long option supports an argument and, if so, whether the argument is required or optional. */ - int * flag; /**< Flag to set if argument is present. */ - int val; /**< Value to put in flag if argument is present. */ -} lsx_option_t; - -/** -Plugins API: -lsx_getopt session information (initialization data and state). -*/ -typedef struct lsx_getopt_t { - int argc; /**< IN argc: Number of arguments in argv */ - char * const * argv; /**< IN argv: Array of arguments */ - char const * shortopts;/**< IN shortopts: Short option characters */ - lsx_option_t const * longopts; /**< IN longopts: Array of long option descriptors */ - lsx_getopt_flags_t flags; /**< IN flags: Flags for longonly and opterr */ - char const * curpos; /**< INOUT curpos: Maintains state between calls to lsx_getopt */ - int ind; /**< INOUT optind: Maintains the index of next element to be processed */ - int opt; /**< OUT optopt: Receives the option character that caused error */ - char const * arg; /**< OUT optarg: Receives the value of the option's argument */ - int lngind; /**< OUT lngind: Receives the index of the matched long option or -1 if not a long option */ -} lsx_getopt_t; - -/** -Plugins API: -Initializes an lsx_getopt_t structure for use with lsx_getopt. -*/ -void -LSX_API -lsx_getopt_init( - LSX_PARAM_IN int argc, /**< Number of arguments in argv */ - LSX_PARAM_IN_COUNT(argc) char * const * argv, /**< Array of arguments */ - LSX_PARAM_IN_Z char const * shortopts, /**< Short options, for example ":abc:def::ghi" (+/- not supported) */ - LSX_PARAM_IN_OPT lsx_option_t const * longopts, /**< Array of long option descriptors */ - LSX_PARAM_IN lsx_getopt_flags_t flags, /**< Flags for longonly and opterr */ - LSX_PARAM_IN int first, /**< First argv to check (usually 1) */ - LSX_PARAM_OUT lsx_getopt_t * state /**< State object to be initialized */ - ); - -/** -Plugins API: -Gets the next option. Options are parameters that start with "-" or "--". -If no more options, returns -1. If unrecognized short option, returns '?'. -If a recognized short option is missing a required argument, -return (shortopts[0]==':' ? ':' : '?'). If successfully recognized short -option, return the recognized character. If successfully recognized long -option, returns (option.flag ? 0 : option.val). -Note: lsx_getopt does not permute the non-option arguments. -@returns option character (short), val or 0 (long), or -1 (no more). -*/ -int -LSX_API -lsx_getopt( - LSX_PARAM_INOUT lsx_getopt_t * state /**< The getopt state pointer. */ - ); - -/* WARNING END */ - -#if defined(__cplusplus) -} -#endif - -#endif /* SOX_H */ diff --git a/freedv/branches/1.2/freedv-dev/src/sox/sox_i.h b/freedv/branches/1.2/freedv-dev/src/sox/sox_i.h deleted file mode 100644 index 9d533263..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/sox_i.h +++ /dev/null @@ -1,417 +0,0 @@ -/* libSoX Internal header - * - * This file is meant for libSoX internal use only - * - * Copyright 2001-2008 Chris Bagwell and SoX Contributors - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Chris Bagwell And SoX Contributors are not responsible for - * the consequences of using this software. - */ - -#ifndef SOX_I_H -#define SOX_I_H - -#include "soxomp.h" /* Note: soxomp.h includes soxconfig.h */ -#include "sox.h" - -#define __FREEDV__ - -#if defined HAVE_FMEMOPEN -#define _GNU_SOURCE -#endif - -#include -#include -#include - -#include "util.h" - -#if defined(LSX_EFF_ALIAS) -#undef lsx_debug -#undef lsx_fail -#undef lsx_report -#undef lsx_warn -#define lsx_debug sox_globals.subsystem=effp->handler.name,lsx_debug_impl -#define lsx_fail sox_globals.subsystem=effp->handler.name,lsx_fail_impl -#define lsx_report sox_globals.subsystem=effp->handler.name,lsx_report_impl -#define lsx_warn sox_globals.subsystem=effp->handler.name,lsx_warn_impl -#endif - -#define RANQD1 ranqd1(sox_globals.ranqd1) -#define DRANQD1 dranqd1(sox_globals.ranqd1) - -typedef enum {SOX_SHORT, SOX_INT, SOX_FLOAT, SOX_DOUBLE} sox_data_t; -typedef enum {SOX_WAVE_SINE, SOX_WAVE_TRIANGLE} lsx_wave_t; -lsx_enum_item const * lsx_get_wave_enum(void); - -/* Define fseeko and ftello for platforms lacking them */ -#ifndef HAVE_FSEEKO -#define fseeko fseek -#define ftello ftell -#endif - -#ifdef _FILE_OFFSET_BITS -assert_static(sizeof(off_t) == _FILE_OFFSET_BITS >> 3, OFF_T_BUILD_PROBLEM); -#endif - -FILE * lsx_tmpfile(void); - -void lsx_debug_more_impl(char const * fmt, ...) LSX_PRINTF12; -void lsx_debug_most_impl(char const * fmt, ...) LSX_PRINTF12; - -#define lsx_debug_more sox_get_globals()->subsystem=__FILE__,lsx_debug_more_impl -#define lsx_debug_most sox_get_globals()->subsystem=__FILE__,lsx_debug_most_impl - -/* Digitise one cycle of a wave and store it as - * a table of samples of a specified data-type. - */ -void lsx_generate_wave_table( - lsx_wave_t wave_type, - sox_data_t data_type, - void * table, /* Really of type indicated by data_type. */ - size_t table_size, /* Number of points on the x-axis. */ - double min, /* Minimum value on the y-axis. (e.g. -1) */ - double max, /* Maximum value on the y-axis. (e.g. +1) */ - double phase); /* Phase at 1st point; 0..2pi. (e.g. pi/2 for cosine) */ -char const * lsx_parsesamples(sox_rate_t rate, const char *str, uint64_t *samples, int def); -int lsx_parse_note(char const * text, char * * end_ptr); -double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key); -#define lsx_parse_frequency(a, b) lsx_parse_frequency_k(a, b, INT_MAX) -FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename); - -void lsx_prepare_spline3(double const * x, double const * y, int n, - double start_1d, double end_1d, double * y_2d); -double lsx_spline3(double const * x, double const * y, double const * y_2d, - int n, double x1); - -double lsx_bessel_I_0(double x); -int lsx_set_dft_length(int num_taps); -void init_fft_cache(void); -void clear_fft_cache(void); -void lsx_safe_rdft(int len, int type, double * d); -void lsx_safe_cdft(int len, int type, double * d); -void lsx_power_spectrum(int n, double const * in, double * out); -void lsx_power_spectrum_f(int n, float const * in, float * out); -void lsx_apply_hann_f(float h[], const int num_points); -void lsx_apply_hann(double h[], const int num_points); -void lsx_apply_hamming(double h[], const int num_points); -void lsx_apply_bartlett(double h[], const int num_points); -void lsx_apply_blackman(double h[], const int num_points, double alpha); -void lsx_apply_blackman_nutall(double h[], const int num_points); -double lsx_kaiser_beta(double att); -void lsx_apply_kaiser(double h[], const int num_points, double beta); -double * lsx_make_lpf(int num_taps, double Fc, double beta, double scale, sox_bool dc_norm); -int lsx_lpf_num_taps(double att, double tr_bw, int k); -double * lsx_design_lpf( - double Fp, /* End of pass-band; ~= 0.01dB point */ - double Fc, /* Start of stop-band */ - double Fn, /* Nyquist freq; e.g. 0.5, 1, PI */ - sox_bool allow_aliasing, - double att, /* Stop-band attenuation in dB */ - int * num_taps, /* (Single phase.) 0: value will be estimated */ - int k); /* Number of phases; 0 for single-phase */ -void lsx_fir_to_phase(double * * h, int * len, - int * post_len, double phase0); -#define LSX_TO_6dB .5869 -#define LSX_TO_3dB ((2/3.) * (.5 + LSX_TO_6dB)) -#define LSX_MAX_TBW0 36. -#define LSX_MAX_TBW0A (LSX_MAX_TBW0 / (1 + LSX_TO_3dB)) -#define LSX_MAX_TBW3 floor(LSX_MAX_TBW0 * LSX_TO_3dB) -#define LSX_MAX_TBW3A floor(LSX_MAX_TBW0A * LSX_TO_3dB) -void lsx_plot_fir(double * h, int num_points, sox_rate_t rate, sox_plot_t type, char const * title, double y1, double y2); - -#ifdef HAVE_BYTESWAP_H -#include -#define lsx_swapw(x) bswap_16(x) -#define lsx_swapdw(x) bswap_32(x) -#elif defined(_MSC_VER) -#define lsx_swapw(x) _byteswap_ushort(x) -#define lsx_swapdw(x) _byteswap_ulong(x) -#else -#define lsx_swapw(uw) (((uw >> 8) | (uw << 8)) & 0xffff) -#define lsx_swapdw(udw) ((udw >> 24) | ((udw >> 8) & 0xff00) | ((udw << 8) & 0xff0000) | (udw << 24)) -#endif - - - -/*------------------------ Implemented in libsoxio.c -------------------------*/ - -/* Read and write basic data types from "ft" stream. */ -size_t lsx_readbuf(sox_format_t * ft, void *buf, size_t len); -int lsx_skipbytes(sox_format_t * ft, size_t n); -int lsx_padbytes(sox_format_t * ft, size_t n); -size_t lsx_writebuf(sox_format_t * ft, void const *buf, size_t len); -int lsx_reads(sox_format_t * ft, char *c, size_t len); -int lsx_writes(sox_format_t * ft, char const * c); -void lsx_set_signal_defaults(sox_format_t * ft); -#define lsx_writechars(ft, chars, len) (lsx_writebuf(ft, chars, len) == len? SOX_SUCCESS : SOX_EOF) - -size_t lsx_read_3_buf(sox_format_t * ft, sox_uint24_t *buf, size_t len); -size_t lsx_read_b_buf(sox_format_t * ft, uint8_t *buf, size_t len); -size_t lsx_read_df_buf(sox_format_t * ft, double *buf, size_t len); -size_t lsx_read_dw_buf(sox_format_t * ft, uint32_t *buf, size_t len); -size_t lsx_read_qw_buf(sox_format_t * ft, uint64_t *buf, size_t len); -size_t lsx_read_f_buf(sox_format_t * ft, float *buf, size_t len); -size_t lsx_read_w_buf(sox_format_t * ft, uint16_t *buf, size_t len); - -size_t lsx_write_3_buf(sox_format_t * ft, sox_uint24_t *buf, size_t len); -size_t lsx_write_b_buf(sox_format_t * ft, uint8_t *buf, size_t len); -size_t lsx_write_df_buf(sox_format_t * ft, double *buf, size_t len); -size_t lsx_write_dw_buf(sox_format_t * ft, uint32_t *buf, size_t len); -size_t lsx_write_qw_buf(sox_format_t * ft, uint64_t *buf, size_t len); -size_t lsx_write_f_buf(sox_format_t * ft, float *buf, size_t len); -size_t lsx_write_w_buf(sox_format_t * ft, uint16_t *buf, size_t len); - -int lsx_read3(sox_format_t * ft, sox_uint24_t * u3); -int lsx_readb(sox_format_t * ft, uint8_t * ub); -int lsx_readchars(sox_format_t * ft, char * chars, size_t len); -int lsx_readdf(sox_format_t * ft, double * d); -int lsx_readdw(sox_format_t * ft, uint32_t * udw); -int lsx_readqw(sox_format_t * ft, uint64_t * udw); -int lsx_readf(sox_format_t * ft, float * f); -int lsx_readw(sox_format_t * ft, uint16_t * uw); - -#if 1 /* FIXME: use defines */ -UNUSED static int lsx_readsb(sox_format_t * ft, int8_t * sb) -{return lsx_readb(ft, (uint8_t *)sb);} -UNUSED static int lsx_readsw(sox_format_t * ft, int16_t * sw) -{return lsx_readw(ft, (uint16_t *)sw);} -#else -#define lsx_readsb(ft, sb) lsx_readb(ft, (uint8_t *)sb) -#define lsx_readsw(ft, sw) lsx_readb(ft, (uint16_t *)sw) -#endif - -int lsx_write3(sox_format_t * ft, unsigned u3); -int lsx_writeb(sox_format_t * ft, unsigned ub); -int lsx_writedf(sox_format_t * ft, double d); -int lsx_writedw(sox_format_t * ft, unsigned udw); -int lsx_writeqw(sox_format_t * ft, uint64_t uqw); -int lsx_writef(sox_format_t * ft, double f); -int lsx_writew(sox_format_t * ft, unsigned uw); - -int lsx_writesb(sox_format_t * ft, signed); -int lsx_writesw(sox_format_t * ft, signed); - -int lsx_eof(sox_format_t * ft); -int lsx_error(sox_format_t * ft); -int lsx_flush(sox_format_t * ft); -int lsx_seeki(sox_format_t * ft, off_t offset, int whence); -int lsx_unreadb(sox_format_t * ft, unsigned ub); -uint64_t lsx_filelength(sox_format_t * ft); -off_t lsx_tell(sox_format_t * ft); -void lsx_clearerr(sox_format_t * ft); -void lsx_rewind(sox_format_t * ft); - -int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, off_t to_sample); - -void lsx_fail_errno(sox_format_t *, int, const char *, ...) -#ifdef __GNUC__ -__attribute__ ((format (printf, 3, 4))); -#else -; -#endif - -typedef struct sox_formats_globals { /* Global parameters (for formats) */ - sox_globals_t * global_info; -} sox_formats_globals; - - - -/*------------------------------ File Handlers -------------------------------*/ - -int lsx_check_read_params(sox_format_t * ft, unsigned channels, - sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, - uint64_t num_samples, sox_bool check_length); -#define LSX_FORMAT_HANDLER(name) \ -sox_format_handler_t const * lsx_##name##_format_fn(void); \ -sox_format_handler_t const * lsx_##name##_format_fn(void) -#define div_bits(size, bits) ((uint64_t)(size) * 8 / bits) - -/* Raw I/O */ -int lsx_rawstartread(sox_format_t * ft); -size_t lsx_rawread(sox_format_t * ft, sox_sample_t *buf, size_t nsamp); -int lsx_rawstopread(sox_format_t * ft); -int lsx_rawstartwrite(sox_format_t * ft); -size_t lsx_rawwrite(sox_format_t * ft, const sox_sample_t *buf, size_t nsamp); -int lsx_rawseek(sox_format_t * ft, uint64_t offset); -int lsx_rawstart(sox_format_t * ft, sox_bool default_rate, sox_bool default_channels, sox_bool default_length, sox_encoding_t encoding, unsigned bits_per_sample); -#define lsx_rawstartread(ft) lsx_rawstart(ft, sox_false, sox_false, sox_false, SOX_ENCODING_UNKNOWN, 0) -#define lsx_rawstartwrite lsx_rawstartread -#define lsx_rawstopread NULL -#define lsx_rawstopwrite NULL - -extern sox_format_handler_t const * lsx_sndfile_format_fn(void); - -char * lsx_cat_comments(sox_comments_t comments); - -/*--------------------------------- Effects ----------------------------------*/ - -int lsx_flow_copy(sox_effect_t * effp, const sox_sample_t * ibuf, - sox_sample_t * obuf, size_t * isamp, size_t * osamp); -int lsx_usage(sox_effect_t * effp); -char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n); -#define EFFECT(f) extern sox_effect_handler_t const * lsx_##f##_effect_fn(void); -#include "effects.h" -#undef EFFECT - -#define NUMERIC_PARAMETER(name, min, max) { \ - char * end_ptr; \ - double d; \ - if (argc == 0) break; \ - d = strtod(*argv, &end_ptr); \ - if (end_ptr != *argv) { \ - if (d < min || d > max || *end_ptr != '\0') {\ - lsx_fail("parameter `%s' must be between %g and %g", #name, (double)min, (double)max); \ - return lsx_usage(effp); \ - } \ - p->name = d; \ - --argc, ++argv; \ - } \ -} - -#define TEXTUAL_PARAMETER(name, enum_table) { \ - lsx_enum_item const * e; \ - if (argc == 0) break; \ - e = lsx_find_enum_text(*argv, enum_table, 0); \ - if (e != NULL) { \ - p->name = e->value; \ - --argc, ++argv; \ - } \ -} - -#define GETOPT_NUMERIC(state, ch, name, min, max) case ch:{ \ - char * end_ptr; \ - double d = strtod(state.arg, &end_ptr); \ - if (end_ptr == state.arg || d < min || d > max || *end_ptr != '\0') {\ - lsx_fail("parameter `%s' must be between %g and %g", #name, (double)min, (double)max); \ - return lsx_usage(effp); \ - } \ - p->name = d; \ - break; \ -} - -int lsx_effect_set_imin(sox_effect_t * effp, size_t imin); - -int lsx_effects_init(void); -int lsx_effects_quit(void); - -/*--------------------------------- Dynamic Library ----------------------------------*/ - -#if defined(HAVE_WIN32_LTDL_H) - #include "win32-ltdl.h" - #define HAVE_LIBLTDL 1 - typedef lt_dlhandle lsx_dlhandle; -#elif defined(HAVE_LIBLTDL) - #include - typedef lt_dlhandle lsx_dlhandle; -#else - struct lsx_dlhandle_tag; - typedef struct lsx_dlhandle_tag *lsx_dlhandle; -#endif - -typedef void (*lsx_dlptr)(void); - -typedef struct lsx_dlfunction_info -{ - const char* name; - lsx_dlptr static_func; - lsx_dlptr stub_func; -} lsx_dlfunction_info; - -int lsx_open_dllibrary( - int show_error_on_failure, - const char* library_description, - const char * const library_names[], - const lsx_dlfunction_info func_infos[], - lsx_dlptr selected_funcs[], - lsx_dlhandle* pdl); - -void lsx_close_dllibrary( - lsx_dlhandle dl); - -#define LSX_DLENTRIES_APPLY__(entries, f, x) entries(f, x) - -#define LSX_DLENTRY_TO_PTR__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - func_return (*func_ptr) func_args; - -#define LSX_DLENTRIES_TO_FUNCTIONS__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - func_return func_name func_args; - -/* LSX_DLENTRIES_TO_PTRS: Given an ENTRIES macro and the name of the dlhandle - variable, declares the corresponding function pointer variables and the - dlhandle variable. */ -#define LSX_DLENTRIES_TO_PTRS(entries, dlhandle) \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRY_TO_PTR__, 0) \ - lsx_dlhandle dlhandle - -/* LSX_DLENTRIES_TO_FUNCTIONS: Given an ENTRIES macro, declares the corresponding - functions. */ -#define LSX_DLENTRIES_TO_FUNCTIONS(entries) \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRIES_TO_FUNCTIONS__, 0) - -#define LSX_DLLIBRARY_OPEN1__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - { #func_name, (lsx_dlptr)(static_func), (lsx_dlptr)(stub_func) }, - -#define LSX_DLLIBRARY_OPEN2__(ptr_container, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - (ptr_container)->func_ptr = (func_return (*)func_args)lsx_dlfunction_open_library_funcs[lsx_dlfunction_open_library_index++]; - -/* LSX_DLLIBRARY_OPEN: Input an ENTRIES macro, the library's description, - a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }), - the name of the dlhandle variable, the name of the structure that contains - the function pointer and dlhandle variables, and the name of the variable in - which the result of the lsx_open_dllibrary call should be stored. This will - call lsx_open_dllibrary and copy the resulting function pointers into the - structure members. If the library cannot be opened, show a failure message. */ -#define LSX_DLLIBRARY_OPEN(ptr_container, dlhandle, entries, library_description, library_names, return_var) \ - LSX_DLLIBRARY_TRYOPEN(1, ptr_container, dlhandle, entries, library_description, library_names, return_var) - -/* LSX_DLLIBRARY_TRYOPEN: Input an ENTRIES macro, the library's description, - a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }), - the name of the dlhandle variable, the name of the structure that contains - the function pointer and dlhandle variables, and the name of the variable in - which the result of the lsx_open_dllibrary call should be stored. This will - call lsx_open_dllibrary and copy the resulting function pointers into the - structure members. If the library cannot be opened, show a report or a failure - message, depending on whether error_on_failure is non-zero. */ -#define LSX_DLLIBRARY_TRYOPEN(error_on_failure, ptr_container, dlhandle, entries, library_description, library_names, return_var) \ - do { \ - lsx_dlfunction_info lsx_dlfunction_open_library_infos[] = { \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN1__, 0) \ - {NULL,NULL,NULL} }; \ - int lsx_dlfunction_open_library_index = 0; \ - lsx_dlptr lsx_dlfunction_open_library_funcs[sizeof(lsx_dlfunction_open_library_infos)/sizeof(lsx_dlfunction_open_library_infos[0])]; \ - (return_var) = lsx_open_dllibrary((error_on_failure), (library_description), (library_names), lsx_dlfunction_open_library_infos, lsx_dlfunction_open_library_funcs, &(ptr_container)->dlhandle); \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN2__, ptr_container) \ - } while(0) - -#define LSX_DLLIBRARY_CLOSE(ptr_container, dlhandle) \ - lsx_close_dllibrary((ptr_container)->dlhandle) - - /* LSX_DLENTRY_STATIC: For use in creating an ENTRIES macro. func is - expected to be available at link time. If not present, link will fail. */ -#define LSX_DLENTRY_STATIC(f,x, ret, func, args) f(x, ret, func, args, func, NULL, func) - - /* LSX_DLENTRY_DYNAMIC: For use in creating an ENTRIES macro. func need - not be available at link time (and if present, the link time version will - not be used). func will be loaded via dlsym. If this function is not - found in the shared library, the shared library will not be used. */ -#define LSX_DLENTRY_DYNAMIC(f,x, ret, func, args) f(x, ret, func, args, NULL, NULL, func) - - /* LSX_DLENTRY_STUB: For use in creating an ENTRIES macro. func need not - be available at link time (and if present, the link time version will not - be used). If using DL_LAME, the func may be loaded via dlopen/dlsym, but - if not found, the shared library will still be used if all of the - non-stub functions are found. If the function is not found via dlsym (or - if we are not loading any shared libraries), the stub will be used. This - assumes that the name of the stub function is the name of the function + - "_stub". */ -#define LSX_DLENTRY_STUB(f,x, ret, func, args) f(x, ret, func, args, NULL, func##_stub, func) - - /* LSX_DLFUNC_IS_STUB: returns true if the named function is a do-nothing - stub. Assumes that the name of the stub function is the name of the - function + "_stub". */ -#define LSX_DLFUNC_IS_STUB(ptr_container, func) ((ptr_container)->func == func##_stub) - -#endif diff --git a/freedv/branches/1.2/freedv-dev/src/sox/soxomp.h b/freedv/branches/1.2/freedv-dev/src/sox/soxomp.h deleted file mode 100644 index 6fce07d9..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/soxomp.h +++ /dev/null @@ -1,38 +0,0 @@ -#include "soxconfig.h" - -#ifdef HAVE_OPENMP - #include -#else - -typedef int omp_lock_t; -typedef int omp_nest_lock_t; - -#define omp_set_num_threads(int) (void)0 -#define omp_get_num_threads() 1 -#define omp_get_max_threads() 1 -#define omp_get_thread_num() 0 -#define omp_get_num_procs() 1 -#define omp_in_parallel() 1 - -#define omp_set_dynamic(int) (void)0 -#define omp_get_dynamic() 0 - -#define omp_set_nested(int) (void)0 -#define omp_get_nested() 0 - -#define omp_init_lock(omp_lock_t) (void)0 -#define omp_destroy_lock(omp_lock_t) (void)0 -#define omp_set_lock(omp_lock_t) (void)0 -#define omp_unset_lock(omp_lock_t) (void)0 -#define omp_test_lock(omp_lock_t) 0 - -#define omp_init_nest_lock(omp_nest_lock_t) (void)0 -#define omp_destroy_nest_lock(omp_nest_lock_t) (void)0 -#define omp_set_nest_lock(omp_nest_lock_t) (void)0 -#define omp_unset_nest_lock(omp_nest_lock_t) (void)0 -#define omp_test_nest_lock(omp_nest_lock_t) 0 - -#define omp_get_wtime() 0 -#define omp_get_wtick() 0 - -#endif diff --git a/freedv/branches/1.2/freedv-dev/src/sox/util.h b/freedv/branches/1.2/freedv-dev/src/sox/util.h deleted file mode 100644 index 89bbe752..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/util.h +++ /dev/null @@ -1,231 +0,0 @@ -/* General purpose, i.e. non SoX specific, utility functions and macros. - * - * (c) 2006-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "soxconfig.h" - -#ifdef HAVE_SYS_TYPES_H -#include /* For off_t not found in stdio.h */ -#endif - -#ifdef HAVE_SYS_STAT_H -#include /* Needs to be included before we redefine off_t. */ -#endif - -#include "xmalloc.h" - -/*---------------------------- Portability stuff -----------------------------*/ - -#if defined(HAVE_INTTYPES_H) - #include -#elif defined(HAVE_STDINT_H) - #include -#else - typedef sox_int8_t int8_t; - typedef sox_uint8_t uint8_t; - typedef sox_int16_t int16_t; - typedef sox_uint16_t uint16_t; - typedef sox_int32_t int32_t; - typedef sox_uint32_t uint32_t; - typedef sox_int64_t int64_t; - typedef sox_uint64_t uint64_t; -#endif - -/* Define the format specifier to use for int64_t values. - * Example: printf("You may have already won $ %" PRId64 " !!!", n64); */ -#ifndef PRId64 /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %lld. */ -#define PRId64 "I64d" -#elif LONG_MAX==9223372036854775807 -#define PRId64 "ld" -#else -#define PRId64 "lld" -#endif -#endif /* PRId64 */ - -/* Define the format specifier to use for uint64_t values. */ -#ifndef PRIu64 /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %llu. */ -#define PRIu64 "I64u" -#elif ULONG_MAX==0xffffffffffffffff -#define PRIu64 "lu" -#else -#define PRIu64 "llu" -#endif -#endif /* PRIu64 */ - -/* Define the format specifier to use for size_t values. - * Example: printf("Sizeof(x) = %" PRIuPTR " bytes", sizeof(x)); */ -#ifndef PRIuPTR /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %zu. */ -#define PRIuPTR "Iu" -#else -#define PRIuPTR "zu" -#endif -#endif /* PRIuPTR */ - -#ifdef __GNUC__ -#define NORET __attribute__((noreturn)) -#define UNUSED __attribute__ ((unused)) -#else -#define NORET -#define UNUSED -#endif - -#ifdef _MSC_VER - -#define __STDC__ 1 -#define O_BINARY _O_BINARY -#define O_CREAT _O_CREAT -#define O_RDWR _O_RDWR -#define O_TRUNC _O_TRUNC -#define S_IFMT _S_IFMT -#define S_IFREG _S_IFREG -#define S_IREAD _S_IREAD -#define S_IWRITE _S_IWRITE -#define close _close -#define dup _dup -#define fdopen _fdopen -#define fileno _fileno - -#ifdef _fstati64 -#define fstat _fstati64 -#else -#define fstat _fstat -#endif - -#define ftime _ftime -#define inline __inline -#define isatty _isatty -#define kbhit _kbhit -#define mktemp _mktemp -#define off_t _off_t -#define open _open -#define pclose _pclose -#define popen _popen -#define setmode _setmode -#define snprintf _snprintf - -#ifdef _stati64 -#define stat _stati64 -#else -#define stat _stat -#endif - -#define strdup _strdup -#define timeb _timeb -#define unlink _unlink - -#if defined(HAVE__FSEEKI64) && !defined(HAVE_FSEEKO) -#undef off_t -#define fseeko _fseeki64 -#define ftello _ftelli64 -#define off_t __int64 -#define HAVE_FSEEKO 1 -#endif - -#elif defined(__MINGW32__) - -#if !defined(HAVE_FSEEKO) -#undef off_t -#define fseeko fseeko64 -#define fstat _fstati64 -#define ftello ftello64 -#define off_t off64_t -#define stat _stati64 -#define HAVE_FSEEKO 1 -#endif - -#endif - -#if defined(DOS) || defined(WIN32) || defined(__NT__) || defined(__DJGPP__) || defined(__OS2__) - #define LAST_SLASH(path) max(strrchr(path, '/'), strrchr(path, '\\')) - #define IS_ABSOLUTE(path) ((path)[0] == '/' || (path)[0] == '\\' || (path)[1] == ':') - #define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) - #define POPEN_MODE "rb" -#else - #define LAST_SLASH(path) strrchr(path, '/') - #define IS_ABSOLUTE(path) ((path)[0] == '/') - #define SET_BINARY_MODE(file) -#endif - -#ifdef WORDS_BIGENDIAN - #define MACHINE_IS_BIGENDIAN 1 - #define MACHINE_IS_LITTLEENDIAN 0 -#else - #define MACHINE_IS_BIGENDIAN 0 - #define MACHINE_IS_LITTLEENDIAN 1 -#endif - -/*--------------------------- Language extensions ----------------------------*/ - -/* Compile-time ("static") assertion */ -/* e.g. assert_static(sizeof(int) >= 4, int_type_too_small) */ -#define assert_static(e,f) enum {assert_static__##f = 1/(e)} -#define array_length(a) (sizeof(a)/sizeof(a[0])) -#define field_offset(type, field) ((size_t)&(((type *)0)->field)) -#define unless(x) if (!(x)) - -/*------------------------------- Maths stuff --------------------------------*/ - -#include - -#ifdef min -#undef min -#endif -#define min(a, b) ((a) <= (b) ? (a) : (b)) - -#ifdef max -#undef max -#endif -#define max(a, b) ((a) >= (b) ? (a) : (b)) - -#define range_limit(x, lower, upper) (min(max(x, lower), upper)) - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#endif -#ifndef M_LN10 -#define M_LN10 2.30258509299404568402 /* natural log of 10 */ -#endif -#ifndef M_SQRT2 -#define M_SQRT2 sqrt(2.) -#endif - -#define sqr(a) ((a) * (a)) -#define sign(x) ((x) < 0? -1 : 1) - -/* Numerical Recipes in C, p. 284 */ -#define ranqd1(x) ((x) = 1664525L * (x) + 1013904223L) /* int32_t x */ -#define dranqd1(x) (ranqd1(x) * (1. / (65536. * 32768.))) /* [-1,1) */ - -#define dB_to_linear(x) exp((x) * M_LN10 * 0.05) -#define linear_to_dB(x) (log10(x) * 20) - -extern int lsx_strcasecmp(const char *s1, const char *st); -extern int lsx_strncasecmp(char const *s1, char const *s2, size_t n); - -#ifndef HAVE_STRCASECMP -#define strcasecmp(s1, s2) lsx_strcasecmp((s1), (s2)) -#define strncasecmp(s1, s2, n) lsx_strncasecmp((s1), (s2), (n)) -#endif diff --git a/freedv/branches/1.2/freedv-dev/src/sox/xmalloc.c b/freedv/branches/1.2/freedv-dev/src/sox/xmalloc.c deleted file mode 100644 index 9bf15969..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/xmalloc.c +++ /dev/null @@ -1,43 +0,0 @@ -/* SoX Memory allocation functions - * - * Copyright (c) 2005-2006 Reuben Thomas. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include - -/* Resize an allocated memory area; abort if not possible. - * - * For malloc, `If the size of the space requested is zero, the behavior is - * implementation defined: either a null pointer is returned, or the - * behavior is as if the size were some nonzero value, except that the - * returned pointer shall not be used to access an object' - */ -void *lsx_realloc(void *ptr, size_t newsize) -{ - if (ptr && newsize == 0) { - free(ptr); - return NULL; - } - - if ((ptr = realloc(ptr, newsize)) == NULL) { - lsx_fail("out of memory"); - exit(2); - } - - return ptr; -} diff --git a/freedv/branches/1.2/freedv-dev/src/sox/xmalloc.h b/freedv/branches/1.2/freedv-dev/src/sox/xmalloc.h deleted file mode 100644 index 9ee77f63..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox/xmalloc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* libSoX Memory allocation functions - * - * Copyright (c) 2005-2006 Reuben Thomas. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LSX_MALLOC_H -#define LSX_MALLOC_H - -#include -#include - -#define lsx_malloc(size) lsx_realloc(NULL, (size)) -#define lsx_calloc(n,s) (((n)*(s))? memset(lsx_malloc((n)*(s)),0,(n)*(s)) : NULL) -#define lsx_Calloc(v,n) v = lsx_calloc(n,sizeof(*(v))) -#define lsx_strdup(p) ((p)? strcpy((char *)lsx_malloc(strlen(p) + 1), p) : NULL) -#define lsx_memdup(p,s) ((p)? memcpy(lsx_malloc(s), p, s) : NULL) -#define lsx_valloc(v,n) v = lsx_malloc((n)*sizeof(*(v))) -#define lsx_revalloc(v,n) v = lsx_realloc(v, (n)*sizeof(*(v))) - -#endif diff --git a/freedv/branches/1.2/freedv-dev/src/sox_biquad.c b/freedv/branches/1.2/freedv-dev/src/sox_biquad.c deleted file mode 100644 index 548f4249..00000000 --- a/freedv/branches/1.2/freedv-dev/src/sox_biquad.c +++ /dev/null @@ -1,134 +0,0 @@ -//========================================================================== -// Name: sox_biquad.h -// Purpose: Interface into Sox Biquad filters -// Created: Dec 1, 2012 -// Authors: David Rowe -// -// To test: -/* - $ gcc sox_biquad.c sox/effects_i.c sox/effects.c sox/formats_i.c \ - sox/biquad.c sox/biquads.c sox/xmalloc.c sox/libsox.c \ - -o sox_biquad -DSOX_BIQUAD_UNITTEST -D__FREEDV__ \ - -Wall -lm -lsndfile -g - $ ./sox_biquad -*/ -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include -#include -#include -#include "sox/sox.h" - -#include "sox_biquad.h" - - -#define N_MAX 1024 - -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, - sox_sample_t *obuf, size_t *isamp, size_t *osamp); - -void sox_biquad_start(void) -{ - int r = sox_init(); - assert(r == SOX_SUCCESS); -} - -void sox_biquad_finish(void) -{ - sox_quit(); -} - -/* - Effect must be implemented by biquads.c in sox, arguments are just - like sox command line, for example: - - char *argv[10]; - argv[0] = "highpass"; argv[1]="1000"; argc=1; -*/ - -void *sox_biquad_create(int argc, const char *argv[]) -{ - int ret; - sox_effect_t *e; - int (*start)(sox_effect_t *); /* function pointer to effect start func */ - - e = sox_create_effect(sox_find_effect(argv[0])); assert(e != NULL); - ret = sox_effect_options(e, argc, (char * const*)&argv[1]); - assert(ret == SOX_SUCCESS); - - start = e->handler.start; - e->in_signal.rate = 8000; /* locked at FS=8000 Hz */ - ret = start(e); assert(ret == SOX_SUCCESS); - - return (void *)e; -} - -void sox_biquad_destroy(void *sbq) { - sox_effect_t *e = (sox_effect_t *)sbq; - free(e); -} - -void sox_biquad_filter(void *sbq, short out[], short in[], int n) -{ - sox_effect_t *e = (sox_effect_t *)sbq; - sox_sample_t ibuf[N_MAX]; - sox_sample_t obuf[N_MAX]; - size_t isamp, osamp; - unsigned int clips; - SOX_SAMPLE_LOCALS; - int i; - - assert(n <= N_MAX); - - clips = 0; - for(i=0; i. -// -//========================================================================== - -#ifndef __SOX_BIQUAD__ -#define __SOX_BIQUAD__ - -#ifdef __cplusplus -extern "C" { - -#endif - -void sox_biquad_start(void); -void sox_biquad_finish(void); -void *sox_biquad_create(int argc, const char *argv[]); -void sox_biquad_destroy(void *sbq); -void sox_biquad_filter(void *sbq, short out[], short in[], int n); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/freedv/branches/1.2/freedv-dev/src/topFrame.cpp b/freedv/branches/1.2/freedv-dev/src/topFrame.cpp deleted file mode 100644 index a5b574a1..00000000 --- a/freedv/branches/1.2/freedv-dev/src/topFrame.cpp +++ /dev/null @@ -1,595 +0,0 @@ -//========================================================================== -// Name: topFrame.cpp -// -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "topFrame.h" - -extern int g_playFileToMicInEventId; -extern int g_recFileFromRadioEventId; -extern int g_playFileFromRadioEventId; - -//========================================================================= -// Code that lays out the main application window -//========================================================================= -TopFrame::TopFrame(wxString plugInName, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) -{ - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); - //===================================================== - // Menubar Setup - m_menubarMain = new wxMenuBar(wxMB_DOCKABLE); - file = new wxMenu(); - - wxMenuItem* m_menuItemOnTop; - m_menuItemOnTop = new wxMenuItem(file, wxID_ANY, wxString(_("On Top")) , _("Always Top Window"), wxITEM_NORMAL); - file->Append(m_menuItemOnTop); - - wxMenuItem* m_menuItemExit; - m_menuItemExit = new wxMenuItem(file, ID_EXIT, wxString(_("E&xit")) , _("Exit Program"), wxITEM_NORMAL); - file->Append(m_menuItemExit); - - m_menubarMain->Append(file, _("&File")); - - tools = new wxMenu(); - wxMenuItem* m_menuItemAudio; - m_menuItemAudio = new wxMenuItem(tools, wxID_ANY, wxString(_("&Audio Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemAudio); - - wxMenuItem* m_menuItemRigCtrlCfg; - m_menuItemRigCtrlCfg = new wxMenuItem(tools, wxID_ANY, wxString(_("&PTT Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemRigCtrlCfg); - - wxMenuItem* m_menuItemOptions; - m_menuItemOptions = new wxMenuItem(tools, wxID_ANY, wxString(_("Options")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemOptions); - - wxMenuItem* m_menuItemFilter; - m_menuItemFilter = new wxMenuItem(tools, wxID_ANY, wxString(_("&Filter")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemFilter); - - wxMenuItem* m_menuItemPlugIn; - if (!wxIsEmpty(plugInName)) { - m_menuItemPlugIn = new wxMenuItem(tools, wxID_ANY, plugInName + wxString(_(" Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemPlugIn); - } - - wxMenuItem* m_menuItemPlayFileToMicIn; - m_menuItemPlayFileToMicIn = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Play File - Mic In")) , wxEmptyString, wxITEM_NORMAL); - g_playFileToMicInEventId = m_menuItemPlayFileToMicIn->GetId(); - tools->Append(m_menuItemPlayFileToMicIn); - - wxMenuItem* m_menuItemRecFileFromRadio; - m_menuItemRecFileFromRadio = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Record File - From Radio")) , wxEmptyString, wxITEM_NORMAL); - g_recFileFromRadioEventId = m_menuItemRecFileFromRadio->GetId(); - tools->Append(m_menuItemRecFileFromRadio); - - wxMenuItem* m_menuItemPlayFileFromRadio; - m_menuItemPlayFileFromRadio = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Play File - From Radio")) , wxEmptyString, wxITEM_NORMAL); - g_playFileFromRadioEventId = m_menuItemPlayFileFromRadio->GetId(); - tools->Append(m_menuItemPlayFileFromRadio); - m_menubarMain->Append(tools, _("&Tools")); - - help = new wxMenu(); - wxMenuItem* m_menuItemHelpUpdates; - m_menuItemHelpUpdates = new wxMenuItem(help, wxID_ANY, wxString(_("Check for Updates")) , wxEmptyString, wxITEM_NORMAL); - help->Append(m_menuItemHelpUpdates); - m_menuItemHelpUpdates->Enable(false); - - wxMenuItem* m_menuItemAbout; - m_menuItemAbout = new wxMenuItem(help, ID_ABOUT, wxString(_("&About")) , _("About this program"), wxITEM_NORMAL); - help->Append(m_menuItemAbout); - - m_menubarMain->Append(help, _("&Help")); - - this->SetMenuBar(m_menubarMain); - - wxBoxSizer* bSizer1; - bSizer1 = new wxBoxSizer(wxHORIZONTAL); - - //===================================================== - // Left side - //===================================================== - wxBoxSizer* leftSizer; - leftSizer = new wxBoxSizer(wxVERTICAL); - - wxStaticBoxSizer* snrSizer; - snrSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("SNR")), wxVERTICAL); - - //------------------------------ - // S/N ratio Guage (vert. bargraph) - //------------------------------ - m_gaugeSNR = new wxGauge(this, wxID_ANY, 25, wxDefaultPosition, wxSize(15,135), wxGA_SMOOTH|wxGA_VERTICAL); - m_gaugeSNR->SetToolTip(_("Displays signal to noise ratio in dB.")); - snrSizer->Add(m_gaugeSNR, 1, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); - - //------------------------------ - // Box for S/N ratio (Numeric) - //------------------------------ - m_textSNR = new wxStaticText(this, wxID_ANY, wxT(" 0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - snrSizer->Add(m_textSNR, 0, wxALIGN_CENTER_HORIZONTAL, 1); - - //------------------------------ - // S/N ratio slow Checkbox - //------------------------------ - m_ckboxSNR = new wxCheckBox(this, wxID_ANY, _("Slow"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - m_ckboxSNR->SetToolTip(_("Smooth but slow SNR estimation")); - snrSizer->Add(m_ckboxSNR, 0, wxALIGN_CENTER_HORIZONTAL, 5); - - leftSizer->Add(snrSizer, 2, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 1); - - //------------------------------ - // Sync Indicator box - //------------------------------ - wxStaticBoxSizer* sbSizer3_33; - sbSizer3_33 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Sync")), wxVERTICAL); - - m_rbSync = new wxRadioButton( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - m_rbSync->SetForegroundColour( wxColour( 255, 0, 0 ) ); - sbSizer3_33->Add(m_rbSync, 0, wxALIGN_CENTER|wxALL, 1); - leftSizer->Add(sbSizer3_33,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // BER Frames box - //------------------------------ - - wxStaticBoxSizer* sbSizer_ber; - sbSizer_ber = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Bit Error Rate")), wxVERTICAL); - - m_BtnBerReset = new wxButton(this, wxID_ANY, _("Reset"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_ber->Add(m_BtnBerReset, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - m_textBits = new wxStaticText(this, wxID_ANY, wxT("Bits: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textBits, 0, wxALIGN_LEFT, 1); - m_textErrors = new wxStaticText(this, wxID_ANY, wxT("Errs: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textErrors, 0, wxALIGN_LEFT, 1); - m_textBER = new wxStaticText(this, wxID_ANY, wxT("BER: 0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textBER, 0, wxALIGN_LEFT, 1); - - m_textResyncs = new wxStaticText(this, wxID_ANY, wxT("Resyncs: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textResyncs, 0, wxALIGN_LEFT, 1); - - leftSizer->Add(sbSizer_ber,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Signal Level(vert. bargraph) - //------------------------------ - wxStaticBoxSizer* levelSizer; - levelSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Level")), wxVERTICAL); - - m_textLevel = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(60,-1), wxALIGN_CENTRE); - m_textLevel->SetForegroundColour(wxColour(255,0,0)); - levelSizer->Add(m_textLevel, 0, wxALIGN_LEFT, 1); - - m_gaugeLevel = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(15,135), wxGA_SMOOTH|wxGA_VERTICAL); - m_gaugeLevel->SetToolTip(_("Peak of From Radio in Rx, or peak of From Mic in Tx mode. If Red you should reduce your levels")); - levelSizer->Add(m_gaugeLevel, 1, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); - - leftSizer->Add(levelSizer, 2, wxALIGN_CENTER|wxALL|wxEXPAND, 1); - - bSizer1->Add(leftSizer, 0, wxALL|wxEXPAND, 5); - - //===================================================== - // Center Section - //===================================================== - wxBoxSizer* centerSizer; - centerSizer = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* upperSizer; - upperSizer = new wxBoxSizer(wxVERTICAL); - - //===================================================== - // Tabbed Notebook control containing display graphs - //===================================================== - //m_auiNbookCtrl = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_NB_BOTTOM|wxAUI_NB_DEFAULT_STYLE); - //long style = wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_CLOSE_ON_ACTIVE_TAB | wxAUI_NB_MIDDLE_CLICK_CLOSE; - long nb_style = wxAUI_NB_BOTTOM | wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS; - m_auiNbookCtrl = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, nb_style); - // This line sets the fontsize for the tabs on the notebook control - m_auiNbookCtrl->SetFont(wxFont(8, 70, 90, 90, false, wxEmptyString)); - - upperSizer->Add(m_auiNbookCtrl, 1, wxALIGN_TOP|wxEXPAND, 1); - centerSizer->Add(upperSizer, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALIGN_TOP|wxEXPAND, 0); - - // lower middle used for user ID - - wxBoxSizer* lowerSizer; - lowerSizer = new wxBoxSizer(wxHORIZONTAL); - - m_BtnCallSignReset = new wxButton(this, wxID_ANY, _("Clear"), wxDefaultPosition, wxDefaultSize, 0); - lowerSizer->Add(m_BtnCallSignReset, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - wxBoxSizer* bSizer15; - bSizer15 = new wxBoxSizer(wxVERTICAL); - m_txtCtrlCallSign = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_txtCtrlCallSign->SetToolTip(_("Call Sign of transmitting station will appear here")); - bSizer15->Add(m_txtCtrlCallSign, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5); - lowerSizer->Add(bSizer15, 1, wxEXPAND, 5); - -#ifdef __EXPERIMENTAL_UDP__ - wxStaticBoxSizer* sbSizer_Checksum = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Checksums")), wxHORIZONTAL); - - wxStaticText *goodLabel = new wxStaticText(this, wxID_ANY, wxT("Good: "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - sbSizer_Checksum->Add(goodLabel, 0, 0, 2); - m_txtChecksumGood = new wxStaticText(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(30,-1), wxALIGN_CENTRE); - sbSizer_Checksum->Add(m_txtChecksumGood, 0, 0, 2); - - wxStaticText *badLabel = new wxStaticText(this, wxID_ANY, wxT("Bad: "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - sbSizer_Checksum->Add(badLabel, 0, 0, 1); - m_txtChecksumBad = new wxStaticText(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(30,-1), wxALIGN_CENTRE); - sbSizer_Checksum->Add(m_txtChecksumBad, 0, 0, 1); - - lowerSizer->Add(sbSizer_Checksum, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - //===================================================== - // These are the buttons that autosend the userid (?) - //===================================================== - - // DR 4 Dec - taken off for screen for Beta release to avoid questions on their use until - // we implement this feature - #ifdef UNIMPLEMENTED - wxBoxSizer* bSizer141; - bSizer141 = new wxBoxSizer(wxHORIZONTAL); - - // TxID - //--------- - m_togTxID = new wxToggleButton(this, wxID_ANY, _("TxID"), wxDefaultPosition, wxDefaultSize, 0); - m_togTxID->SetToolTip(_("Send Tx ID information")); - bSizer141->Add(m_togTxID, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); - - // RxID - //--------- - m_togRxID = new wxToggleButton(this, wxID_ANY, _("RxID"), wxDefaultPosition, wxDefaultSize, 0); - m_togRxID->SetToolTip(_("Enable reception of ID information")); - bSizer141->Add(m_togRxID, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxALL|wxFIXED_MINSIZE, 5); - - lowerSizer->Add(bSizer141, 0, wxALIGN_RIGHT, 5); -#endif - - centerSizer->Add(lowerSizer, 0, wxALIGN_BOTTOM|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 2); - bSizer1->Add(centerSizer, 4, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 1); - - //===================================================== - // Right side - //===================================================== - wxBoxSizer* rightSizer; - rightSizer = new wxBoxSizer(wxVERTICAL); - - //===================================================== - // Squelch Slider Control - //===================================================== - wxStaticBoxSizer* sbSizer3; - sbSizer3 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Squelch")), wxVERTICAL); - - m_sliderSQ = new wxSlider(this, wxID_ANY, 0, 0, 40, wxDefaultPosition, wxSize(-1,80), wxSL_AUTOTICKS|wxSL_INVERSE|wxSL_VERTICAL); - m_sliderSQ->SetToolTip(_("Set Squelch level in dB.")); - - sbSizer3->Add(m_sliderSQ, 1, wxALIGN_CENTER_HORIZONTAL, 0); - - //------------------------------ - // Squelch Level static text box - //------------------------------ - m_textSQ = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - - sbSizer3->Add(m_textSQ, 0, wxALIGN_CENTER_HORIZONTAL, 0); - - //------------------------------ - // Squelch Toggle Checkbox - //------------------------------ - m_ckboxSQ = new wxCheckBox(this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - - sbSizer3->Add(m_ckboxSQ, 0, wxALIGN_CENTER_HORIZONTAL, 0); - rightSizer->Add(sbSizer3, 2, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 0); - - //rightSizer->Add(sbSizer3_33,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - /* new --- */ - - //------------------------------ - // Mode box - //------------------------------ - wxStaticBoxSizer* sbSizer_mode; - sbSizer_mode = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Mode")), wxVERTICAL); - -#ifdef DISABLED_FEATURE - m_rb1400old = new wxRadioButton( this, wxID_ANY, wxT("1400 V0.91"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb1400old, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1400 = new wxRadioButton( this, wxID_ANY, wxT("1400"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1400, 0, wxALIGN_LEFT|wxALL, 1); - m_rb700 = new wxRadioButton( this, wxID_ANY, wxT("700"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700, 0, wxALIGN_LEFT|wxALL, 1); - m_rb700b = new wxRadioButton( this, wxID_ANY, wxT("700B"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700b, 0, wxALIGN_LEFT|wxALL, 1); -#endif - m_rb700c = new wxRadioButton( this, wxID_ANY, wxT("700C"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700c, 0, wxALIGN_LEFT|wxALL, 1); - m_rb800xa = new wxRadioButton( this, wxID_ANY, wxT("800XA"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb800xa, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1600 = new wxRadioButton( this, wxID_ANY, wxT("1600"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1600, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1600->SetValue(true); - - m_rbPlugIn = NULL; - if (!wxIsEmpty(plugInName)) { - // Optional plug in - - m_rbPlugIn = new wxRadioButton( this, wxID_ANY, plugInName, wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rbPlugIn, 0, wxALIGN_LEFT|wxALL, 1); - } - -#ifdef DISABLED_FEATURE - m_rb1600Wide = new wxRadioButton( this, wxID_ANY, wxT("1600 Wide"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1600Wide, 0, wxALIGN_LEFT|wxALL, 1); - m_rb2000 = new wxRadioButton( this, wxID_ANY, wxT("2000"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb2000, 0, wxALIGN_LEFT|wxALL, 1); -#endif - - rightSizer->Add(sbSizer_mode,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - #ifdef MOVED_TO_OPTIONS_DIALOG - /* new --- */ - - //------------------------------ - // Test Frames box - //------------------------------ - - wxStaticBoxSizer* sbSizer_testFrames; - sbSizer_testFrames = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Test Frames")), wxVERTICAL); - - m_ckboxTestFrame = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxTestFrame, 0, wxALIGN_LEFT, 0); - - rightSizer->Add(sbSizer_testFrames,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - #endif - - //===================================================== - // Control Toggles box - //===================================================== - wxStaticBoxSizer* sbSizer5; - sbSizer5 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Control")), wxVERTICAL); - wxBoxSizer* bSizer1511; - bSizer1511 = new wxBoxSizer(wxVERTICAL); - - //------------------------------- - // Stop/Stop signal processing (rx and tx) - //------------------------------- - m_togBtnOnOff = new wxToggleButton(this, wxID_ANY, _("Start"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnOnOff->SetToolTip(_("Begin/End receiving data.")); - bSizer1511->Add(m_togBtnOnOff, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer1511, 0, wxEXPAND, 1); - -#ifdef UNIMPLEMENTED - //------------------------------ - // Toggle Loopback button for RX - //------------------------------ - wxBoxSizer* bSizer15113; - bSizer15113 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* bSizer15111; - bSizer15111 = new wxBoxSizer(wxVERTICAL); - wxSize wxSz = wxSize(44, 30); - m_togBtnLoopRx = new wxToggleButton(this, wxID_ANY, _("Loop\nRX"), wxDefaultPosition, wxSz, 0); - m_togBtnLoopRx->SetFont(wxFont(6, 70, 90, 90, false, wxEmptyString)); - m_togBtnLoopRx->SetToolTip(_("Loopback Receive audio data.")); - - bSizer15111->Add(m_togBtnLoopRx, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - //sbSizer5->Add(bSizer15111, 0, wxEXPAND, 1); - bSizer15113->Add(bSizer15111, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - //------------------------------ - // Toggle Loopback button for Tx - //------------------------------ - wxBoxSizer* bSizer15112; - bSizer15112 = new wxBoxSizer(wxVERTICAL); - m_togBtnLoopTx = new wxToggleButton(this, wxID_ANY, _("Loop\nTX"), wxDefaultPosition, wxSz, 0); - m_togBtnLoopTx->SetFont(wxFont(6, 70, 90, 90, false, wxEmptyString)); - m_togBtnLoopTx->SetToolTip(_("Loopback Transmit audio data.")); - - bSizer15112->Add(m_togBtnLoopTx, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - bSizer15113->Add(bSizer15112, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - sbSizer5->Add(bSizer15113, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - //------------------------------ - // Split Frequency Mode Toggle - //------------------------------ - wxBoxSizer* bSizer151; - bSizer151 = new wxBoxSizer(wxVERTICAL); - - m_togBtnSplit = new wxToggleButton(this, wxID_ANY, _("Split"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnSplit->SetToolTip(_("Toggle split frequency mode.")); - - bSizer151->Add(m_togBtnSplit, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 1); - wxBoxSizer* bSizer13; - bSizer13 = new wxBoxSizer(wxVERTICAL); - - //------------------------------ - // Analog Passthrough Toggle - //------------------------------ - m_togBtnAnalog = new wxToggleButton(this, wxID_ANY, _("Analog"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnAnalog->SetToolTip(_("Toggle analog/digital operation.")); - bSizer13->Add(m_togBtnAnalog, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer13, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - //------------------------------ - // Voice Keyer Toggle - //------------------------------ - m_togBtnVoiceKeyer = new wxToggleButton(this, wxID_ANY, _("Voice Keyer"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnVoiceKeyer->SetToolTip(_("Toggle Voice Keyer")); - wxBoxSizer* bSizer13a = new wxBoxSizer(wxVERTICAL); - bSizer13a->Add(m_togBtnVoiceKeyer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer13a, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - // not implemented on fdmdv2 -#ifdef ALC - //------------------------------ - // Toggle for ALC - //------------------------------ - wxBoxSizer* bSizer14; - bSizer14 = new wxBoxSizer(wxVERTICAL); - m_togBtnALC = new wxToggleButton(this, wxID_ANY, _("ALC"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnALC->SetToolTip(_("Toggle automatic level control mode.")); - - bSizer14->Add(m_togBtnALC, 0, wxALL, 1); - sbSizer5->Add(bSizer14, 0, wxALIGN_CENTER|wxALIGN_CENTER_HORIZONTAL|wxALL, 1); -#endif - - //------------------------------ - // PTT button: Toggle Transmit/Receive mode - //------------------------------ - wxBoxSizer* bSizer11; - bSizer11 = new wxBoxSizer(wxVERTICAL); - m_btnTogPTT = new wxToggleButton(this, wxID_ANY, _("PTT"), wxDefaultPosition, wxDefaultSize, 0); - m_btnTogPTT->SetToolTip(_("Push to Talk - Switch between Receive and Transmit - you can also use the space bar ")); - bSizer11->Add(m_btnTogPTT, 1, wxALIGN_CENTER|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer11, 2, wxEXPAND, 1); - rightSizer->Add(sbSizer5, 2, wxALIGN_CENTER|wxALL|wxEXPAND, 3); - bSizer1->Add(rightSizer, 0, wxALL|wxEXPAND, 3); - this->SetSizer(bSizer1); - this->Layout(); - m_statusBar1 = this->CreateStatusBar(3, wxST_SIZEGRIP, wxID_ANY); - - //===================================================== - // End of layout - //===================================================== - - //------------------- - // Connect Events - //------------------- - this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TopFrame::topFrame_OnClose)); - this->Connect(wxEVT_PAINT, wxPaintEventHandler(TopFrame::topFrame_OnPaint)); - this->Connect(wxEVT_SIZE, wxSizeEventHandler(TopFrame::topFrame_OnSize)); - this->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::topFrame_OnUpdateUI)); - - this->Connect(m_menuItemExit->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnExit)); - this->Connect(m_menuItemOnTop->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnTop)); - - this->Connect(m_menuItemAudio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsAudio)); - this->Connect(m_menuItemAudio->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsAudioUI)); - this->Connect(m_menuItemFilter->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsFilter)); - this->Connect(m_menuItemFilter->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsFilterUI)); - this->Connect(m_menuItemRigCtrlCfg->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsComCfg)); - this->Connect(m_menuItemRigCtrlCfg->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsComCfgUI)); - this->Connect(m_menuItemOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsOptions)); - this->Connect(m_menuItemOptions->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsOptionsUI)); - - if (!wxIsEmpty(plugInName)) { - this->Connect(m_menuItemPlugIn->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsPlugInCfg)); - this->Connect(m_menuItemPlugIn->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsPlugInCfgUI)); - } - - this->Connect(m_menuItemPlayFileToMicIn->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); - this->Connect(m_menuItemRecFileFromRadio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); - this->Connect(m_menuItemPlayFileFromRadio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileFromRadio)); - - this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); - this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - this->Connect(m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpAbout)); - //m_togRxID->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnRxID), NULL, this); - //m_togTxID->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnTxID), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnSliderScrollBottom), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScrollChanged), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnSliderScrollTop), NULL, this); - m_ckboxSQ->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSQClick), NULL, this); - - m_ckboxSNR->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSNRClick), NULL, this); - - m_togBtnOnOff->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnOnOff), NULL, this); - m_togBtnSplit->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnSplitClick), NULL, this); - m_togBtnAnalog->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnAnalogClick), NULL, this); - m_togBtnVoiceKeyer->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnVoiceKeyerClick), NULL, this); -#ifdef ALC - m_togBtnALC->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnALCClick), NULL, this); -#endif - m_btnTogPTT->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnPTT), NULL, this); - - m_BtnCallSignReset->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnCallSignReset), NULL, this); - m_BtnBerReset->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnBerReset), NULL, this); -} - -TopFrame::~TopFrame() -{ - //------------------- - // Disconnect Events - //------------------- - this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TopFrame::topFrame_OnClose)); - this->Disconnect(wxEVT_PAINT, wxPaintEventHandler(TopFrame::topFrame_OnPaint)); - this->Disconnect(wxEVT_SIZE, wxSizeEventHandler(TopFrame::topFrame_OnSize)); - this->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::topFrame_OnUpdateUI)); - this->Disconnect(ID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnExit)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsAudio)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsAudioUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsFilter)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsFilterUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsComCfg)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsComCfgUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsOptions)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsOptionsUI)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsPlugInCfg)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileFromRadio)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - this->Disconnect(ID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpAbout)); - //m_togRxID->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnRxID), NULL, this); - //m_togTxID->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnTxID), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnSliderScrollBottom), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScrollChanged), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnSliderScrollTop), NULL, this); - m_ckboxSQ->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSQClick), NULL, this); - - m_togBtnOnOff->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnOnOff), NULL, this); - m_togBtnSplit->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnSplitClick), NULL, this); - m_togBtnAnalog->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnAnalogClick), NULL, this); - m_togBtnVoiceKeyer->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnVoiceKeyerClick), NULL, this); -#ifdef ALC - m_togBtnALC->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnALCClick), NULL, this); -#endif - m_btnTogPTT->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnPTT), NULL, this); - -} - diff --git a/freedv/branches/1.2/freedv-dev/src/topFrame.h b/freedv/branches/1.2/freedv-dev/src/topFrame.h deleted file mode 100644 index 37950a99..00000000 --- a/freedv/branches/1.2/freedv-dev/src/topFrame.h +++ /dev/null @@ -1,194 +0,0 @@ -//========================================================================== -// Name: topFrame.h -// -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// 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 . -// -//========================================================================== -#ifndef __TOPFRAME_H__ -#define __TOPFRAME_H__ - -#include "version.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/////////////////////////////////////////////////////////////////////////// - -#define ID_OPEN 1000 -#define ID_SAVE 1001 -#define ID_CLOSE 1002 -#define ID_EXIT 1003 -#define ID_COPY 1004 -#define ID_CUT 1005 -#define ID_PASTE 1006 -#define ID_OPTIONS 1007 -#define ID_ABOUT 1008 - -/////////////////////////////////////////////////////////////////////////////// -/// Class TopFrame -/////////////////////////////////////////////////////////////////////////////// -class TopFrame : public wxFrame -{ - private: - - protected: - wxMenuBar* m_menubarMain; - wxMenu* file; - wxMenu* edit; - wxMenu* tools; - wxMenu* help; - wxGauge* m_gaugeSNR; - wxStaticText* m_textSNR; - wxCheckBox* m_ckboxSNR; - wxGauge* m_gaugeLevel; - wxStaticText* m_textLevel; - - wxButton* m_BtnCallSignReset; - wxTextCtrl* m_txtCtrlCallSign; - wxStaticText* m_txtChecksumGood; - wxStaticText* m_txtChecksumBad; - - wxSlider* m_sliderSQ; - wxCheckBox* m_ckboxSQ; - wxStaticText* m_textSQ; - wxStatusBar* m_statusBar1; - - wxButton* m_BtnBerReset; - wxStaticText *m_textBits; - wxStaticText *m_textErrors; - wxStaticText *m_textBER; - wxStaticText *m_textResyncs; - - wxRadioButton *m_rbSync; - wxRadioButton *m_rb1400old; - wxRadioButton *m_rb1400; - wxRadioButton *m_rb700; - wxRadioButton *m_rb700b; - wxRadioButton *m_rb700c; - wxRadioButton *m_rb800xa; - wxRadioButton *m_rb1600; - wxRadioButton *m_rb2000; - wxRadioButton *m_rb1600Wide; - wxRadioButton *m_rbPlugIn; - - // Virtual event handlers, overide them in your derived class - virtual void topFrame_OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void topFrame_OnPaint( wxPaintEvent& event ) { event.Skip(); } - virtual void topFrame_OnSize( wxSizeEvent& event ) { event.Skip(); } - virtual void topFrame_OnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnExit( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTop( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsAudio( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsAudioUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsFilter( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsFilterUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsOptions( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnToolsPlugInCfg( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsPlugInCfgUI( wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnToolsUDP( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsOptionsUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsComCfg( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsComCfgUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnPlayFileToMicIn( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRecFileFromRadio( wxCommandEvent& event ) { event.Skip(); } - virtual void OnPlayFileFromRadio( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnHelpCheckUpdates( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnHelpAbout( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnRxID( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnTxID( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCmdSliderScroll( wxScrollEvent& event ) { event.Skip(); } - virtual void OnSliderScrollBottom( wxScrollEvent& event ) { event.Skip(); } - virtual void OnCmdSliderScrollChanged( wxScrollEvent& event ) { event.Skip(); } - virtual void OnSliderScrollTop( wxScrollEvent& event ) { event.Skip(); } - virtual void OnCheckSQClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCheckSNRClick( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnTogBtnLoopRx( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnLoopTx( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnOnOff( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnSplitClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnAnalogClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnVoiceKeyerClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnALCClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnPTT( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnTogBtnSplitClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnAnalogClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnALCClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnRxIDUI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnTxIDUI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnPTT_UI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnOnOffUI(wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnCallSignReset( wxCommandEvent& event ) { event.Skip(); } - virtual void OnBerReset( wxCommandEvent& event ) { event.Skip(); } - - public: - wxToggleButton* m_togRxID; - wxToggleButton* m_togTxID; - wxToggleButton* m_togBtnOnOff; - wxToggleButton* m_togBtnSplit; - wxToggleButton* m_togBtnAnalog; - wxToggleButton* m_togBtnVoiceKeyer; - wxToggleButton* m_togBtnALC; - wxToggleButton* m_btnTogPTT; - wxToggleButton* m_togBtnLoopRx; - wxToggleButton* m_togBtnLoopTx; - wxAuiNotebook* m_auiNbookCtrl; - - TopFrame( wxString plugInName, wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeDV ") + _(FREEDV_VERSION), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(561,300 ), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL ); - - ~TopFrame(); -}; - -#endif //__TOPFRAME_H__ diff --git a/freedv/branches/1.2/script/spot.sh b/freedv/branches/1.2/script/spot.sh deleted file mode 100644 index cb1309a2..00000000 --- a/freedv/branches/1.2/script/spot.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# -# spot.sh -# David Rowe Sep 2015 -# - -# Demo script for "spotting" based on FreeDV txt string. Posts a -# date-stamped text file to a web server. Called from FreeDV GUI -# program when a callsign is received in the txt msg. - - -# Q: how to remove repeated spots, or those close in time? -# -# Set up automated lftp login: -# -# $ lftp ftp://username@server -# Password: -# lftp username@server:~> set bmk:save-passwords true -# lftp username@server:~> bookmark add yourserver -# lftp username@server:~> bookmark list -# lftp username@server:~> quit - -SPOTFILE=/home/david/tmp/freedvspot.html -FTPSERVER=ftp.rowetel.com - -echo `date -u` " " $1 "
" >> $SPOTFILE -tail -n 25 $SPOTFILE > /tmp/spot.tmp1 -mv /tmp/spot.tmp1 $SPOTFILE -lftp -e "cd www;put $SPOTFILE;quit" $FTPSERVER diff --git a/freedv/branches/1.2/src/CMakeLists.txt b/freedv/branches/1.2/src/CMakeLists.txt deleted file mode 100644 index ef0798b3..00000000 --- a/freedv/branches/1.2/src/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -set(FREEDV_SOURCES - dlg_audiooptions.cpp - dlg_filter.cpp - dlg_options.cpp - dlg_ptt.cpp - dlg_plugin.cpp - fdmdv2_main.cpp - fdmdv2_pa_wrapper.cpp - fdmdv2_plot.cpp - fdmdv2_plot_scalar.cpp - fdmdv2_plot_scatter.cpp - fdmdv2_plot_spectrum.cpp - fdmdv2_plot_waterfall.cpp - hamlib.cpp - serialport.cpp - topFrame.cpp - sox_biquad.c - comp.h - dlg_audiooptions.h - dlg_filter.h - dlg_options.h - dlg_ptt.h - fdmdv2_defines.h - fdmdv2_main.h - fdmdv2_pa_wrapper.h - fdmdv2_plot.h - fdmdv2_plot_scalar.h - fdmdv2_plot_scatter.h - fdmdv2_plot_spectrum.h - fdmdv2_plot_waterfall.h - hamlib.h - sox_biquad.h - sox/band.h - sox/biquad.c - sox/biquads.c - sox/biquad.h - sox/effects.c - sox/effects.h - sox/effects_i.c - sox/formats_i.c - sox/libsox.c - sox/sox.h - sox/sox_i.h - sox/soxomp.h - sox/util.h - sox/xmalloc.h - sox/xmalloc.c - topFrame.h - version.h -) - -# WIN32 is needed for Windows GUI apps and is ignored for UNIX like systems. -add_executable(freedv WIN32 ${FREEDV_SOURCES} ${RES_FILES}) -target_link_libraries(freedv ${FREEDV_LINK_LIBS}) -include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) -if(FREEDV_STATIC_DEPS) - add_dependencies(freedv ${FREEDV_STATIC_DEPS}) -endif(FREEDV_STATIC_DEPS) -install(TARGETS freedv - RUNTIME DESTINATION bin) - -# Custom commands to build OSX images. -if(APPLE) - add_custom_command( - TARGET freedv - POST_BUILD - COMMAND mkdir ARGS -p FreeDV.app/Contents/MacOS - COMMAND mkdir ARGS -p FreeDV.app/Contents/Resources/English.lproj - COMMAND cp ARGS ${CMAKE_CURRENT_SOURCE_DIR}/info.plist FreeDV.app/Contents - COMMAND cp ARGS ${CMAKE_CURRENT_SOURCE_DIR}/freedv.icns FreeDV.app/Contents/Resources - COMMAND echo ARGS -n "APPL????" > FreeDV.app/Contents/PkgInfo - COMMAND cp ARGS freedv FreeDV.app/Contents/MacOS/FreeDV - COMMAND dylibbundler ARGS -od -b -x FreeDV.app/Contents/MacOS/FreeDV -d FreeDV.app/Contents/libs -p @executable_path/../libs/ - COMMAND mkdir dist_tmp - COMMAND cp -r FreeDV.app dist_tmp - COMMAND hdiutil create -srcfolder dist_tmp/ -volname FreeDV -format UDZO ./FreeDV.dmg - COMMAND rm -rf dist_tmp - ) -endif(APPLE) diff --git a/freedv/branches/1.2/src/Makefile.win32 b/freedv/branches/1.2/src/Makefile.win32 deleted file mode 100644 index 932e8518..00000000 --- a/freedv/branches/1.2/src/Makefile.win32 +++ /dev/null @@ -1,52 +0,0 @@ -# src/Makefile.win32 -# David Rowe 26 Oct 2012 -# -# Makefile for Win32 on msys/Mingw to help David R get up to speed -# -# $ make -f Makefile.Win32 - -CODEC2_PATH=$(HOME)/codec2-dev -INCLUDE_PATH=/usr/local/include - -WX_CONFIG=wx-config -WX_CPPFLAGS = $(shell $(WX_CONFIG) --cxxflags) -D__WXDEBUG__ -WX_LIBS = $(shell $(WX_CONFIG) --libs core, base, aui, adv, net) -SVN_REVISION=$(shell svnversion) -CODEC2_INC=$(CODEC2_PATH)/src -CODEC2_LIB=$(CODEC2_PATH)/build_win32/src/ - -CPP_FLAGS = -D_NO_AUTOTOOLS_ -I$(INCLUDE_PATH) $(WX_CPPFLAGS) -I$(CODEC2_INC) -I../extern/include -I. -g -Wall -DSVN_REVISION=\"$(SVN_REVISION)\" -LIBS = $(WX_LIBS) -L$(CODEC2_LIB) -lcodec2 -lm -lportaudiocpp -lportaudio -lpthread -lsndfile -lsamplerate -lhamlib -lsox -lspeexdsp - -OBJS = topFrame.o \ -fdmdv2_main.o \ -fdmdv2_plot.o \ -fdmdv2_plot_scalar.o \ -fdmdv2_plot_scatter.o \ -fdmdv2_plot_spectrum.o \ -fdmdv2_plot_waterfall.o \ -fdmdv2_pa_wrapper.o \ -dlg_audiooptions.o \ -dlg_ptt.o \ -dlg_options.o \ -dlg_filter.o \ -sox_biquad.o \ -hamlib.o \ -../../codec2-dev/src/golay23.o - -HDRS = version.h dlg_audiooptions.h dlg_ptt.h dlg_filter.h fdmdv2_main.h fdmdv2_defines.h fdmdv2_plot.h fdmdv2_plot_scalar.h fdmdv2_plot_waterfall.h fdmdv2_plot_scatter.h fdmdv2_plot_spectrum.h fdmdv2_pa_wrapper.h topFrame.h dlg_audiooptions.h topFrame.h varicode.h ../../codec2-dev/src/golay23.h hamlib.h - -all: freedv - -freedv: $(OBJS) - g++ -o freedv $(OBJS) $(CPP_FLAGS) $(LIBS) - -%.o: %.cpp $(HDRS) Makefile.win32 - g++ $(CPP_FLAGS) -c $< -o $@ - -%.o: %.c $(HDRS) Makefile.win32 - gcc $(CPP_FLAGS) -c $< -o $@ - -clean: - rm -f *.o fdmdv2 - diff --git a/freedv/branches/1.2/src/afreedvplugin.c b/freedv/branches/1.2/src/afreedvplugin.c deleted file mode 100644 index 5fdc424e..00000000 --- a/freedv/branches/1.2/src/afreedvplugin.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - afreedvplugin.c - David Rowe Feb 2016 - - Sample FreeDV plugin - - TODO: - [ ] plugin to call back to functions - [ ] ability to list .so's/DLLs and scan - [ ] where do we put plugins? - [ ] Windows build and test environment - - linux .so: - $ gcc -Wall -fPIC -c afreedvplugin.c - $ gcc -shared -Wl,-soname,afreedvplugin.so -o afreedvplugin.so afreedvplugin.o - win32 .dll: - $ i686-w64-mingw32-gcc -c afreedvplugin.c - $ i686-w64-mingw32-gcc -shared -o afreedvplugin.dll afreedvplugin.o -Wl,--out-implib,afreedvplugin_dll.a - -*/ - -#include -#include -#include - -#ifdef _WIN32_ -#define DLL __declspec(dllexport) -#else -#define DLL -#endif - - -#ifdef LATER -/* functions plugin can call - not sure how to link to these */ - -int plugin_alert(char string[]); -int plugin_get_persistant(char name[], char value[]); -int plugin_set_persistant(char name[], char value[]); -#endif -static int (*plugin_get_persistant)(char name[], char value[]); - -struct APLUGIN_STATES { - int symbol_rate; - int num_tones; - int counter; -}; - -/* plugin functions called by host, we need to write these */ - -void DLL plugin_name(char name[]) { - - sprintf(name, "aFreeDVplugIn"); -} - -/* - Text fields will be created for nparams, using the names - in *param_names[]. These fields we be saved to persistent - storage as name/param_names[0], name/param_names[1] .... -*/ - -void DLL *plugin_open(char *param_names[], - int *nparams, - int (*aplugin_get_persistant)(char *, char *)) -{ - struct APLUGIN_STATES *states; - - /* set up function ptrs */ - - plugin_get_persistant = aplugin_get_persistant; - - /* tell host how many persistent parameters we have and their names */ - - strcpy(param_names[0], "SymbolRate"); - strcpy(param_names[1], "NumTones"); - *nparams = 2; - - /* init local states */ - - states = (struct APLUGIN_STATES *)malloc(sizeof(struct APLUGIN_STATES)); - if (states == NULL) { - // TODO: plugin_alert("Problem starting plugin!"); - return NULL; - } - states->counter = 0; - - return (void*)states; -} - -void DLL plugin_close(void *states) { - free(states); -} - -void DLL plugin_start(void *s) { - struct APLUGIN_STATES *states = (struct APLUGIN_STATES*)s; - char txt[80]; - - fprintf(stderr, "\nplugin_start\n"); - - (plugin_get_persistant)("SymbolRate",txt); - states->symbol_rate = atoi(txt); - - (plugin_get_persistant)("NumTones",txt); - states->num_tones = atoi(txt); - - fprintf(stderr, "symbol_rate: %d num_tones: %d\n", states->symbol_rate, states->num_tones); -} - -void DLL plugin_stop(void *states) { - fprintf(stderr, "\nplugin_stop\n"); -} - -void DLL plugin_rx_samples(void *s, short samples[], int n) { - struct APLUGIN_STATES *states = (struct APLUGIN_STATES*)s; - //fprintf(stderr, "Got n=%d samples!\n", n); - //fprintf(stderr, "samples[0] = %d samples[%d-1] = %d counter = %d\n", samples[0], n, samples[n-1], states->counter++); -} - diff --git a/freedv/branches/1.2/src/comp.h b/freedv/branches/1.2/src/comp.h deleted file mode 100644 index a3a1bd9b..00000000 --- a/freedv/branches/1.2/src/comp.h +++ /dev/null @@ -1,39 +0,0 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: comp.h - AUTHOR......: David Rowe - DATE CREATED: 24/08/09 - - Complex number definition. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2009 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 . -*/ - -#ifndef __COMP__ -#define __COMP__ - -/* Complex number */ - -typedef struct -{ - float real; - float imag; -} COMP; - -#endif diff --git a/freedv/branches/1.2/src/dlg_audiooptions.cpp b/freedv/branches/1.2/src/dlg_audiooptions.cpp deleted file mode 100644 index 4e8cd981..00000000 --- a/freedv/branches/1.2/src/dlg_audiooptions.cpp +++ /dev/null @@ -1,1264 +0,0 @@ -//========================================================================= -// Name: AudioOptsDialog.cpp -// Purpose: Implements an Audio options selection dialog. -// -// Authors: David Rowe, David Witten -// License: -// -// All rights reserved. -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================= -#include "fdmdv2_main.h" -#include "dlg_audiooptions.h" - -// constants for test waveform plots - -#define TEST_WAVEFORM_X 180 -#define TEST_WAVEFORM_Y 180 -#define TEST_WAVEFORM_PLOT_TIME 2.0 -#define TEST_WAVEFORM_PLOT_FS 400 -#define TEST_BUF_SIZE 1024 -#define TEST_FS 48000.0 -#define TEST_DT 0.1 // time between plot updates in seconds -#define TEST_WAVEFORM_PLOT_BUF ((int)(DT*400)) - -void AudioOptsDialog::Pa_Init(void) -{ - m_isPaInitialized = false; - - if((pa_err = Pa_Initialize()) == paNoError) - { - m_isPaInitialized = true; - } - else - { - wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK); - return; - } -} - - -void AudioOptsDialog::buildTestControls(PlotScalar **plotScalar, wxButton **btnTest, - wxPanel *parentPanel, wxBoxSizer *bSizer, wxString buttonLabel) -{ - wxBoxSizer* bSizer1 = new wxBoxSizer(wxVERTICAL); - - wxPanel *panel = new wxPanel(parentPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); - *plotScalar = new PlotScalar((wxFrame*) panel, 1, TEST_WAVEFORM_PLOT_TIME, 1.0/TEST_WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "", 1); - (*plotScalar)->SetClientSize(wxSize(TEST_WAVEFORM_X,TEST_WAVEFORM_Y)); - bSizer1->Add(panel, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 8); - - *btnTest = new wxButton(parentPanel, wxID_ANY, buttonLabel, wxDefaultPosition, wxDefaultSize); - bSizer1->Add(*btnTest, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 0); - - bSizer->Add(bSizer1, 0, wxALIGN_CENTER_HORIZONTAL |wxALIGN_CENTER_VERTICAL ); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// AudioOptsDialog() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -AudioOptsDialog::AudioOptsDialog(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - //this->SetSizeHints(wxSize(850, 600), wxDefaultSize); - fprintf(stderr, "pos %d %d\n", pos.x, pos.y); - Pa_Init(); - - wxBoxSizer* mainSizer; - mainSizer = new wxBoxSizer(wxVERTICAL); - m_panel1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer4; - bSizer4 = new wxBoxSizer(wxVERTICAL); - m_notebook1 = new wxNotebook(m_panel1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM); - m_panelRx = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer20; - bSizer20 = new wxBoxSizer(wxVERTICAL); - wxGridSizer* gSizer4; - gSizer4 = new wxGridSizer(2, 1, 0, 0); - - // Rx In ----------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer2; - sbSizer2 = new wxStaticBoxSizer(new wxStaticBox(m_panelRx, wxID_ANY, _("From Radio")), wxHORIZONTAL); - - wxBoxSizer* bSizer811a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlRxInDevices = new wxListCtrl(m_panelRx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer811a->Add(m_listCtrlRxInDevices, 1, wxALL|wxEXPAND, 1); - - wxBoxSizer* bSizer811; - bSizer811 = new wxBoxSizer(wxHORIZONTAL); - m_staticText51 = new wxStaticText(m_panelRx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText51->Wrap(-1); - bSizer811->Add(m_staticText51, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_textCtrlRxIn = new wxTextCtrl(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer811->Add(m_textCtrlRxIn, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText6 = new wxStaticText(m_panelRx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText6->Wrap(-1); - bSizer811->Add(m_staticText6, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateRxIn = new wxComboBox(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer811->Add(m_cbSampleRateRxIn, 0, wxALIGN_CENTER_VERTICAL|wxALL, 1); - - bSizer811a->Add(bSizer811, 0, wxEXPAND, 5); - - sbSizer2->Add(bSizer811a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarRxIn, &m_btnRxInTest, m_panelRx, sbSizer2, _("Rec 2s")); - - gSizer4->Add(sbSizer2, 1, wxEXPAND, 5); - - // Rx Out ----------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer3; - sbSizer3 = new wxStaticBoxSizer(new wxStaticBox(m_panelRx, wxID_ANY, _("To Speaker/Headphones")), wxHORIZONTAL); - - wxBoxSizer* bSizer81a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlRxOutDevices = new wxListCtrl(m_panelRx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer81a->Add(m_listCtrlRxOutDevices, 1, wxALL|wxEXPAND, 1); - - wxBoxSizer* bSizer81; - bSizer81 = new wxBoxSizer(wxHORIZONTAL); - m_staticText9 = new wxStaticText(m_panelRx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText9->Wrap(-1); - bSizer81->Add(m_staticText9, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_textCtrlRxOut = new wxTextCtrl(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer81->Add(m_textCtrlRxOut, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText10 = new wxStaticText(m_panelRx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText10->Wrap(-1); - bSizer81->Add(m_staticText10, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateRxOut = new wxComboBox(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer81->Add(m_cbSampleRateRxOut, 0, wxALIGN_CENTER_VERTICAL|wxALL, 1); - - bSizer81a->Add(bSizer81, 0, wxEXPAND, 5); - - sbSizer3->Add(bSizer81a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarRxOut, &m_btnRxOutTest, m_panelRx, sbSizer3, _("Play 2s")); - - gSizer4->Add(sbSizer3, 1, wxEXPAND, 2); - bSizer20->Add(gSizer4, 1, wxEXPAND, 1); - m_panelRx->SetSizer(bSizer20); - m_panelRx->Layout(); - bSizer20->Fit(m_panelRx); - m_notebook1->AddPage(m_panelRx, _("Receive"), true); - - // Tx Tab ------------------------------------------------------------------------------- - - m_panelTx = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer18; - bSizer18 = new wxBoxSizer(wxVERTICAL); - wxGridSizer* gSizer2; - gSizer2 = new wxGridSizer(2, 1, 0, 0); - - // Tx In ---------------------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer22; - sbSizer22 = new wxStaticBoxSizer(new wxStaticBox(m_panelTx, wxID_ANY, _("From Microphone")), wxHORIZONTAL); - - wxBoxSizer* bSizer83a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlTxInDevices = new wxListCtrl(m_panelTx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer83a->Add(m_listCtrlTxInDevices, 1, wxALL|wxEXPAND, 1); - wxBoxSizer* bSizer83; - bSizer83 = new wxBoxSizer(wxHORIZONTAL); - m_staticText12 = new wxStaticText(m_panelTx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText12->Wrap(-1); - bSizer83->Add(m_staticText12, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_textCtrlTxIn = new wxTextCtrl(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer83->Add(m_textCtrlTxIn, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText11 = new wxStaticText(m_panelTx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText11->Wrap(-1); - bSizer83->Add(m_staticText11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateTxIn = new wxComboBox(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer83->Add(m_cbSampleRateTxIn, 0, wxALL, 1); - - bSizer83a->Add(bSizer83, 0, wxEXPAND, 5); - - sbSizer22->Add(bSizer83a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarTxIn, &m_btnTxInTest, m_panelTx, sbSizer22, _("Rec 2s")); - - gSizer2->Add(sbSizer22, 1, wxEXPAND, 5); - - // Tx Out ---------------------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer21; - sbSizer21 = new wxStaticBoxSizer(new wxStaticBox(m_panelTx, wxID_ANY, _("To Radio")), wxHORIZONTAL); - - wxBoxSizer* bSizer82a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlTxOutDevices = new wxListCtrl(m_panelTx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer82a->Add(m_listCtrlTxOutDevices, 1, wxALL|wxEXPAND, 2); - wxBoxSizer* bSizer82; - bSizer82 = new wxBoxSizer(wxHORIZONTAL); - m_staticText81 = new wxStaticText(m_panelTx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText81->Wrap(-1); - bSizer82->Add(m_staticText81, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_textCtrlTxOut = new wxTextCtrl(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer82->Add(m_textCtrlTxOut, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText71 = new wxStaticText(m_panelTx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText71->Wrap(-1); - bSizer82->Add(m_staticText71, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateTxOut = new wxComboBox(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer82->Add(m_cbSampleRateTxOut, 0, wxALL, 1); - - bSizer82a->Add(bSizer82, 0, wxEXPAND, 5); - - sbSizer21->Add(bSizer82a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarTxOut, &m_btnTxOutTest, m_panelTx, sbSizer21, _("Play 2s")); - - gSizer2->Add(sbSizer21, 1, wxEXPAND, 5); - bSizer18->Add(gSizer2, 1, wxEXPAND, 1); - m_panelTx->SetSizer(bSizer18); - m_panelTx->Layout(); - bSizer18->Fit(m_panelTx); - m_notebook1->AddPage(m_panelTx, _("Transmit"), false); - - // API Tab ------------------------------------------------------------------- - - m_panelAPI = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer12; - bSizer12 = new wxBoxSizer(wxHORIZONTAL); - wxGridSizer* gSizer31; - gSizer31 = new wxGridSizer(2, 1, 0, 0); - wxStaticBoxSizer* sbSizer1; - sbSizer1 = new wxStaticBoxSizer(new wxStaticBox(m_panelAPI, wxID_ANY, _("PortAudio")), wxVERTICAL); - - wxGridSizer* gSizer3; - gSizer3 = new wxGridSizer(4, 2, 0, 0); - - m_staticText7 = new wxStaticText(m_panelAPI, wxID_ANY, _("PortAudio Version String:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText7->Wrap(-1); - gSizer3->Add(m_staticText7, 1, wxALIGN_RIGHT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - m_textStringVer = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - gSizer3->Add(m_textStringVer, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText8 = new wxStaticText(m_panelAPI, wxID_ANY, _("PortAudio Int Version:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText8->Wrap(-1); - gSizer3->Add(m_staticText8, 1, wxALIGN_RIGHT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - m_textIntVer = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - gSizer3->Add(m_textIntVer, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText5 = new wxStaticText(m_panelAPI, wxID_ANY, _("Device Count:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText5->Wrap(-1); - gSizer3->Add(m_staticText5, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 10); - m_textCDevCount = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - gSizer3->Add(m_textCDevCount, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText4 = new wxStaticText(m_panelAPI, wxID_ANY, _("API Count:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText4->Wrap(-1); - gSizer3->Add(m_staticText4, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 10); - m_textAPICount = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - m_textAPICount->SetMaxSize(wxSize(45,-1)); - gSizer3->Add(m_textAPICount, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - sbSizer1->Add(gSizer3, 1, wxEXPAND, 2); - gSizer31->Add(sbSizer1, 1, wxEXPAND, 2); - wxStaticBoxSizer* sbSizer6; - sbSizer6 = new wxStaticBoxSizer(new wxStaticBox(m_panelAPI, wxID_ANY, _("Other")), wxVERTICAL); - gSizer31->Add(sbSizer6, 1, wxEXPAND, 5); - bSizer12->Add(gSizer31, 1, wxEXPAND, 5); - m_panelAPI->SetSizer(bSizer12); - m_panelAPI->Layout(); - bSizer12->Fit(m_panelAPI); - m_notebook1->AddPage(m_panelAPI, _("API Info"), false); - bSizer4->Add(m_notebook1, 1, wxEXPAND | wxALL, 0); - m_panel1->SetSizer(bSizer4); - m_panel1->Layout(); - bSizer4->Fit(m_panel1); - mainSizer->Add(m_panel1, 1, wxEXPAND | wxALL, 1); - - wxBoxSizer* bSizer6; - bSizer6 = new wxBoxSizer(wxHORIZONTAL); - m_btnRefresh = new wxButton(this, wxID_ANY, _("Refresh"), wxDefaultPosition, wxDefaultSize, 0); - bSizer6->Add(m_btnRefresh, 0, wxALIGN_CENTER|wxALL, 2); - - m_sdbSizer1 = new wxStdDialogButtonSizer(); - - m_sdbSizer1OK = new wxButton(this, wxID_OK); - m_sdbSizer1->AddButton(m_sdbSizer1OK); - - m_sdbSizer1Cancel = new wxButton(this, wxID_CANCEL); - m_sdbSizer1->AddButton(m_sdbSizer1Cancel); - - m_sdbSizer1Apply = new wxButton(this, wxID_APPLY); - m_sdbSizer1->AddButton(m_sdbSizer1Apply); - - m_sdbSizer1->Realize(); - - bSizer6->Add(m_sdbSizer1, 1, wxALIGN_CENTER_VERTICAL, 2); - mainSizer->Add(bSizer6, 0, wxEXPAND, 2); - this->SetSizer(mainSizer); - this->Layout(); - this->Centre(wxBOTH); -// this->Centre(wxBOTH); - - m_notebook1->SetSelection(0); - - showAPIInfo(); - m_RxInDevices.m_listDevices = m_listCtrlRxInDevices; - m_RxInDevices.direction = AUDIO_IN; - m_RxInDevices.m_textDevice = m_textCtrlRxIn; - m_RxInDevices.m_cbSampleRate = m_cbSampleRateRxIn; - - m_RxOutDevices.m_listDevices = m_listCtrlRxOutDevices; - m_RxOutDevices.direction = AUDIO_OUT; - m_RxOutDevices.m_textDevice = m_textCtrlRxOut; - m_RxOutDevices.m_cbSampleRate = m_cbSampleRateRxOut; - - m_TxInDevices.m_listDevices = m_listCtrlTxInDevices; - m_TxInDevices.direction = AUDIO_IN; - m_TxInDevices.m_textDevice = m_textCtrlTxIn; - m_TxInDevices.m_cbSampleRate = m_cbSampleRateTxIn; - - m_TxOutDevices.m_listDevices = m_listCtrlTxOutDevices; - m_TxOutDevices.direction = AUDIO_OUT; - m_TxOutDevices.m_textDevice = m_textCtrlTxOut; - m_TxOutDevices.m_cbSampleRate = m_cbSampleRateTxOut; - - populateParams(m_RxInDevices); - populateParams(m_RxOutDevices); - populateParams(m_TxInDevices); - populateParams(m_TxOutDevices); - - m_listCtrlRxInDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnRxInDeviceSelect ), NULL, this ); - m_listCtrlRxOutDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnRxOutDeviceSelect ), NULL, this ); - m_listCtrlTxInDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnTxInDeviceSelect ), NULL, this ); - m_listCtrlTxOutDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnTxOutDeviceSelect ), NULL, this ); - - // wire up test buttons - m_btnRxInTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxInTest ), NULL, this ); - m_btnRxOutTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxOutTest ), NULL, this ); - m_btnTxInTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxInTest ), NULL, this ); - m_btnTxOutTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxOutTest ), NULL, this ); - - m_btnRefresh->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRefreshClick ), NULL, this ); - m_sdbSizer1Apply->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnApplyAudioParameters ), NULL, this ); - m_sdbSizer1Cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnCancelAudioParameters ), NULL, this ); - m_sdbSizer1OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnOkAudioParameters ), NULL, this ); -/* - void OnClose( wxCloseEvent& event ) { event.Skip(); } - void OnHibernate( wxActivateEvent& event ) { event.Skip(); } - void OnIconize( wxIconizeEvent& event ) { event.Skip(); } - void OnInitDialog( wxInitDialogEvent& event ) { event.Skip(); } -*/ -// this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(AudioOptsDialog::OnClose)); - this->Connect(wxEVT_HIBERNATE, wxActivateEventHandler(AudioOptsDialog::OnHibernate)); - this->Connect(wxEVT_ICONIZE, wxIconizeEventHandler(AudioOptsDialog::OnIconize)); - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(AudioOptsDialog::OnInitDialog)); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// ~AudioOptsDialog() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -AudioOptsDialog::~AudioOptsDialog() -{ - Pa_Terminate(); - - // Disconnect Events - this->Disconnect(wxEVT_HIBERNATE, wxActivateEventHandler(AudioOptsDialog::OnHibernate)); - this->Disconnect(wxEVT_ICONIZE, wxIconizeEventHandler(AudioOptsDialog::OnIconize)); - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(AudioOptsDialog::OnInitDialog)); - - m_listCtrlRxInDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnRxInDeviceSelect), NULL, this); - m_listCtrlRxOutDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnRxOutDeviceSelect), NULL, this); - m_listCtrlTxInDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnTxInDeviceSelect), NULL, this); - m_listCtrlTxOutDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnTxOutDeviceSelect), NULL, this); - - m_btnRxInTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxInTest ), NULL, this ); - m_btnRxOutTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxOutTest ), NULL, this ); - m_btnTxInTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxInTest ), NULL, this ); - m_btnTxOutTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxOutTest ), NULL, this ); - - m_btnRefresh->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnRefreshClick), NULL, this); - m_sdbSizer1Apply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnApplyAudioParameters), NULL, this); - m_sdbSizer1Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnCancelAudioParameters), NULL, this); - m_sdbSizer1OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnOkAudioParameters), NULL, this); - -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnInitDialog( wxInitDialogEvent& event ) -{ - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -// -// helper function to look up name of devNum, and if it exists write -// name to textCtrl. Used to trap dissapearing devices. -//------------------------------------------------------------------------- -int AudioOptsDialog::setTextCtrlIfDevNumValid(wxTextCtrl *textCtrl, wxListCtrl *listCtrl, int devNum) -{ - int i, aDevNum, found_devNum; - - // ignore last list entry as it is the "none" entry - - found_devNum = 0; - for(i=0; iGetItemCount()-1; i++) { - aDevNum = wxAtoi(listCtrl->GetItemText(i, 1)); - //printf("aDevNum: %d devNum: %d\n", aDevNum, devNum); - if (aDevNum == devNum) { - found_devNum = 1; - textCtrl->SetValue(listCtrl->GetItemText(i, 0) + " (" + wxString::Format(wxT("%i"),devNum) + ")"); - printf("setting focus of %d\n", i); - listCtrl->SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED); - } - } - - if (found_devNum) - return devNum; - else { - textCtrl->SetValue("none"); - return -1; - } -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -int AudioOptsDialog::ExchangeData(int inout) -{ - if(inout == EXCHANGE_DATA_IN) - { - // Map sound card device numbers to tx/rx device numbers depending - // on number of sound cards in use - - printf("EXCHANGE_DATA_IN:\n"); - printf(" g_nSoundCards: %d\n", g_nSoundCards); - printf(" g_soundCard1InDeviceNum: %d\n", g_soundCard1InDeviceNum); - printf(" g_soundCard1OutDeviceNum: %d\n", g_soundCard1OutDeviceNum); - printf(" g_soundCard1SampleRate: %d\n", g_soundCard1SampleRate); - printf(" g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - printf(" g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - printf(" g_soundCard2SampleRate: %d\n", g_soundCard2SampleRate); - - if (g_nSoundCards == 0) { - m_textCtrlRxIn ->SetValue("none"); rxInAudioDeviceNum = -1; - m_textCtrlRxOut->SetValue("none"); rxOutAudioDeviceNum = -1; - m_textCtrlTxIn ->SetValue("none"); txInAudioDeviceNum = -1; - m_textCtrlTxOut->SetValue("none"); txOutAudioDeviceNum = -1; - } - - if (g_nSoundCards == 1) { - rxInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxIn, - m_listCtrlRxInDevices, - g_soundCard1InDeviceNum); - - rxOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxOut, - m_listCtrlRxOutDevices, - g_soundCard1OutDeviceNum); - - if ((rxInAudioDeviceNum != -1) && (rxInAudioDeviceNum != -1)) { - m_cbSampleRateRxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - m_cbSampleRateRxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - } - - m_textCtrlTxIn ->SetValue("none"); txInAudioDeviceNum = -1; - m_textCtrlTxOut->SetValue("none"); txOutAudioDeviceNum = -1; - } - - if (g_nSoundCards == 2) { - - rxInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxIn, - m_listCtrlRxInDevices, - g_soundCard1InDeviceNum); - - rxOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxOut, - m_listCtrlRxOutDevices, - g_soundCard2OutDeviceNum); - - txInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlTxIn, - m_listCtrlTxInDevices, - g_soundCard2InDeviceNum); - - txOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlTxOut, - m_listCtrlTxOutDevices, - g_soundCard1OutDeviceNum); - - if ((rxInAudioDeviceNum != -1) && (txOutAudioDeviceNum != -1)) { - m_cbSampleRateRxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - m_cbSampleRateTxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - } - - if ((txInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1)) { - m_cbSampleRateTxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard2SampleRate)); - m_cbSampleRateRxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard2SampleRate)); - } - } - printf(" rxInAudioDeviceNum: %d\n rxOutAudioDeviceNum: %d\n txInAudioDeviceNum: %d\n txOutAudioDeviceNum: %d\n", - rxInAudioDeviceNum, rxOutAudioDeviceNum, txInAudioDeviceNum, txOutAudioDeviceNum); - } - - if(inout == EXCHANGE_DATA_OUT) - { - int valid_one_card_config = 0; - int valid_two_card_config = 0; - wxString sampleRate1, sampleRate2; - - printf("EXCHANGE_DATA_OUT:\n"); - printf(" rxInAudioDeviceNum: %d\n rxOutAudioDeviceNum: %d\n txInAudioDeviceNum: %d\n txOutAudioDeviceNum: %d\n", - rxInAudioDeviceNum, rxOutAudioDeviceNum, txInAudioDeviceNum, txOutAudioDeviceNum); - - // --------------------------------------------------------------- - // check we have a valid 1 or 2 sound card configuration - // --------------------------------------------------------------- - - // one sound card config, tx device numbers should be set to -1 - - if ((rxInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1) && - (txInAudioDeviceNum == -1) && (txOutAudioDeviceNum == -1)) { - - valid_one_card_config = 1; - - // in and out sample rate must be the same, as there is one callback - - sampleRate1 = m_cbSampleRateRxIn->GetValue(); - if (!sampleRate1.IsSameAs(m_cbSampleRateRxOut->GetValue())) { - wxMessageBox(wxT("With a single sound card the Sample Rate of " - "From Radio and To Speaker/Headphones must be the same."), wxT(""), wxOK); - return -1; - } - } - - // two card configuration - - if ((rxInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1) && - (txInAudioDeviceNum != -1) && (txOutAudioDeviceNum != -1)) { - - valid_two_card_config = 1; - - // Check we haven't doubled up on sound devices - - if (rxInAudioDeviceNum == txInAudioDeviceNum) { - wxMessageBox(wxT("You must use different devices for From Radio and From Microphone"), wxT(""), wxOK); - return -1; - } - - if (rxOutAudioDeviceNum == txOutAudioDeviceNum) { - wxMessageBox(wxT("You must use different devices for To Radio and To Speaker/Headphones"), wxT(""), wxOK); - return -1; - } - - // Check sample rates for callback 1 devices are the same, - // as input and output are handled synchronously by one - // portaudio callback - - sampleRate1 = m_cbSampleRateRxIn->GetValue(); - if (!sampleRate1.IsSameAs(m_cbSampleRateTxOut->GetValue())) { - wxMessageBox(wxT("With two sound cards the Sample Rate " - "of From Radio and To Radio must be the same."), wxT(""), wxOK); - return -1; - } - - // check sample rate for callback 2 devices is the same - - sampleRate2 = m_cbSampleRateTxIn->GetValue(); - if (!sampleRate2.IsSameAs(m_cbSampleRateRxOut->GetValue())) { - wxMessageBox(wxT("With two sound cards the Sample Rate of " - "From Microphone and To Speaker/Headphones must be the same."), wxT(""), wxOK); - return -1; - } - - } - - printf(" valid_one_card_config: %d valid_two_card_config: %d\n", valid_one_card_config, valid_two_card_config); - - if (!valid_one_card_config && !valid_two_card_config) { - wxMessageBox(wxT("Invalid one or two sound card configuration"), wxT(""), wxOK); - return -1; - } - - // --------------------------------------------------------------- - // Map Rx/TX device numbers to sound card device numbers used - // in callbacks. Portaudio uses one callback per sound card so - // we have to be soundcard oriented at run time rather than - // Tx/Rx oriented as in this dialog. - // --------------------------------------------------------------- - g_nSoundCards = 0; - g_soundCard1InDeviceNum = g_soundCard1OutDeviceNum = g_soundCard2InDeviceNum = g_soundCard2OutDeviceNum = -1; - - if (valid_one_card_config) { - - // Only callback 1 used - - g_nSoundCards = 1; - g_soundCard1InDeviceNum = rxInAudioDeviceNum; - g_soundCard1OutDeviceNum = rxOutAudioDeviceNum; - g_soundCard1SampleRate = wxAtoi(sampleRate1); - } - - if (valid_two_card_config) { - g_nSoundCards = 2; - g_soundCard1InDeviceNum = rxInAudioDeviceNum; - g_soundCard1OutDeviceNum = txOutAudioDeviceNum; - g_soundCard1SampleRate = wxAtoi(sampleRate1); - g_soundCard2InDeviceNum = txInAudioDeviceNum; - g_soundCard2OutDeviceNum = rxOutAudioDeviceNum; - g_soundCard2SampleRate = wxAtoi(sampleRate2); - } - - printf(" g_nSoundCards: %d\n", g_nSoundCards); - printf(" g_soundCard1InDeviceNum: %d\n", g_soundCard1InDeviceNum); - printf(" g_soundCard1OutDeviceNum: %d\n", g_soundCard1OutDeviceNum); - printf(" g_soundCard1SampleRate: %d\n", g_soundCard1SampleRate); - printf(" g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - printf(" g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - printf(" g_soundCard2SampleRate: %d\n", g_soundCard2SampleRate); - - wxConfigBase *pConfig = wxConfigBase::Get(); - pConfig->Write(wxT("/Audio/soundCard1InDeviceNum"), g_soundCard1InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1OutDeviceNum"), g_soundCard1OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1SampleRate"), g_soundCard1SampleRate ); - - pConfig->Write(wxT("/Audio/soundCard2InDeviceNum"), g_soundCard2InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2OutDeviceNum"), g_soundCard2OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2SampleRate"), g_soundCard2SampleRate ); - - pConfig->Flush(); - delete wxConfigBase::Set((wxConfigBase *) NULL); - } - - return 0; -} - -//------------------------------------------------------------------------- -// buildListOfSupportedSampleRates() -//------------------------------------------------------------------------- -int AudioOptsDialog:: buildListOfSupportedSampleRates(wxComboBox *cbSampleRate, int devNum, int in_out) -{ - // every sound device has a different list of supported sample rates, so - // we work out which ones are supported and populate the list ctrl - - static double standardSampleRates[] = - { - 8000.0, 9600.0, - 11025.0, 12000.0, - 16000.0, 22050.0, - 24000.0, 32000.0, - 44100.0, 48000.0, - 88200.0, 96000.0, - 192000.0, -1 // negative terminated list - }; - - const PaDeviceInfo *deviceInfo; - PaStreamParameters inputParameters, outputParameters; - PaError err; - wxString str; - int i, numSampleRates; - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - printf("Pa_GetDeviceInfo(%d) failed!\n", devNum); - cbSampleRate->Clear(); - return 0; - } - - inputParameters.device = devNum; - inputParameters.channelCount = deviceInfo->maxInputChannels; - inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = 0; - inputParameters.hostApiSpecificStreamInfo = NULL; - - outputParameters.device = devNum; - outputParameters.channelCount = deviceInfo->maxOutputChannels; - outputParameters.sampleFormat = paInt16; - outputParameters.suggestedLatency = 0; - outputParameters.hostApiSpecificStreamInfo = NULL; - - cbSampleRate->Clear(); - //printf("devNum %d supports: ", devNum); - numSampleRates = 0; - for(i = 0; standardSampleRates[i] > 0; i++) - { - if (in_out == AUDIO_IN) - err = Pa_IsFormatSupported(&inputParameters, NULL, standardSampleRates[i]); - else - err = Pa_IsFormatSupported(NULL, &outputParameters, standardSampleRates[i]); - - if( err == paFormatIsSupported ) { - str.Printf("%i", (int)standardSampleRates[i]); - cbSampleRate->AppendString(str); - printf("%i ", (int)standardSampleRates[i]); - numSampleRates++; - } - } - printf("\n"); - - return numSampleRates; -} - -//------------------------------------------------------------------------- -// showAPIInfo() -//------------------------------------------------------------------------- -void AudioOptsDialog::showAPIInfo() -{ - wxString strval; - int apiVersion; - int apiCount = 0; - int numDevices = 0; - - strval = Pa_GetVersionText(); - m_textStringVer->SetLabel(strval); - - apiVersion = Pa_GetVersion(); - strval.Printf(wxT("%d"), apiVersion); - m_textIntVer->SetLabel(strval); - - apiCount = Pa_GetHostApiCount(); - strval.Printf(wxT("%d"), apiCount); - m_textAPICount->SetLabel(strval); - - numDevices = Pa_GetDeviceCount(); - strval.Printf(wxT("%d"), numDevices); - m_textCDevCount->SetLabel(strval); -} - -//------------------------------------------------------------------------- -// populateParams() -//------------------------------------------------------------------------- -void AudioOptsDialog::populateParams(AudioInfoDisplay ai) -{ - const PaDeviceInfo *deviceInfo = NULL; - wxListCtrl* ctrl = ai.m_listDevices; - int in_out = ai.direction; - long idx; - int numDevices; - wxListItem listItem; - wxString buf; - int devn; - int col = 0; - - numDevices = Pa_GetDeviceCount(); - - if(ctrl->GetColumnCount() > 0) - { - ctrl->ClearAll(); - } - - listItem.SetAlign(wxLIST_FORMAT_LEFT); - listItem.SetText(wxT("Device")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 300); - - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("ID")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 45); - - listItem.SetAlign(wxLIST_FORMAT_LEFT); - listItem.SetText(wxT("API")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - - if(in_out == AUDIO_IN) - { - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Default Sample Rate")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 160); - } - else if(in_out == AUDIO_OUT) - { - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Default Sample Rate")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 160); - } - - #ifdef LATENCY - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Min Latency")); - ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Max Latency")); - ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - #endif - - for(devn = 0; devn < numDevices; devn++) - { - buf.Printf(wxT("")); - deviceInfo = Pa_GetDeviceInfo(devn); - if( ((in_out == AUDIO_IN) && (deviceInfo->maxInputChannels > 0)) || - ((in_out == AUDIO_OUT) && (deviceInfo->maxOutputChannels > 0))) - { - col = 0; - buf.Printf(wxT("%s"), deviceInfo->name); - idx = ctrl->InsertItem(ctrl->GetItemCount(), buf); - col++; - - buf.Printf(wxT("%d"), devn); - ctrl->SetItem(idx, col++, buf); - - buf.Printf(wxT("%s"), Pa_GetHostApiInfo(deviceInfo->hostApi)->name); - ctrl->SetItem(idx, col++, buf); - - buf.Printf(wxT("%i"), (int)deviceInfo->defaultSampleRate); - ctrl->SetItem(idx, col++, buf); - - #ifdef LATENCY - if (in_out == AUDIO_IN) - buf.Printf(wxT("%8.4f"), deviceInfo->defaultLowInputLatency); - else - buf.Printf(wxT("%8.4f"), deviceInfo->defaultLowOutputLatency); - ctrl->SetItem(idx, col++, buf); - - if (in_out == AUDIO_IN) - buf.Printf(wxT("%8.4f"), deviceInfo->defaultHighInputLatency); - else - buf.Printf(wxT("%8.4f"), deviceInfo->defaultHighOutputLatency); - ctrl->SetItem(idx, col++, buf); - #endif - } - } - - // add "none" option at end - - buf.Printf(wxT("%s"), "none"); - idx = ctrl->InsertItem(ctrl->GetItemCount(), buf); -} - -//------------------------------------------------------------------------- -// OnDeviceSelect() -// -// helper function to set up "Device:" and "Sample Rate:" fields when -// we click on a line in the list of devices box -//------------------------------------------------------------------------- -void AudioOptsDialog::OnDeviceSelect(wxComboBox *cbSampleRate, - wxTextCtrl *textCtrl, - int *devNum, - wxListCtrl *listCtrlDevices, - int index, - int in_out) -{ - - wxString devName = listCtrlDevices->GetItemText(index, 0); - if (devName.IsSameAs("none")) { - *devNum = -1; - textCtrl->SetValue("none"); - } - else { - *devNum = wxAtoi(listCtrlDevices->GetItemText(index, 1)); - textCtrl->SetValue(devName + " (" + wxString::Format(wxT("%i"),*devNum) + ")"); - - int numSampleRates = buildListOfSupportedSampleRates(cbSampleRate, *devNum, in_out); - if (numSampleRates) { - wxString defSampleRate = listCtrlDevices->GetItemText(index, 3); - cbSampleRate->SetValue(defSampleRate); - } - else { - cbSampleRate->SetValue("None"); - } - } -} - -//------------------------------------------------------------------------- -// OnRxInDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxInDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateRxIn, - m_textCtrlRxIn, - &rxInAudioDeviceNum, - m_listCtrlRxInDevices, - evt.GetIndex(), - AUDIO_IN); -} - -//------------------------------------------------------------------------- -// OnRxOutDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxOutDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateRxOut, - m_textCtrlRxOut, - &rxOutAudioDeviceNum, - m_listCtrlRxOutDevices, - evt.GetIndex(), - AUDIO_OUT); -} - -//------------------------------------------------------------------------- -// OnTxInDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxInDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateTxIn, - m_textCtrlTxIn, - &txInAudioDeviceNum, - m_listCtrlTxInDevices, - evt.GetIndex(), - AUDIO_IN); -} - -//------------------------------------------------------------------------- -// OnTxOutDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxOutDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateTxOut, - m_textCtrlTxOut, - &txOutAudioDeviceNum, - m_listCtrlTxOutDevices, - evt.GetIndex(), - AUDIO_OUT); -} - -//------------------------------------------------------------------------- -// plotDeviceInputForAFewSecs() -// -// opens a record device and plots the input speech for a few seconds. This is "modal" using -// synchronous portaudio functions, so the GUI will not respond until after test sample has been -// taken -//------------------------------------------------------------------------- -void AudioOptsDialog::plotDeviceInputForAFewSecs(int devNum, PlotScalar *plotScalar) { - PaStreamParameters inputParameters; - const PaDeviceInfo *deviceInfo = NULL; - PaStream *stream = NULL; - PaError err; - short in48k_stereo_short[2*TEST_BUF_SIZE]; - short in48k_short[TEST_BUF_SIZE]; - short in8k_short[TEST_BUF_SIZE]; - int numDevices, nBufs, i, j, src_error,inputChannels; - float t; - SRC_STATE *src; - FIFO *fifo; - - // a basic sanity check - numDevices = Pa_GetDeviceCount(); - if (devNum >= numDevices) - return; - if (devNum < 0) - return; - printf("devNum %d\n", devNum); - - fifo = fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL); - src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL); - - // work out how many input channels this device supports. - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card "), wxT("Error"), wxOK); - return; - } - if (deviceInfo->maxInputChannels == 1) - inputChannels = 1; - else - inputChannels = 2; - - // open device - - inputParameters.device = devNum; - inputParameters.channelCount = inputChannels; - inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - nBufs = TEST_WAVEFORM_PLOT_TIME*TEST_FS/TEST_BUF_SIZE; - printf("inputChannels: %d nBufs %d\n", inputChannels, nBufs); - - err = Pa_OpenStream( - &stream, - &inputParameters, - NULL, - TEST_FS, - TEST_BUF_SIZE, - paClipOff, - NULL, // no callback, use blocking API - NULL ); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't initialise sound device."), wxT("Error"), wxOK); - return; - } - - err = Pa_StartStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't start sound device."), wxT("Error"), wxOK); - return; - } - - for(i=0, t=0.0; i TEST_DT) { - t -= TEST_DT; - short plotSamples[TEST_WAVEFORM_PLOT_BUF]; - if (fifo_read(fifo, plotSamples, TEST_WAVEFORM_PLOT_BUF)) - memset(plotSamples, 0, TEST_WAVEFORM_PLOT_BUF*sizeof(short)); - plotScalar->add_new_short_samples(0, plotSamples, TEST_WAVEFORM_PLOT_BUF, 32767); - plotScalar->Refresh(); - } - } - - err = Pa_StopStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't stop sound device."), wxT("Error"), wxOK); - return; - } - Pa_CloseStream(stream); - - fifo_destroy(fifo); - src_delete(src); -} - -//------------------------------------------------------------------------- -// plotDeviceOutputForAFewSecs() -// -// opens a play device and plays a tone for a few seconds. This is "modal" using -// synchronous portaudio functions, so the GUI will not respond until after test sample has been -// taken. Also plots a pretty picture like the record versions -//------------------------------------------------------------------------- -void AudioOptsDialog::plotDeviceOutputForAFewSecs(int devNum, PlotScalar *plotScalar) { - PaStreamParameters outputParameters; - const PaDeviceInfo *deviceInfo = NULL; - PaStream *stream = NULL; - PaError err; - short out48k_stereo_short[2*TEST_BUF_SIZE]; - short out48k_short[TEST_BUF_SIZE]; - short out8k_short[TEST_BUF_SIZE]; - int numDevices, nBufs, i, j, src_error, n, outputChannels; - float t; - SRC_STATE *src; - FIFO *fifo; - - // a basic sanity check - numDevices = Pa_GetDeviceCount(); - if (devNum >= numDevices) - return; - if (devNum < 0) - return; - - fifo = fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL); - src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL); - - // work out how many output channels this device supports. - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card "), wxT("Error"), wxOK); - return; - } - if (deviceInfo->maxOutputChannels == 1) - outputChannels = 1; - else - outputChannels = 2; - - printf("outputChannels: %d\n", outputChannels); - - outputParameters.device = devNum; - outputParameters.channelCount = outputChannels; - outputParameters.sampleFormat = paInt16; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - nBufs = TEST_WAVEFORM_PLOT_TIME*TEST_FS/TEST_BUF_SIZE; - - err = Pa_OpenStream( - &stream, - NULL, - &outputParameters, - TEST_FS, - TEST_BUF_SIZE, - paClipOff, - NULL, // no callback, use blocking API - NULL ); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't initialise sound device."), wxT("Error"), wxOK); - return; - } - - err = Pa_StartStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't start sound device."), wxT("Error"), wxOK); - return; - } - - for(i=0, t=0.0, n=0; i TEST_DT) { - t -= TEST_DT; - short plotSamples[TEST_WAVEFORM_PLOT_BUF]; - if (fifo_read(fifo, plotSamples, TEST_WAVEFORM_PLOT_BUF)) - memset(plotSamples, 0, TEST_WAVEFORM_PLOT_BUF*sizeof(short)); - plotScalar->add_new_short_samples(0, plotSamples, TEST_WAVEFORM_PLOT_BUF, 32767); - plotScalar->Refresh(); - } - } - - err = Pa_StopStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't stop sound device."), wxT("Error"), wxOK); - return; - } - Pa_CloseStream(stream); - - fifo_destroy(fifo); - src_delete(src); -} - -//------------------------------------------------------------------------- -// OnRxInTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxInTest(wxCommandEvent& event) -{ - plotDeviceInputForAFewSecs(rxInAudioDeviceNum, m_plotScalarRxIn); -} - -//------------------------------------------------------------------------- -// OnRxOutTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxOutTest(wxCommandEvent& event) -{ - plotDeviceOutputForAFewSecs(rxOutAudioDeviceNum, m_plotScalarRxOut); -} - -//------------------------------------------------------------------------- -// OnTxInTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxInTest(wxCommandEvent& event) -{ - plotDeviceInputForAFewSecs(txInAudioDeviceNum, m_plotScalarTxIn); -} - -//------------------------------------------------------------------------- -// OnTxOutTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxOutTest(wxCommandEvent& event) -{ - plotDeviceOutputForAFewSecs(txOutAudioDeviceNum, m_plotScalarTxOut); -} - -//------------------------------------------------------------------------- -// OnRefreshClick() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRefreshClick(wxCommandEvent& event) -{ - // restart portaudio, to re-sample available devices - - Pa_Terminate(); - Pa_Init(); - - m_notebook1->SetSelection(0); - showAPIInfo(); - populateParams(m_RxInDevices); - populateParams(m_RxOutDevices); - populateParams(m_TxInDevices); - populateParams(m_TxOutDevices); - - // some devices may have dissapeared, so possibily change sound - // card config - - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// OnApplyAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnApplyAudioParameters(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } -} - -//------------------------------------------------------------------------- -// OnCancelAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnCancelAudioParameters(wxCommandEvent& event) -{ - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } - EndModal(wxCANCEL); -} - -//------------------------------------------------------------------------- -// OnOkAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnOkAudioParameters(wxCommandEvent& event) -{ - int status = ExchangeData(EXCHANGE_DATA_OUT); - - // We only accept OK if config sucessful - - printf("status: %d m_isPaInitialized: %d\n", status, m_isPaInitialized); - if (status == 0) { - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - printf("terminated OK\n"); - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } - EndModal(wxOK); - } - -} diff --git a/freedv/branches/1.2/src/dlg_audiooptions.h b/freedv/branches/1.2/src/dlg_audiooptions.h deleted file mode 100644 index 5aa6741d..00000000 --- a/freedv/branches/1.2/src/dlg_audiooptions.h +++ /dev/null @@ -1,176 +0,0 @@ -//========================================================================= -// Name: AudioInfoDisplay.h -// Purpose: Declares simple wxWidgets application with GUI -// created using wxFormBuilder. -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================= -#ifndef __AudioOptsDialog__ -#define __AudioOptsDialog__ - -#include "fdmdv2_main.h" - -#define ID_AUDIO_OPTIONS 1000 -#define AUDIO_IN 0 -#define AUDIO_OUT 1 - -#include "portaudio.h" -#ifdef WIN32 -#if PA_USE_ASIO -#include "pa_asio.h" -#endif -#endif -#include "codec2_fifo.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// AudioInfoDisplay -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class AudioInfoDisplay -{ - public: - wxListCtrl* m_listDevices; - int direction; - wxTextCtrl* m_textDevice; - wxComboBox* m_cbSampleRate; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class AudioOptsDialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class AudioOptsDialog : public wxDialog -{ - private: - - protected: - PaError pa_err; - bool m_isPaInitialized; - - int rxInAudioDeviceNum; - int rxOutAudioDeviceNum; - int txInAudioDeviceNum; - int txOutAudioDeviceNum; - - void buildTestControls(PlotScalar **plotScalar, wxButton **btnTest, - wxPanel *parentPanel, wxBoxSizer *bSizer, wxString buttonLabel); - void plotDeviceInputForAFewSecs(int devNum, PlotScalar *plotScalar); - void plotDeviceOutputForAFewSecs(int devNum, PlotScalar *plotScalar); - - int buildListOfSupportedSampleRates(wxComboBox *cbSampleRate, int devNum, int in_out); - void populateParams(AudioInfoDisplay); - void showAPIInfo(); - int setTextCtrlIfDevNumValid(wxTextCtrl *textCtrl, wxListCtrl *listCtrl, int devNum); - void Pa_Init(void); - void OnDeviceSelect(wxComboBox *cbSampleRate, - wxTextCtrl *textCtrl, - int *devNum, - wxListCtrl *listCtrlDevices, - int index, - int in_out); - - AudioInfoDisplay m_RxInDevices; - AudioInfoDisplay m_RxOutDevices; - AudioInfoDisplay m_TxInDevices; - AudioInfoDisplay m_TxOutDevices; - wxPanel* m_panel1; - wxNotebook* m_notebook1; - - wxPanel* m_panelRx; - - wxListCtrl* m_listCtrlRxInDevices; - wxStaticText* m_staticText51; - wxTextCtrl* m_textCtrlRxIn; - wxStaticText* m_staticText6; - wxComboBox* m_cbSampleRateRxIn; - - wxButton* m_btnRxInTest; - PlotScalar* m_plotScalarRxIn; - - wxListCtrl* m_listCtrlRxOutDevices; - wxStaticText* m_staticText9; - wxTextCtrl* m_textCtrlRxOut; - wxStaticText* m_staticText10; - wxComboBox* m_cbSampleRateRxOut; - - wxButton* m_btnRxOutTest; - PlotScalar* m_plotScalarRxOut; - - wxPanel* m_panelTx; - - wxListCtrl* m_listCtrlTxInDevices; - wxStaticText* m_staticText12; - wxTextCtrl* m_textCtrlTxIn; - wxStaticText* m_staticText11; - wxComboBox* m_cbSampleRateTxIn; - - wxButton* m_btnTxInTest; - PlotScalar* m_plotScalarTxIn; - - wxListCtrl* m_listCtrlTxOutDevices; - wxStaticText* m_staticText81; - wxTextCtrl* m_textCtrlTxOut; - wxStaticText* m_staticText71; - wxComboBox* m_cbSampleRateTxOut; - - wxButton* m_btnTxOutTest; - PlotScalar* m_plotScalarTxOut; - - wxPanel* m_panelAPI; - - wxStaticText* m_staticText7; - wxStaticText* m_textStringVer; - wxStaticText* m_staticText8; - wxStaticText* m_textIntVer; - wxStaticText* m_staticText5; - wxStaticText* m_textCDevCount; - wxStaticText* m_staticText4; - wxStaticText* m_textAPICount; - wxButton* m_btnRefresh; - wxStdDialogButtonSizer* m_sdbSizer1; - wxButton* m_sdbSizer1OK; - wxButton* m_sdbSizer1Apply; - wxButton* m_sdbSizer1Cancel; - - // Virtual event handlers, overide them in your derived class - //virtual void OnActivateApp( wxActivateEvent& event ) { event.Skip(); } -// virtual void OnCloseFrame( wxCloseEvent& event ) { event.Skip(); } - - void OnRxInDeviceSelect( wxListEvent& event ); - - void OnRxInTest( wxCommandEvent& event ); - void OnRxOutTest( wxCommandEvent& event ); - void OnTxInTest( wxCommandEvent& event ); - void OnTxOutTest( wxCommandEvent& event ); - - void OnRxOutDeviceSelect( wxListEvent& event ); - void OnTxInDeviceSelect( wxListEvent& event ); - void OnTxOutDeviceSelect( wxListEvent& event ); - void OnRefreshClick( wxCommandEvent& event ); - void OnApplyAudioParameters( wxCommandEvent& event ); - void OnCancelAudioParameters( wxCommandEvent& event ); - void OnOkAudioParameters( wxCommandEvent& event ); - // Virtual event handlers, overide them in your derived class - void OnClose( wxCloseEvent& event ) { event.Skip(); } - void OnHibernate( wxActivateEvent& event ) { event.Skip(); } - void OnIconize( wxIconizeEvent& event ) { event.Skip(); } - void OnInitDialog( wxInitDialogEvent& event ); - - public: - - AudioOptsDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Audio Config"), const wxPoint& pos = wxPoint(1,1), const wxSize& size = wxSize( 800, 650 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~AudioOptsDialog(); - int ExchangeData(int inout); -}; -#endif //__AudioOptsDialog__ diff --git a/freedv/branches/1.2/src/dlg_filter.cpp b/freedv/branches/1.2/src/dlg_filter.cpp deleted file mode 100644 index 5a5294a9..00000000 --- a/freedv/branches/1.2/src/dlg_filter.cpp +++ /dev/null @@ -1,785 +0,0 @@ -//========================================================================== -// Name: dlg_filter.cpp -// Purpose: Dialog for controlling Codec audio filtering -// Date: Nov 25 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "dlg_filter.h" - -#define SLIDER_MAX 100 -#define SLIDER_LENGTH 100 - -#define FILTER_MIN_MAG_DB -20.0 -#define FILTER_MAX_MAG_DB 20.0 - -#define MAX_FREQ_BASS 600.00 -#define MAX_FREQ_TREBLE 3900.00 -#define MAX_FREQ_DEF 3000.00 - -#define MIN_GAIN -20 -#define MAX_GAIN 20 - -#define MAX_LOG10_Q 1.0 -#define MIN_LOG10_Q -1.0 - -// DFT parameters - -#define IMP_AMP 2000.0 // amplitude of impulse -#define NIMP 50 // number of samples in impulse response -#define F_STEP_DFT 10.0 // frequency steps to sample spectrum -#define F_MAG_N (int)(MAX_F_HZ/F_STEP_DFT) // number of frequency steps - -extern struct freedv *g_pfreedv; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class FilterDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -FilterDlg::FilterDlg(wxWindow* parent, bool running, bool *newMicInFilter, bool *newSpkOutFilter, - wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - m_running = running; - m_newMicInFilter = newMicInFilter; - m_newSpkOutFilter = newSpkOutFilter; - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* bSizer30; - bSizer30 = new wxBoxSizer(wxVERTICAL); - - // LPC Post Filter -------------------------------------------------------- - - wxStaticBoxSizer* lpcpfs = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("LPC Post Filter")), wxHORIZONTAL); - - wxBoxSizer* left = new wxBoxSizer(wxVERTICAL); - - m_codec2LPCPostFilterEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - left->Add(m_codec2LPCPostFilterEnable); - - m_codec2LPCPostFilterBassBoost = new wxCheckBox(this, wxID_ANY, _("0-1 kHz 3dB Boost"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - left->Add(m_codec2LPCPostFilterBassBoost); - lpcpfs->Add(left, 0, wxALL, 5); - - newLPCPFControl(&m_codec2LPCPostFilterBeta, &m_staticTextBeta, lpcpfs, "Beta"); - newLPCPFControl(&m_codec2LPCPostFilterGamma, &m_staticTextGamma, lpcpfs, "Gamma"); - - m_LPCPostFilterDefault = new wxButton(this, wxID_ANY, wxT("Default")); - lpcpfs->Add(m_LPCPostFilterDefault, 0, wxALL|wxALIGN_CENTRE_HORIZONTAL|wxALIGN_CENTRE_VERTICAL, 5); - - bSizer30->Add(lpcpfs, 0, wxALL, 0); - - // Speex pre-processor -------------------------------------------------- - - wxStaticBoxSizer* sbSizer_speexpp; - wxStaticBox *sb_speexpp = new wxStaticBox(this, wxID_ANY, _("Speex Mic Audio Pre-Processor")); - sbSizer_speexpp = new wxStaticBoxSizer(sb_speexpp, wxVERTICAL); - - m_ckboxSpeexpp = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_speexpp->SetToolTip(_("Enable noise supression, dereverberation, AGC of mic signal")); - sbSizer_speexpp->Add(m_ckboxSpeexpp, wxALIGN_LEFT, 2); - - bSizer30->Add(sbSizer_speexpp, 0, wxALL, 0); - - // EQ Filters ----------------------------------------------------------- - - wxStaticBoxSizer* eqMicInSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Mic In Equaliser")), wxVERTICAL); - wxBoxSizer* eqMicInSizer1 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* eqMicInSizer2 = new wxBoxSizer(wxHORIZONTAL); - - m_MicInBass = newEQ(eqMicInSizer1, "Bass" , MAX_FREQ_BASS, disableQ); - m_MicInTreble = newEQ(eqMicInSizer1, "Treble", MAX_FREQ_TREBLE, disableQ); - eqMicInSizer->Add(eqMicInSizer1); - - m_MicInEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - eqMicInSizer2->Add(m_MicInEnable,0,wxALIGN_CENTRE_VERTICAL|wxRIGHT,10); - m_MicInMid = newEQ(eqMicInSizer2, "Mid" , MAX_FREQ_DEF, enableQ); - m_MicInDefault = new wxButton(this, wxID_ANY, wxT("Default")); - eqMicInSizer2->Add(m_MicInDefault,0,wxALIGN_CENTRE_VERTICAL|wxLEFT,20); - eqMicInSizer->Add(eqMicInSizer2); - - wxStaticBoxSizer* eqSpkOutSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Speaker Out Equaliser")), wxVERTICAL); - wxBoxSizer* eqSpkOutSizer1 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* eqSpkOutSizer2 = new wxBoxSizer(wxHORIZONTAL); - - m_SpkOutBass = newEQ(eqSpkOutSizer1, "Bass" , MAX_FREQ_BASS, disableQ); - m_SpkOutTreble = newEQ(eqSpkOutSizer1, "Treble", MAX_FREQ_TREBLE, disableQ); - eqSpkOutSizer->Add(eqSpkOutSizer1); - - m_SpkOutEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - eqSpkOutSizer2->Add(m_SpkOutEnable,0,wxALIGN_CENTRE_VERTICAL|wxRIGHT,10); - m_SpkOutMid = newEQ(eqSpkOutSizer2, "Mid" , MAX_FREQ_DEF, enableQ); - m_SpkOutDefault = new wxButton(this, wxID_ANY, wxT("Default")); - eqSpkOutSizer2->Add(m_SpkOutDefault,0,wxALIGN_CENTRE_VERTICAL|wxLEFT,20); - eqSpkOutSizer->Add(eqSpkOutSizer2); - - bSizer30->Add(eqMicInSizer, 0, wxALL, 0); - bSizer30->Add(eqSpkOutSizer, 0, wxALL, 0); - - // Storgage for spectrum magnitude plots ------------------------------------ - - m_MicInMagdB = new float[F_MAG_N]; - for(int i=0; iSetFont(wxFont(8, 70, 90, 90, false, wxEmptyString)); - - bSizer30->Add(m_auiNotebook, 0, wxEXPAND|wxALL, 3); - - m_MicInFreqRespPlot = new PlotSpectrum((wxFrame*) m_auiNotebook, m_MicInMagdB, F_MAG_N, FILTER_MIN_MAG_DB, FILTER_MAX_MAG_DB); - m_auiNotebook->AddPage(m_MicInFreqRespPlot, _("Microphone In Equaliser")); - - m_SpkOutFreqRespPlot = new PlotSpectrum((wxFrame*)m_auiNotebook, m_SpkOutMagdB, F_MAG_N, FILTER_MIN_MAG_DB, FILTER_MAX_MAG_DB); - m_auiNotebook->AddPage(m_SpkOutFreqRespPlot, _("Speaker Out Equaliser")); - - // OK - Cancel buttons at the bottom -------------------------- - - wxBoxSizer* bSizer31 = new wxBoxSizer(wxHORIZONTAL); - - m_sdbSizer5OK = new wxButton(this, wxID_OK); - bSizer31->Add(m_sdbSizer5OK, 0, wxALL, 2); - - m_sdbSizer5Cancel = new wxButton(this, wxID_CANCEL); - bSizer31->Add(m_sdbSizer5Cancel, 0, wxALL, 2); - - bSizer30->Add(bSizer31, 0, wxALIGN_RIGHT|wxALL, 0); - - this->SetSizer(bSizer30); - this->Layout(); - - this->Centre(wxBOTH); - - // Connect Events ------------------------------------------------------- - - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(FilterDlg::OnInitDialog)); - - m_codec2LPCPostFilterEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnEnable), NULL, this); - m_codec2LPCPostFilterBassBoost->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnBassBoost), NULL, this); - m_codec2LPCPostFilterBeta->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnBetaScroll), NULL, this); - m_codec2LPCPostFilterGamma->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnGammaScroll), NULL, this); - m_LPCPostFilterDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnLPCPostFilterDefault), NULL, this); - - m_ckboxSpeexpp->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpeexppEnable), NULL, this); - - m_MicInBass.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassFreqScroll), NULL, this); - m_MicInBass.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassGainScroll), NULL, this); - m_MicInTreble.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleFreqScroll), NULL, this); - m_MicInTreble.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleGainScroll), NULL, this); - m_MicInMid.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidFreqScroll), NULL, this); - m_MicInMid.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidGainScroll), NULL, this); - m_MicInMid.sliderQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidQScroll), NULL, this); - m_MicInEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnMicInEnable), NULL, this); - m_MicInDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnMicInDefault), NULL, this); - - m_SpkOutBass.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassFreqScroll), NULL, this); - m_SpkOutBass.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassGainScroll), NULL, this); - m_SpkOutTreble.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleFreqScroll), NULL, this); - m_SpkOutTreble.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleGainScroll), NULL, this); - m_SpkOutMid.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidFreqScroll), NULL, this); - m_SpkOutMid.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidGainScroll), NULL, this); - m_SpkOutMid.sliderQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidQScroll), NULL, this); - m_SpkOutEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpkOutEnable), NULL, this); - m_SpkOutDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnSpkOutDefault), NULL, this); - - m_sdbSizer5Cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnCancel), NULL, this); - m_sdbSizer5OK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnOK), NULL, this); - -} - -//------------------------------------------------------------------------- -// ~FilterDlg() -//------------------------------------------------------------------------- -FilterDlg::~FilterDlg() -{ - delete m_MicInMagdB; - delete m_SpkOutMagdB; - - // Disconnect Events - - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(FilterDlg::OnInitDialog)); - - m_codec2LPCPostFilterEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnEnable), NULL, this); - m_codec2LPCPostFilterBassBoost->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnBassBoost), NULL, this); - m_codec2LPCPostFilterBeta->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnBetaScroll), NULL, this); - m_codec2LPCPostFilterGamma->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnGammaScroll), NULL, this); - m_LPCPostFilterDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnLPCPostFilterDefault), NULL, this); - - m_MicInBass.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassFreqScroll), NULL, this); - m_MicInBass.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassGainScroll), NULL, this); - m_MicInTreble.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleFreqScroll), NULL, this); - m_MicInTreble.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleGainScroll), NULL, this); - m_MicInMid.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidFreqScroll), NULL, this); - m_MicInMid.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidGainScroll), NULL, this); - m_MicInMid.sliderQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidQScroll), NULL, this); - m_MicInEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnMicInEnable), NULL, this); - m_MicInDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnMicInDefault), NULL, this); - - m_SpkOutBass.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassFreqScroll), NULL, this); - m_SpkOutBass.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassGainScroll), NULL, this); - m_SpkOutTreble.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleFreqScroll), NULL, this); - m_SpkOutTreble.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleGainScroll), NULL, this); - m_SpkOutMid.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidFreqScroll), NULL, this); - m_SpkOutMid.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidGainScroll), NULL, this); - m_SpkOutMid.sliderQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidQScroll), NULL, this); - m_SpkOutEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpkOutEnable), NULL, this); - m_SpkOutDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnSpkOutDefault), NULL, this); - - m_sdbSizer5Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnCancel), NULL, this); - m_sdbSizer5OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnOK), NULL, this); -} - -void FilterDlg::newLPCPFControl(wxSlider **slider, wxStaticText **stValue, wxSizer *s, wxString controlName) -{ - wxBoxSizer *bs = new wxBoxSizer(wxHORIZONTAL); - - wxStaticText* st = new wxStaticText(this, wxID_ANY, controlName, wxDefaultPosition, wxSize(70,-1), wxALIGN_RIGHT); - bs->Add(st, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 2); - - *slider = new wxSlider(this, wxID_ANY, 0, 0, SLIDER_MAX, wxDefaultPosition, wxSize(SLIDER_LENGTH,wxDefaultCoord)); - bs->Add(*slider, 1, wxALIGN_CENTER_VERTICAL|wxALL, 2); - - *stValue = new wxStaticText(this, wxID_ANY, wxT("0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - bs->Add(*stValue, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL, 2); - - s->Add(bs, 0); -} - -void FilterDlg::newEQControl(wxSlider** slider, wxStaticText** value, wxStaticBoxSizer *bs, wxString controlName) -{ - wxStaticText* label = new wxStaticText(this, wxID_ANY, controlName, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); - bs->Add(label, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 0); - - *slider = new wxSlider(this, wxID_ANY, 0, 0, SLIDER_MAX, wxDefaultPosition, wxSize(SLIDER_LENGTH,wxDefaultCoord)); - bs->Add(*slider, 1, wxALIGN_CENTER_VERTICAL|wxALL, 0); - - *value = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(40,-1), wxALIGN_LEFT); - bs->Add(*value, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxRIGHT, 5); -} - -EQ FilterDlg::newEQ(wxSizer *bs, wxString eqName, float maxFreqHz, bool enableQ) -{ - EQ eq; - - wxStaticBoxSizer *bsEQ = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, eqName), wxHORIZONTAL); - - newEQControl(&eq.sliderFreq, &eq.valueFreq, bsEQ, "Freq"); - eq.maxFreqHz = maxFreqHz; - eq.sliderFreqId = eq.sliderFreq->GetId(); - - newEQControl(&eq.sliderGain, &eq.valueGain, bsEQ, "Gain"); - if (enableQ) - newEQControl(&eq.sliderQ, &eq.valueQ, bsEQ, "Q"); - else - eq.sliderQ = NULL; - - bs->Add(bsEQ); - - return eq; -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void FilterDlg::ExchangeData(int inout, bool storePersistent) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - if(inout == EXCHANGE_DATA_IN) - { - // LPC Post filter - - m_codec2LPCPostFilterEnable->SetValue(wxGetApp().m_codec2LPCPostFilterEnable); - m_codec2LPCPostFilterBassBoost->SetValue(wxGetApp().m_codec2LPCPostFilterBassBoost); - m_beta = wxGetApp().m_codec2LPCPostFilterBeta; setBeta(); - m_gamma = wxGetApp().m_codec2LPCPostFilterGamma; setGamma(); - - // Speex Pre-Processor - - m_ckboxSpeexpp->SetValue(wxGetApp().m_speexpp_enable); - - // Mic In Equaliser - - m_MicInBass.freqHz = wxGetApp().m_MicInBassFreqHz; - m_MicInBass.freqHz = limit(m_MicInBass.freqHz, 1.0, MAX_FREQ_BASS); - setFreq(&m_MicInBass); - m_MicInBass.gaindB = wxGetApp().m_MicInBassGaindB; - m_MicInBass.gaindB = limit(m_MicInBass.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInBass); - - m_MicInTreble.freqHz = wxGetApp().m_MicInTrebleFreqHz; - m_MicInTreble.freqHz = limit(m_MicInTreble.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_MicInTreble); - m_MicInTreble.gaindB = wxGetApp().m_MicInTrebleGaindB; - m_MicInTreble.gaindB = limit(m_MicInTreble.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInTreble); - - m_MicInMid.freqHz = wxGetApp().m_MicInMidFreqHz; - m_MicInMid.freqHz = limit(m_MicInMid.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_MicInMid); - m_MicInMid.gaindB = wxGetApp().m_MicInMidGaindB; - m_MicInMid.gaindB = limit(m_MicInMid.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInMid); - m_MicInMid.Q = wxGetApp().m_MicInMidQ; - m_MicInMid.Q = limit(m_MicInMid.Q, pow(10.0,MIN_LOG10_Q), pow(10.0, MAX_LOG10_Q)); - setQ(&m_MicInMid); - - m_MicInEnable->SetValue(wxGetApp().m_MicInEQEnable); - - plotMicInFilterSpectrum(); - - // Spk Out Equaliser - - m_SpkOutBass.freqHz = wxGetApp().m_SpkOutBassFreqHz; - m_SpkOutBass.freqHz = limit(m_SpkOutBass.freqHz, 1.0, MAX_FREQ_BASS); - setFreq(&m_SpkOutBass); - m_SpkOutBass.gaindB = wxGetApp().m_SpkOutBassGaindB; - m_SpkOutBass.gaindB = limit(m_SpkOutBass.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutBass); - - m_SpkOutTreble.freqHz = wxGetApp().m_SpkOutTrebleFreqHz; - m_SpkOutTreble.freqHz = limit(m_SpkOutTreble.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_SpkOutTreble); - m_SpkOutTreble.gaindB = wxGetApp().m_SpkOutTrebleGaindB; - m_SpkOutTreble.gaindB = limit(m_SpkOutTreble.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutTreble); - - m_SpkOutMid.freqHz = wxGetApp().m_SpkOutMidFreqHz; - m_SpkOutMid.freqHz = limit(m_SpkOutMid.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_SpkOutMid); - m_SpkOutMid.gaindB = wxGetApp().m_SpkOutMidGaindB; - m_SpkOutMid.gaindB = limit(m_SpkOutMid.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutMid); - m_SpkOutMid.Q = wxGetApp().m_SpkOutMidQ; - m_SpkOutMid.Q = limit(m_SpkOutMid.Q, pow(10.0,MIN_LOG10_Q), pow(10.0, MAX_LOG10_Q)); - setQ(&m_SpkOutMid); - - m_SpkOutEnable->SetValue(wxGetApp().m_SpkOutEQEnable); - - plotSpkOutFilterSpectrum(); - } - if(inout == EXCHANGE_DATA_OUT) - { - // LPC Post filter - - wxGetApp().m_codec2LPCPostFilterEnable = m_codec2LPCPostFilterEnable->GetValue(); - wxGetApp().m_codec2LPCPostFilterBassBoost = m_codec2LPCPostFilterBassBoost->GetValue(); - wxGetApp().m_codec2LPCPostFilterBeta = m_beta; - wxGetApp().m_codec2LPCPostFilterGamma = m_gamma; - - // Speex Pre-Processor - - wxGetApp().m_speexpp_enable = m_ckboxSpeexpp->GetValue(); - - // Mic In Equaliser - - wxGetApp().m_MicInBassFreqHz = m_MicInBass.freqHz; - wxGetApp().m_MicInBassGaindB = m_MicInBass.gaindB; - - wxGetApp().m_MicInTrebleFreqHz = m_MicInTreble.freqHz; - wxGetApp().m_MicInTrebleGaindB = m_MicInTreble.gaindB; - - wxGetApp().m_MicInMidFreqHz = m_MicInMid.freqHz; - wxGetApp().m_MicInMidGaindB = m_MicInMid.gaindB; - wxGetApp().m_MicInMidQ = m_MicInMid.Q; - - // Spk Out Equaliser - - wxGetApp().m_SpkOutBassFreqHz = m_SpkOutBass.freqHz; - wxGetApp().m_SpkOutBassGaindB = m_SpkOutBass.gaindB; - - wxGetApp().m_SpkOutTrebleFreqHz = m_SpkOutTreble.freqHz; - wxGetApp().m_SpkOutTrebleGaindB = m_SpkOutTreble.gaindB; - - wxGetApp().m_SpkOutMidFreqHz = m_SpkOutMid.freqHz; - wxGetApp().m_SpkOutMidGaindB = m_SpkOutMid.gaindB; - wxGetApp().m_SpkOutMidQ = m_SpkOutMid.Q; - - if (storePersistent) { - pConfig->Write(wxT("/Filter/codec2LPCPostFilterEnable"), wxGetApp().m_codec2LPCPostFilterEnable); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterBassBoost"), wxGetApp().m_codec2LPCPostFilterBassBoost); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterBeta"), (int)(m_beta*100.0)); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterGamma"), (int)(m_gamma*100.0)); - - pConfig->Write(wxT("/Filter/speexpp_enable"), wxGetApp().m_speexpp_enable); - - pConfig->Write(wxT("/Filter/MicInBassFreqHz"), (int)m_MicInBass.freqHz); - pConfig->Write(wxT("/Filter/MicInBassGaindB"), (int)(10.0*m_MicInBass.gaindB)); - pConfig->Write(wxT("/Filter/MicInTrebleFreqHz"), (int)m_MicInTreble.freqHz); - pConfig->Write(wxT("/Filter/MicInTrebleGaindB"), (int)(10.0*m_MicInTreble.gaindB)); - pConfig->Write(wxT("/Filter/MicInMidFreqHz"), (int)m_MicInMid.freqHz); - pConfig->Write(wxT("/Filter/MicInMidGaindB"), (int)(10.0*m_MicInMid.gaindB)); - pConfig->Write(wxT("/Filter/MicInMidQ"), (int)(100.0*m_MicInMid.Q)); - - pConfig->Write(wxT("/Filter/SpkOutBassFreqHz"), (int)m_SpkOutBass.freqHz); - pConfig->Write(wxT("/Filter/SpkOutBassGaindB"), (int)(10.0*m_SpkOutBass.gaindB)); - pConfig->Write(wxT("/Filter/SpkOutTrebleFreqHz"), (int)m_SpkOutTreble.freqHz); - pConfig->Write(wxT("/Filter/SpkOutTrebleGaindB"), (int)(10.0*m_SpkOutTreble.gaindB)); - pConfig->Write(wxT("/Filter/SpkOutMidQ"), (int)(100.0*m_SpkOutMid.Q)); - pConfig->Write(wxT("/Filter/SpkOutMidFreqHz"), (int)m_SpkOutMid.freqHz); - pConfig->Write(wxT("/Filter/SpkOutMidGaindB"), (int)(10.0*m_SpkOutMid.gaindB)); - - pConfig->Flush(); - } - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -float FilterDlg::limit(float value, float min, float max) { - if (value < min) return min; - if (value > max) return max; - return value; -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void FilterDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnDefault() -//------------------------------------------------------------------------- - -void FilterDlg::OnLPCPostFilterDefault(wxCommandEvent& event) -{ - m_beta = CODEC2_LPC_PF_BETA; setBeta(); - m_gamma = CODEC2_LPC_PF_GAMMA; setGamma(); - m_codec2LPCPostFilterEnable->SetValue(true); - m_codec2LPCPostFilterBassBoost->SetValue(true); -} - -void FilterDlg::OnMicInDefault(wxCommandEvent& event) -{ - m_MicInBass.freqHz = 100.0; - m_MicInBass.gaindB = 0.0; - setFreq(&m_MicInBass); setGain(&m_MicInBass); - - m_MicInTreble.freqHz = 3000.0; - m_MicInTreble.gaindB = 0.0; - setFreq(&m_MicInTreble); setGain(&m_MicInTreble); - - m_MicInMid.freqHz = 1500.0; - m_MicInMid.gaindB = 0.0; - m_MicInMid.Q = 1.0; - setFreq(&m_MicInMid); setGain(&m_MicInMid); setQ(&m_MicInMid); - - plotMicInFilterSpectrum(); -} - -void FilterDlg::OnSpkOutDefault(wxCommandEvent& event) -{ - m_SpkOutBass.freqHz = 100.0; - m_SpkOutBass.gaindB = 0.0; - setFreq(&m_SpkOutBass); setGain(&m_SpkOutBass); - - m_SpkOutTreble.freqHz = 3000.0; - m_SpkOutTreble.gaindB = 0.0; - setFreq(&m_SpkOutTreble); setGain(&m_SpkOutTreble); - - m_SpkOutMid.freqHz = 1500.0; - m_SpkOutMid.gaindB = 0.0; - m_SpkOutMid.Q = 1.0; - setFreq(&m_SpkOutMid); setGain(&m_SpkOutMid); setQ(&m_SpkOutMid); - - plotSpkOutFilterSpectrum(); -} - -//------------------------------------------------------------------------- -// OnOK() -//------------------------------------------------------------------------- -void FilterDlg::OnOK(wxCommandEvent& event) -{ - //printf("FilterDlg::OnOK\n"); - ExchangeData(EXCHANGE_DATA_OUT, true); - this->EndModal(wxID_OK); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void FilterDlg::OnClose(wxCloseEvent& event) -{ - this->EndModal(wxID_OK); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void FilterDlg::OnInitDialog(wxInitDialogEvent& event) -{ - //printf("FilterDlg::OnInitDialog\n"); - ExchangeData(EXCHANGE_DATA_IN, false); - //printf("m_beta: %f\n", m_beta); -} - -void FilterDlg::setBeta(void) { - wxString buf; - buf.Printf(wxT("%3.2f"), m_beta); - m_staticTextBeta->SetLabel(buf); - int slider = (int)(m_beta*SLIDER_MAX + 0.5); - m_codec2LPCPostFilterBeta->SetValue(slider); -} - -void FilterDlg::setCodec2(void) { - if (m_running) { - codec2_set_lpc_post_filter(freedv_get_codec2(g_pfreedv), - m_codec2LPCPostFilterEnable->GetValue(), - m_codec2LPCPostFilterBassBoost->GetValue(), - m_beta, m_gamma); - } -} - -void FilterDlg::setGamma(void) { - wxString buf; - buf.Printf(wxT("%3.2f"), m_gamma); - m_staticTextGamma->SetLabel(buf); - int slider = (int)(m_gamma*SLIDER_MAX + 0.5); - m_codec2LPCPostFilterGamma->SetValue(slider); -} - -void FilterDlg::OnEnable(wxScrollEvent& event) { - setCodec2(); -} - -void FilterDlg::OnBassBoost(wxScrollEvent& event) { - setCodec2(); -} - -void FilterDlg::OnBetaScroll(wxScrollEvent& event) { - m_beta = (float)m_codec2LPCPostFilterBeta->GetValue()/SLIDER_MAX; - setBeta(); - setCodec2(); -} - -void FilterDlg::OnGammaScroll(wxScrollEvent& event) { - m_gamma = (float)m_codec2LPCPostFilterGamma->GetValue()/SLIDER_MAX; - setGamma(); - setCodec2(); -} - -// immediately change enable flags rather using ExchangeData() so we can switch on and off at run time - -void FilterDlg::OnSpeexppEnable(wxScrollEvent& event) { - wxGetApp().m_speexpp_enable = m_ckboxSpeexpp->GetValue(); -} - -void FilterDlg::OnMicInEnable(wxScrollEvent& event) { - wxGetApp().m_MicInEQEnable = m_MicInEnable->GetValue(); -} - -void FilterDlg::OnSpkOutEnable(wxScrollEvent& event) { - wxGetApp().m_SpkOutEQEnable = m_SpkOutEnable->GetValue(); - //printf("wxGetApp().m_SpkOutEQEnable: %d\n", wxGetApp().m_SpkOutEQEnable); -} - -void FilterDlg::setFreq(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%3.0f"), eq->freqHz); - eq->valueFreq->SetLabel(buf); - int slider = (int)((eq->freqHz/eq->maxFreqHz)*SLIDER_MAX + 0.5); - eq->sliderFreq->SetValue(slider); -} - -void FilterDlg::sliderToFreq(EQ *eq, bool micIn) -{ - eq->freqHz = ((float)eq->sliderFreq->GetValue()/SLIDER_MAX)*eq->maxFreqHz; - if (eq->freqHz < 1.0) eq->freqHz = 1.0; // sox doesn't like 0 Hz; - setFreq(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } -} - -void FilterDlg::setGain(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%3.1f"), eq->gaindB); - eq->valueGain->SetLabel(buf); - int slider = (int)(((eq->gaindB-MIN_GAIN)/(MAX_GAIN-MIN_GAIN))*SLIDER_MAX + 0.5); - eq->sliderGain->SetValue(slider); -} - -void FilterDlg::sliderToGain(EQ *eq, bool micIn) -{ - float range = MAX_GAIN-MIN_GAIN; - - eq->gaindB = MIN_GAIN + range*((float)eq->sliderGain->GetValue()/SLIDER_MAX); - //printf("gaindB: %f\n", eq->gaindB); - setGain(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } - -} - -void FilterDlg::setQ(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%2.1f"), eq->Q); - eq->valueQ->SetLabel(buf); - - float log10_range = MAX_LOG10_Q - MIN_LOG10_Q; - - int slider = (int)(((log10(eq->Q+1E-6)-MIN_LOG10_Q)/log10_range)*SLIDER_MAX + 0.5); - eq->sliderQ->SetValue(slider); -} - -void FilterDlg::sliderToQ(EQ *eq, bool micIn) -{ - float log10_range = MAX_LOG10_Q - MIN_LOG10_Q; - - float sliderNorm = (float)eq->sliderQ->GetValue()/SLIDER_MAX; - float log10Q = MIN_LOG10_Q + sliderNorm*(log10_range); - eq->Q = pow(10.0, log10Q); - //printf("log10Q: %f eq->Q: %f\n", log10Q, eq->Q); - setQ(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } -} - -void FilterDlg::plotMicInFilterSpectrum(void) { - plotFilterSpectrum(&m_MicInBass, &m_MicInMid, &m_MicInTreble, m_MicInFreqRespPlot, m_MicInMagdB); -} - -void FilterDlg::plotSpkOutFilterSpectrum(void) { - plotFilterSpectrum(&m_SpkOutBass, &m_SpkOutMid, &m_SpkOutTreble, m_SpkOutFreqRespPlot, m_SpkOutMagdB); -} - -void FilterDlg::adjRunTimeMicInFilter(void) { - // signal an adjustment in running filter coeffs - - if (m_running) { - ExchangeData(EXCHANGE_DATA_OUT, false); - *m_newMicInFilter = true; - } -} - -void FilterDlg::adjRunTimeSpkOutFilter(void) { - // signal an adjustment in running filter coeffs - - if (m_running) { - ExchangeData(EXCHANGE_DATA_OUT, false); - *m_newSpkOutFilter = true; - } -} - - -void FilterDlg::plotFilterSpectrum(EQ *eqBass, EQ *eqMid, EQ *eqTreble, PlotSpectrum* freqRespPlot, float *magdB) { - char *argBass[10]; - char *argTreble[10]; - char *argMid[10]; - char argstorage[10][80]; - float magBass[F_MAG_N]; - float magTreble[F_MAG_N]; - float magMid[F_MAG_N]; - int i; - - for(i=0; i<10; i++) { - argBass[i] = &argstorage[i][0]; - argTreble[i] = &argstorage[i][0]; - argMid[i] = &argstorage[i][0]; - } - sprintf(argBass[0], "bass"); - sprintf(argBass[1], "%f", eqBass->gaindB+1E-6); - sprintf(argBass[2], "%f", eqBass->freqHz); - - calcFilterSpectrum(magBass, 2, argBass); - - sprintf(argTreble[0], "treble"); - sprintf(argTreble[1], "%f", eqTreble->gaindB+1E-6); - sprintf(argTreble[2], "%f", eqTreble->freqHz); - - calcFilterSpectrum(magTreble, 2, argTreble); - - sprintf(argTreble[0], "equalizer"); - sprintf(argTreble[1], "%f", eqMid->freqHz); - sprintf(argTreble[2], "%f", eqMid->Q); - sprintf(argTreble[3], "%f", eqMid->gaindB+1E-6); - - calcFilterSpectrum(magMid, 3, argMid); - - for(i=0; im_newdata = true; - freqRespPlot->Refresh(); -} - -void FilterDlg::calcFilterSpectrum(float magdB[], int argc, char *argv[]) { - void *sbq; - short in[NIMP]; - short out[NIMP]; - COMP X[F_MAG_N]; - float f, w; - int i, k; - - // find impulse response ----------------------------------- - - for(i=0; i. -// -//========================================================================== - -#ifndef __FILTER_DIALOG__ -#define __FILTER_DIALOG__ - -#include "fdmdv2_main.h" - -enum {disableQ = false, enableQ = true}; - -typedef struct { - wxSlider *sliderFreq; - wxStaticText *valueFreq; - wxSlider *sliderGain; - wxStaticText *valueGain; - wxSlider *sliderQ; - wxStaticText *valueQ; - - int sliderFreqId; - int sliderGainId; - int sliderQId; - - float freqHz; - float gaindB; - float Q; - - float maxFreqHz; -} EQ; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class FilterDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class FilterDlg : public wxDialog -{ - public: - FilterDlg( wxWindow* parent, bool running, bool *newMicInFilter, bool *newSpkOutFilter, - wxWindowID id = wxID_ANY, const wxString& title = _("Filter"), - const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 800, 630 ), - long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~FilterDlg(); - - void ExchangeData(int inout, bool storePersistent); - - protected: - // Handlers for events. - void OnCancel(wxCommandEvent& event); - void OnOK(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void OnInitDialog(wxInitDialogEvent& event); - void OnLPCPostFilterDefault(wxCommandEvent& event); - - void OnBetaScroll(wxScrollEvent& event); - void OnGammaScroll(wxScrollEvent& event); - void OnEnable(wxScrollEvent& event); - void OnBassBoost(wxScrollEvent& event); - - void OnSpeexppEnable(wxScrollEvent& event); - - void OnMicInBassFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInBass, true); } - void OnMicInBassGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInBass, true); } - void OnMicInTrebleFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInTreble, true); } - void OnMicInTrebleGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInTreble, true); } - void OnMicInMidFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInMid, true); } - void OnMicInMidGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInMid, true); } - void OnMicInMidQScroll(wxScrollEvent& event) { sliderToQ(&m_MicInMid, true); } - void OnMicInEnable(wxScrollEvent& event); - void OnMicInDefault(wxCommandEvent& event); - - void OnSpkOutBassFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutBass, false); } - void OnSpkOutBassGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutBass, false); } - void OnSpkOutTrebleFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutTreble, false); } - void OnSpkOutTrebleGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutTreble, false); } - void OnSpkOutMidFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutMid, false); } - void OnSpkOutMidGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutMid, false); } - void OnSpkOutMidQScroll(wxScrollEvent& event) { sliderToQ(&m_SpkOutMid, false); } - void OnSpkOutEnable(wxScrollEvent& event); - void OnSpkOutDefault(wxCommandEvent& event); - - wxStaticText* m_staticText8; - wxCheckBox* m_codec2LPCPostFilterEnable; - wxStaticText* m_staticText9; - wxCheckBox* m_codec2LPCPostFilterBassBoost; - wxStaticText* m_staticText91; - wxSlider* m_codec2LPCPostFilterBeta; - wxStaticText* m_staticTextBeta; - wxStaticText* m_staticText911; - wxSlider* m_codec2LPCPostFilterGamma; - wxStaticText* m_staticTextGamma; - wxButton* m_LPCPostFilterDefault; - - wxCheckBox* m_ckboxSpeexpp; - - wxStdDialogButtonSizer* m_sdbSizer5; - wxButton* m_sdbSizer5OK; - wxButton* m_sdbSizer5Cancel; - PlotSpectrum* m_MicInFreqRespPlot; - PlotSpectrum* m_SpkOutFreqRespPlot; - - wxCheckBox* m_MicInEnable; - wxButton* m_MicInDefault; - wxCheckBox* m_SpkOutEnable; - wxButton* m_SpkOutDefault; - - float *m_MicInMagdB; - float *m_SpkOutMagdB; - - private: - bool m_running; - float m_beta; - float m_gamma; - - void setBeta(void); // sets slider and static text from m_beta - void setGamma(void); // sets slider and static text from m_gamma - void setCodec2(void); - - void newEQControl(wxSlider** slider, wxStaticText** value, wxStaticBoxSizer *bs, wxString controlName); - EQ newEQ(wxSizer *bs, wxString eqName, float maxFreqHz, bool enableQ); - void newLPCPFControl(wxSlider **slider, wxStaticText **stValue, wxSizer *sbs, wxString controlName); - wxAuiNotebook *m_auiNotebook; - void setFreq(EQ *eq); - void setGain(EQ *eq); - void setQ(EQ *eq); - void sliderToFreq(EQ *eq, bool micIn); - void sliderToGain(EQ *eq, bool micIn); - void sliderToQ(EQ *eq, bool micIn); - void plotFilterSpectrum(EQ *eqBass, EQ *eqMid, EQ* eqTreble, PlotSpectrum* freqRespPlot, float *magdB); - void calcFilterSpectrum(float magdB[], int arc, char *argv[]); - void plotMicInFilterSpectrum(void); - void plotSpkOutFilterSpectrum(void); - void adjRunTimeMicInFilter(void); - void adjRunTimeSpkOutFilter(void); - - EQ m_MicInBass; - EQ m_MicInMid; - EQ m_MicInTreble; - - EQ m_SpkOutBass; - EQ m_SpkOutMid; - EQ m_SpkOutTreble; - - float limit(float value, float min, float max); - - bool *m_newMicInFilter; - bool *m_newSpkOutFilter; - -}; - -#endif // __FILTER_DIALOG__ diff --git a/freedv/branches/1.2/src/dlg_options.cpp b/freedv/branches/1.2/src/dlg_options.cpp deleted file mode 100644 index 79b6c87f..00000000 --- a/freedv/branches/1.2/src/dlg_options.cpp +++ /dev/null @@ -1,616 +0,0 @@ -//========================================================================== -// Name: dlg_options.cpp -// Purpose: Dialog for controlling misc FreeDV options -// Date: May 24 2013 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "dlg_options.h" - -extern bool g_modal; -extern struct freedv *g_pfreedv; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class OptionsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -OptionsDlg::OptionsDlg(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* bSizer30; - bSizer30 = new wxBoxSizer(wxVERTICAL); - - //------------------------------ - // Txt Msg Text Box - //------------------------------ - - wxStaticBoxSizer* sbSizer_callSign; - wxStaticBox *sb_textMsg = new wxStaticBox(this, wxID_ANY, _("Txt Msg")); - sbSizer_callSign = new wxStaticBoxSizer(sb_textMsg, wxVERTICAL); - - m_txtCtrlCallSign = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - m_txtCtrlCallSign->SetToolTip(_("Txt Msg you can send along with Voice")); - sbSizer_callSign->Add(m_txtCtrlCallSign, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 3); - - bSizer30->Add(sbSizer_callSign,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //---------------------------------------------------------------------- - // Voice Keyer - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer28a = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Voice Keyer")), wxHORIZONTAL); - - wxStaticText *m_staticText28b = new wxStaticText(this, wxID_ANY, _("Wave File: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28b, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtCtrlVoiceKeyerWaveFile = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300,-1), 0); - m_txtCtrlVoiceKeyerWaveFile->SetToolTip(_("Wave file to play for Voice Keyer")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerWaveFile, 0, 0, 5); - - m_buttonChooseVoiceKeyerWaveFile = new wxButton(this, wxID_APPLY, _("Choose"), wxDefaultPosition, wxSize(-1,-1), 0); - staticBoxSizer28a->Add(m_buttonChooseVoiceKeyerWaveFile, 0, wxALIGN_CENTER_VERTICAL, 5); - - wxStaticText *m_staticText28c = new wxStaticText(this, wxID_ANY, _(" Rx Pause: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28c, 0, wxALIGN_CENTER_VERTICAL , 5); - m_txtCtrlVoiceKeyerRxPause = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0); - m_txtCtrlVoiceKeyerRxPause->SetToolTip(_("How long to wait in Rx mode before repeat")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerRxPause, 0, 0, 5); - - wxStaticText *m_staticText28d = new wxStaticText(this, wxID_ANY, _(" Repeats: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28d, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtCtrlVoiceKeyerRepeats = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0); - m_txtCtrlVoiceKeyerRepeats->SetToolTip(_("How long to wait in Rx mode before repeat")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerRepeats, 0, 0, 5); - - bSizer30->Add(staticBoxSizer28a,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - -#ifdef __WXMSW__ - //------------------------------ - // debug console, for WIndows build make console pop up for debug messages - //------------------------------ - - wxStaticBoxSizer* sbSizer_console; - wxStaticBox *sb_console = new wxStaticBox(this, wxID_ANY, _("Debug")); - sbSizer_console = new wxStaticBoxSizer(sb_console, wxHORIZONTAL); - - m_ckboxDebugConsole = new wxCheckBox(this, wxID_ANY, _("Show Console"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_console->Add(m_ckboxDebugConsole, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_console,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); -#endif - - //------------------------------ - // FreeDV 700 Options - //------------------------------ - - wxStaticBoxSizer* sbSizer_freedv700; - wxStaticBox *sb_freedv700 = new wxStaticBox(this, wxID_ANY, _("FreeDV 700 Options")); - sbSizer_freedv700 = new wxStaticBoxSizer(sb_freedv700, wxHORIZONTAL); - - m_ckboxFreeDV700txClip = new wxCheckBox(this, wxID_ANY, _("Clipping"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_freedv700->Add(m_ckboxFreeDV700txClip, 0, wxALIGN_LEFT, 0); - m_ckboxFreeDV700Combine = new wxCheckBox(this, wxID_ANY, _("Diversity Combine for plots"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_freedv700->Add(m_ckboxFreeDV700Combine, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_freedv700, 0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Half/Full duplex selection - //------------------------------ - - wxStaticBox *sb_duplex = new wxStaticBox(this, wxID_ANY, _("Half/Full Duplex Operation")); - wxStaticBoxSizer* sbSizer_duplex = new wxStaticBoxSizer(sb_duplex, wxHORIZONTAL); - m_ckHalfDuplex = new wxCheckBox(this, wxID_ANY, _("Half Duplex"), wxDefaultPosition, wxSize(-1,-1), 0); - sbSizer_duplex->Add(m_ckHalfDuplex, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5); - bSizer30->Add(sbSizer_duplex,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Test Frames/Channel simulation check box - //------------------------------ - - wxStaticBoxSizer* sbSizer_testFrames; - wxStaticBox *sb_testFrames = new wxStaticBox(this, wxID_ANY, _("Testing and Channel Simulation")); - sbSizer_testFrames = new wxStaticBoxSizer(sb_testFrames, wxHORIZONTAL); - - m_ckboxTestFrame = new wxCheckBox(this, wxID_ANY, _("Test Frames"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxTestFrame, 0, wxALIGN_LEFT, 0); - - m_ckboxChannelNoise = new wxCheckBox(this, wxID_ANY, _("Channel Noise SNR (dB):"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxChannelNoise, 0, wxALIGN_LEFT, 0); - m_txtNoiseSNR = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_testFrames->Add(m_txtNoiseSNR, 0, wxALIGN_LEFT, 0); - - m_ckboxAttnCarrierEn = new wxCheckBox(this, wxID_ANY, _("Attn Carrier Carrier:"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxAttnCarrierEn, 0, wxALIGN_LEFT, 0); - m_txtAttnCarrier = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_testFrames->Add(m_txtAttnCarrier, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_testFrames,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Interfering tone - //------------------------------ - - wxStaticBoxSizer* sbSizer_tone; - wxStaticBox *sb_tone = new wxStaticBox(this, wxID_ANY, _("Simulated Interference Tone")); - sbSizer_tone = new wxStaticBoxSizer(sb_tone, wxHORIZONTAL); - - m_ckboxTone = new wxCheckBox(this, wxID_ANY, _("Tone Freq (Hz):"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_tone->Add(m_ckboxTone, 0, wxALIGN_LEFT, 0); - m_txtToneFreqHz = new wxTextCtrl(this, wxID_ANY, "1000", wxDefaultPosition, wxSize(60,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_tone->Add(m_txtToneFreqHz, 0, wxALIGN_LEFT, 0); - wxStaticText *m_staticTextta = new wxStaticText(this, wxID_ANY, _(" Amplitude (pk): "), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_tone->Add(m_staticTextta, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtToneAmplitude = new wxTextCtrl(this, wxID_ANY, "1000", wxDefaultPosition, wxSize(60,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_tone->Add(m_txtToneAmplitude, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_tone,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - -#ifdef __EXPERIMENTAL_UDP__ - //------------------------------ - // Txt Encoding - //------------------------------ - - wxStaticBoxSizer* sbSizer_encoding = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Text Encoding")), wxHORIZONTAL); - -#ifdef SHORT_VARICODE - m_rb_textEncoding1 = new wxRadioButton( this, wxID_ANY, wxT("Long varicode"), wxDefaultPosition, wxDefaultSize, 0); - m_rb_textEncoding1->SetValue(true); - sbSizer_encoding->Add(m_rb_textEncoding1, 0, wxALIGN_LEFT|wxALL, 1); - m_rb_textEncoding2 = new wxRadioButton( this, wxID_ANY, wxT("Short Varicode"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_encoding->Add(m_rb_textEncoding2, 0, wxALIGN_LEFT|wxALL, 1); -#endif - - m_ckboxEnableChecksum = new wxCheckBox(this, wxID_ANY, _("Use Checksum on Rx"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_encoding->Add(m_ckboxEnableChecksum, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_encoding,0, wxALL|wxEXPAND, 3); - - //------------------------------ - // Event processing - //------------------------------ - - wxStaticBoxSizer* sbSizer_events; - wxStaticBox *sb_events = new wxStaticBox(this, wxID_ANY, _("Event Processing")); - sbSizer_events = new wxStaticBoxSizer(sb_events, wxVERTICAL); - - // event processing enable and spam timer - - wxStaticBoxSizer* sbSizer_events_top; - wxStaticBox* sb_events1 = new wxStaticBox(this, wxID_ANY, _("")); - sbSizer_events_top = new wxStaticBoxSizer(sb_events1, wxHORIZONTAL); - - m_ckbox_events = new wxCheckBox(this, wxID_ANY, _("Enable System Calls Syscall Spam Timer"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_events->SetToolTip(_("Enable processing of events and generation of system calls")); - sbSizer_events_top->Add(m_ckbox_events, 0, 0, 5); - m_txt_spam_timer = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - m_txt_spam_timer->SetToolTip(_("Many matching events can cause a flood of syscalls. Set minimum time (seconds) between syscalls for each event here")); - sbSizer_events_top->Add(m_txt_spam_timer, 0, 0, 5); - m_rb_spam_timer = new wxRadioButton( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - m_rb_spam_timer->SetForegroundColour( wxColour(0, 255, 0 ) ); - sbSizer_events_top->Add(m_rb_spam_timer, 0, 0, 10); - sbSizer_events->Add(sbSizer_events_top, 0, 0, 5); - - // list of regexps - - wxStaticBoxSizer* sbSizer_regexp = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Regular Expressions to Process Events")), wxHORIZONTAL); - m_txt_events_regexp_match = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,100), wxTE_MULTILINE); - m_txt_events_regexp_match->SetToolTip(_("Enter regular expressions to match events")); - sbSizer_regexp->Add(m_txt_events_regexp_match, 1, wxEXPAND, 5); - m_txt_events_regexp_replace = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,100), wxTE_MULTILINE); - m_txt_events_regexp_replace->SetToolTip(_("Enter regular expressions to replace events")); - sbSizer_regexp->Add(m_txt_events_regexp_replace, 1, wxEXPAND, 5); - sbSizer_events->Add(sbSizer_regexp, 1, wxEXPAND, 5); - - // log of events and responses - - wxStaticBoxSizer* sbSizer_event_log = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Log of Events and Responses")), wxVERTICAL); - wxBoxSizer* bSizer33 = new wxBoxSizer(wxHORIZONTAL); - m_txt_events_in = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,50), wxTE_MULTILINE | wxTE_READONLY); - bSizer33->Add(m_txt_events_in, 1, wxEXPAND, 5); - m_txt_events_out = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,50), wxTE_MULTILINE | wxTE_READONLY); - bSizer33->Add(m_txt_events_out, 1, wxEXPAND, 5); - sbSizer_event_log->Add(bSizer33, 1, wxEXPAND, 5); - sbSizer_events->Add(sbSizer_event_log, 1, wxEXPAND, 5); - - bSizer30->Add(sbSizer_events,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // UDP control port - //------------------------------ - - wxStaticBoxSizer* sbSizer_udp; - wxStaticBox* sb_udp = new wxStaticBox(this, wxID_ANY, _("UDP Control Port")); - sbSizer_udp = new wxStaticBoxSizer(sb_udp, wxHORIZONTAL); - m_ckbox_udp_enable = new wxCheckBox(this, wxID_ANY, _("Enable UDP Control Port UDP Port Number:"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_udp->SetToolTip(_("Enable control of FreeDV via UDP port")); - sbSizer_udp->Add(m_ckbox_udp_enable, 0, wxALIGN_CENTER_HORIZONTAL, 5); - m_txt_udp_port = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(50,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_udp->Add(m_txt_udp_port, 0, wxALIGN_CENTER_HORIZONTAL, 5); - - bSizer30->Add(sbSizer_udp,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); -#endif - - //------------------------------ - // OK - Cancel - Apply Buttons - //------------------------------ - - wxBoxSizer* bSizer31 = new wxBoxSizer(wxHORIZONTAL); - - m_sdbSizer5OK = new wxButton(this, wxID_OK); - bSizer31->Add(m_sdbSizer5OK, 0, wxALL, 2); - - m_sdbSizer5Cancel = new wxButton(this, wxID_CANCEL); - bSizer31->Add(m_sdbSizer5Cancel, 0, wxALL, 2); - - m_sdbSizer5Apply = new wxButton(this, wxID_APPLY); - bSizer31->Add(m_sdbSizer5Apply, 0, wxALL, 2); - - bSizer30->Add(bSizer31, 0, wxALIGN_CENTER, 0); - - this->SetSizer(bSizer30); - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - this->Layout(); - - this->Centre(wxBOTH); - - // Connect Events ------------------------------------------------------- - - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(OptionsDlg::OnInitDialog)); - - m_sdbSizer5OK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnOK), NULL, this); - m_sdbSizer5Cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnCancel), NULL, this); - m_sdbSizer5Apply->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnApply), NULL, this); - - m_ckboxTestFrame->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnTestFrame), NULL, this); - m_ckboxChannelNoise->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnChannelNoise), NULL, this); - m_ckboxAttnCarrierEn->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnAttnCarrierEn), NULL, this); - - m_ckboxFreeDV700txClip->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700txClip), NULL, this); - m_ckboxFreeDV700Combine->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700Combine), NULL, this); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnDebugConsole), NULL, this); -#endif - - m_buttonChooseVoiceKeyerWaveFile->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnChooseVoiceKeyerWaveFile), NULL, this); - - event_in_serial = 0; - event_out_serial = 0; -} - -//------------------------------------------------------------------------- -// ~OptionsDlg() -//------------------------------------------------------------------------- -OptionsDlg::~OptionsDlg() -{ - - // Disconnect Events - - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(OptionsDlg::OnInitDialog)); - - m_sdbSizer5OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnOK), NULL, this); - m_sdbSizer5Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnCancel), NULL, this); - m_sdbSizer5Apply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnApply), NULL, this); - - m_ckboxTestFrame->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnTestFrame), NULL, this); - m_ckboxChannelNoise->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnChannelNoise), NULL, this); - m_ckboxAttnCarrierEn->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnAttnCarrierEn), NULL, this); - - m_ckboxFreeDV700txClip->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700txClip), NULL, this); - m_ckboxFreeDV700Combine->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700Combine), NULL, this); - m_buttonChooseVoiceKeyerWaveFile->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnChooseVoiceKeyerWaveFile), NULL, this); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnDebugConsole), NULL, this); -#endif -} - - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void OptionsDlg::ExchangeData(int inout, bool storePersistent) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - - if(inout == EXCHANGE_DATA_IN) - { - m_txtCtrlCallSign->SetValue(wxGetApp().m_callSign); - - /* Voice Keyer */ - - m_txtCtrlVoiceKeyerWaveFile->SetValue(wxGetApp().m_txtVoiceKeyerWaveFile); - m_txtCtrlVoiceKeyerRxPause->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intVoiceKeyerRxPause)); - m_txtCtrlVoiceKeyerRepeats->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intVoiceKeyerRepeats)); - - m_ckHalfDuplex->SetValue(wxGetApp().m_boolHalfDuplex); - - m_ckboxTestFrame->SetValue(wxGetApp().m_testFrames); - - m_ckboxChannelNoise->SetValue(wxGetApp().m_channel_noise); - m_txtNoiseSNR->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_noise_snr)); - - m_ckboxTone->SetValue(wxGetApp().m_tone); - m_txtToneFreqHz->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_tone_freq_hz)); - m_txtToneAmplitude->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_tone_amplitude)); - - m_ckboxAttnCarrierEn->SetValue(wxGetApp().m_attn_carrier_en); - m_txtAttnCarrier->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_attn_carrier)); - -#ifdef __EXPERIMENTAL_UDP__ - m_ckbox_events->SetValue(wxGetApp().m_events); - m_txt_spam_timer->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_events_spam_timer)); - - m_txt_events_regexp_match->SetValue(wxGetApp().m_events_regexp_match); - m_txt_events_regexp_replace->SetValue(wxGetApp().m_events_regexp_replace); - - m_ckbox_udp_enable->SetValue(wxGetApp().m_udp_enable); - m_txt_udp_port->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_udp_port)); - -#ifdef SHORT_VARICODE - if (wxGetApp().m_textEncoding == 1) - m_rb_textEncoding1->SetValue(true); - if (wxGetApp().m_textEncoding == 2) - m_rb_textEncoding2->SetValue(true); -#endif - m_ckboxEnableChecksum->SetValue(wxGetApp().m_enable_checksum); -#endif - - m_ckboxFreeDV700txClip->SetValue(wxGetApp().m_FreeDV700txClip); - m_ckboxFreeDV700Combine->SetValue(wxGetApp().m_FreeDV700Combine); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->SetValue(wxGetApp().m_debug_console); -#endif - } - - if(inout == EXCHANGE_DATA_OUT) - { - wxGetApp().m_callSign = m_txtCtrlCallSign->GetValue(); - - wxGetApp().m_boolHalfDuplex = m_ckHalfDuplex->GetValue(); - pConfig->Write(wxT("/Rig/HalfDuplex"), wxGetApp().m_boolHalfDuplex); - - /* Voice Keyer */ - - wxGetApp().m_txtVoiceKeyerWaveFile = m_txtCtrlVoiceKeyerWaveFile->GetValue(); - pConfig->Write(wxT("/VoiceKeyer/WaveFile"), wxGetApp().m_txtVoiceKeyerWaveFile); - long tmp; - m_txtCtrlVoiceKeyerRxPause->GetValue().ToLong(&tmp); if (tmp < 0) tmp = 0; wxGetApp().m_intVoiceKeyerRxPause = (int)tmp; - pConfig->Write(wxT("/VoiceKeyer/RxPause"), wxGetApp().m_intVoiceKeyerRxPause); - m_txtCtrlVoiceKeyerRepeats->GetValue().ToLong(&tmp); - if (tmp < 0) tmp = 0; if (tmp > 100) tmp = 100; - wxGetApp().m_intVoiceKeyerRepeats = (int)tmp; - pConfig->Write(wxT("/VoiceKeyer/Repeats"), wxGetApp().m_intVoiceKeyerRepeats); - - wxGetApp().m_testFrames = m_ckboxTestFrame->GetValue(); - - wxGetApp().m_channel_noise = m_ckboxChannelNoise->GetValue(); - long noise_snr; - m_txtNoiseSNR->GetValue().ToLong(&noise_snr); - wxGetApp().m_noise_snr = (int)noise_snr; - - wxGetApp().m_tone = m_ckboxTone->GetValue(); - long tone_freq_hz, tone_amplitude; - m_txtToneFreqHz->GetValue().ToLong(&tone_freq_hz); - wxGetApp().m_tone_freq_hz = (int)tone_freq_hz; - m_txtToneAmplitude->GetValue().ToLong(&tone_amplitude); - wxGetApp().m_tone_amplitude = (int)tone_amplitude; - - wxGetApp().m_attn_carrier_en = m_ckboxAttnCarrierEn->GetValue(); - long attn_carrier; - m_txtAttnCarrier->GetValue().ToLong(&attn_carrier); - wxGetApp().m_attn_carrier = (int)attn_carrier; - -#ifdef __EXPERIMENTAL_UDP__ - wxGetApp().m_events = m_ckbox_events->GetValue(); - long spam_timer; - m_txt_spam_timer->GetValue().ToLong(&spam_timer); - wxGetApp().m_events_spam_timer = (int)spam_timer; - - // make sure regexp lists are terminated by a \n - - if (m_txt_events_regexp_match->GetValue().Last() != '\n') { - m_txt_events_regexp_match->SetValue(m_txt_events_regexp_match->GetValue()+'\n'); - } - if (m_txt_events_regexp_replace->GetValue().Last() != '\n') { - m_txt_events_regexp_replace->SetValue(m_txt_events_regexp_replace->GetValue()+'\n'); - } - wxGetApp().m_events_regexp_match = m_txt_events_regexp_match->GetValue(); - wxGetApp().m_events_regexp_replace = m_txt_events_regexp_replace->GetValue(); - - wxGetApp().m_udp_enable = m_ckbox_udp_enable->GetValue(); - long port; - m_txt_udp_port->GetValue().ToLong(&port); - wxGetApp().m_udp_port = (int)port; - -#ifdef SHORT_VARICODE - if (m_rb_textEncoding1->GetValue()) - wxGetApp().m_textEncoding = 1; - if (m_rb_textEncoding2->GetValue()) - wxGetApp().m_textEncoding = 2; -#endif - wxGetApp().m_enable_checksum = m_ckboxEnableChecksum->GetValue(); -#endif - - wxGetApp().m_FreeDV700txClip = m_ckboxFreeDV700txClip->GetValue(); - wxGetApp().m_FreeDV700Combine = m_ckboxFreeDV700Combine->GetValue(); - -#ifdef __WXMSW__ - wxGetApp().m_debug_console = m_ckboxDebugConsole->GetValue(); -#endif - - if (storePersistent) { - pConfig->Write(wxT("/Data/CallSign"), wxGetApp().m_callSign); -#ifdef SHORT_VARICODE - pConfig->Write(wxT("/Data/TextEncoding"), wxGetApp().m_textEncoding); -#endif - pConfig->Write(wxT("/Data/EnableChecksumOnMsgRx"), wxGetApp().m_enable_checksum); - - pConfig->Write(wxT("/Events/enable"), wxGetApp().m_events); - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - pConfig->Write(wxT("/Events/regexp_match"), wxGetApp().m_events_regexp_match); - pConfig->Write(wxT("/Events/regexp_replace"), wxGetApp().m_events_regexp_replace); - - pConfig->Write(wxT("/UDP/enable"), wxGetApp().m_udp_enable); - pConfig->Write(wxT("/UDP/port"), wxGetApp().m_udp_port); - - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - - pConfig->Write(wxT("/FreeDV700/txClip"), wxGetApp().m_FreeDV700txClip); - - pConfig->Write(wxT("/Noise/noise_snr"), wxGetApp().m_noise_snr); - -#ifdef __WXMSW__ - pConfig->Write(wxT("/Debug/console"), wxGetApp().m_debug_console); -#endif - - pConfig->Flush(); - } - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -//------------------------------------------------------------------------- -// OnOK() -//------------------------------------------------------------------------- -void OptionsDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT, true); - //this->EndModal(wxID_OK); - g_modal = false; - this->Show(false); -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void OptionsDlg::OnCancel(wxCommandEvent& event) -{ - //this->EndModal(wxID_CANCEL); - g_modal = false; - this->Show(false); -} - -//------------------------------------------------------------------------- -// OnApply() -//------------------------------------------------------------------------- -void OptionsDlg::OnApply(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT, true); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void OptionsDlg::OnInitDialog(wxInitDialogEvent& event) -{ - ExchangeData(EXCHANGE_DATA_IN, false); -} - -// immediately change flags rather using ExchangeData() so we can switch on and off at run time - -void OptionsDlg::OnTestFrame(wxScrollEvent& event) { - wxGetApp().m_testFrames = m_ckboxTestFrame->GetValue(); -} - -void OptionsDlg::OnChannelNoise(wxScrollEvent& event) { - wxGetApp().m_channel_noise = m_ckboxChannelNoise->GetValue(); -} - - -void OptionsDlg::OnChooseVoiceKeyerWaveFile(wxCommandEvent& event) { - wxFileDialog openFileDialog( - this, - wxT("Voice Keyer wave file"), - wxGetApp().m_txtVoiceKeyerWaveFilePath, - wxEmptyString, - wxT("WAV files (*.wav)|*.wav"), - wxFD_OPEN - ); - if(openFileDialog.ShowModal() == wxID_CANCEL) { - return; // the user changed their mind... - } - - wxString fileName, extension; - wxGetApp().m_txtVoiceKeyerWaveFile = openFileDialog.GetPath(); - wxFileName::SplitPath(wxGetApp().m_txtVoiceKeyerWaveFile, &wxGetApp().m_txtVoiceKeyerWaveFilePath, &fileName, &extension); - m_txtCtrlVoiceKeyerWaveFile->SetValue(wxGetApp().m_txtVoiceKeyerWaveFile); -} - -// Run time update of carrier amplitude attenuation - -void OptionsDlg::OnAttnCarrierEn(wxScrollEvent& event) { - long attn_carrier; - m_txtAttnCarrier->GetValue().ToLong(&attn_carrier); - wxGetApp().m_attn_carrier = (int)attn_carrier; - - /* uncheck -> checked, attenuate selected carrier */ - - if (m_ckboxAttnCarrierEn->GetValue() && !wxGetApp().m_attn_carrier_en) { - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C) { - freedv_set_carrier_ampl(g_pfreedv, wxGetApp().m_attn_carrier, 0.25); - } else { - wxMessageBox("Carrier attenuation feature only works on 700C", wxT("Warning"), wxOK | wxICON_WARNING, this); - } - } - - /* checked -> unchecked, reset selected carrier */ - - if (!m_ckboxAttnCarrierEn->GetValue() && wxGetApp().m_attn_carrier_en) { - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C) { - freedv_set_carrier_ampl(g_pfreedv, wxGetApp().m_attn_carrier, 1.0); - } - } - - wxGetApp().m_attn_carrier_en = m_ckboxAttnCarrierEn->GetValue(); -} - -void OptionsDlg::OnFreeDV700txClip(wxScrollEvent& event) { - wxGetApp().m_FreeDV700txClip = m_ckboxFreeDV700txClip->GetValue(); -} - -void OptionsDlg::OnFreeDV700Combine(wxScrollEvent& event) { - wxGetApp().m_FreeDV700Combine = m_ckboxFreeDV700Combine->GetValue(); -} - -void OptionsDlg::updateEventLog(wxString event_in, wxString event_out) { - wxString event_in_with_serial, event_out_with_serial; - event_in_with_serial.Printf(_T("[%d] %s"), event_in_serial++, event_in); - event_out_with_serial.Printf(_T("[%d] %s"), event_out_serial++, event_out); - - m_txt_events_in->AppendText(event_in_with_serial+"\n"); - m_txt_events_out->AppendText(event_out_with_serial+"\n"); -} - - -void OptionsDlg::OnDebugConsole(wxScrollEvent& event) { - wxGetApp().m_debug_console = m_ckboxDebugConsole->GetValue(); -#ifdef __WXMSW__ - // somewhere to send printfs while developing, causes conmsole to pop up on Windows - if (wxGetApp().m_debug_console) { - int ret = AllocConsole(); - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - fprintf(stderr, "AllocConsole: %d m_debug_console: %d\n", ret, wxGetApp().m_debug_console); - } -#endif -} diff --git a/freedv/branches/1.2/src/dlg_options.h b/freedv/branches/1.2/src/dlg_options.h deleted file mode 100644 index 081448cb..00000000 --- a/freedv/branches/1.2/src/dlg_options.h +++ /dev/null @@ -1,137 +0,0 @@ -//========================================================================== -// Name: dlg_options.h -// Purpose: Dialog for controlling misc FreeDV options -// Created: Nov 25 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#ifndef __OPTIONS_DIALOG__ -#define __OPTIONS_DIALOG__ - -#include "fdmdv2_main.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class OptionsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class OptionsDlg : public wxDialog -{ - public: - OptionsDlg( wxWindow* parent, - wxWindowID id = wxID_ANY, const wxString& title = _("Options"), - const wxPoint& pos = wxDefaultPosition, -#ifdef __WXMSW__ - /* we add debug console check box for windows */ - const wxSize& size = wxSize(600,410), -#else - const wxSize& size = wxSize(600,380), -#endif - long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~OptionsDlg(); - - void ExchangeData(int inout, bool storePersistent); - void updateEventLog(wxString event_in, wxString event_out); - - bool enableEventsChecked() {return m_ckbox_events->GetValue();} - - void SetSpamTimerLight(bool state) { - - // Colours don't work on Windows - - if (state) { - m_rb_spam_timer->SetForegroundColour( wxColour( 255,0 , 0 ) ); // red - m_rb_spam_timer->SetValue(true); - } - else { - m_rb_spam_timer->SetForegroundColour( wxColour( 0, 255, 0 ) ); // green - m_rb_spam_timer->SetValue(false); - } - } - - - protected: - - // Handlers for events. - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnApply(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void OnInitDialog(wxInitDialogEvent& event); - - void OnTestFrame(wxScrollEvent& event); - void OnChannelNoise(wxScrollEvent& event); - void OnAttnCarrierEn(wxScrollEvent& event); - void OnFreeDV700txClip(wxScrollEvent& event); - void OnFreeDV700Combine(wxScrollEvent& event); - void OnDebugConsole(wxScrollEvent& event); - - wxTextCtrl *m_txtCtrlCallSign; // TODO: this should be renamed to tx_txtmsg, and rename all related incl persis strge - - wxCheckBox* m_ckHalfDuplex; - - /* Voice Keyer */ - - wxButton *m_buttonChooseVoiceKeyerWaveFile; - wxTextCtrl *m_txtCtrlVoiceKeyerWaveFile; - wxTextCtrl *m_txtCtrlVoiceKeyerRxPause; - wxTextCtrl *m_txtCtrlVoiceKeyerRepeats; - - /* test frames, other simulated channel impairments */ - - wxCheckBox *m_ckboxTestFrame; - wxCheckBox *m_ckboxChannelNoise; - wxTextCtrl *m_txtNoiseSNR; - wxCheckBox *m_ckboxAttnCarrierEn; - wxTextCtrl *m_txtAttnCarrier; - - wxCheckBox *m_ckboxTone; - wxTextCtrl *m_txtToneFreqHz; - wxTextCtrl *m_txtToneAmplitude; - - wxCheckBox *m_ckboxFreeDV700txClip; - wxCheckBox *m_ckboxFreeDV700Combine; - - wxRadioButton *m_rb_textEncoding1; - wxRadioButton *m_rb_textEncoding2; - wxCheckBox *m_ckboxEnableChecksum; - - wxCheckBox *m_ckbox_events; - wxTextCtrl *m_txt_events_regexp_match; - wxTextCtrl *m_txt_events_regexp_replace; - wxTextCtrl *m_txt_events_in; - wxTextCtrl *m_txt_events_out; - wxTextCtrl *m_txt_spam_timer; - wxRadioButton *m_rb_spam_timer; - - wxCheckBox *m_ckbox_udp_enable; - wxTextCtrl *m_txt_udp_port; - - wxButton* m_sdbSizer5OK; - wxButton* m_sdbSizer5Cancel; - wxButton* m_sdbSizer5Apply; - - wxCheckBox *m_ckboxDebugConsole; - - unsigned int event_in_serial, event_out_serial; - - void OnChooseVoiceKeyerWaveFile(wxCommandEvent& event); - - private: -}; - -#endif // __OPTIONS_DIALOG__ diff --git a/freedv/branches/1.2/src/dlg_plugin.cpp b/freedv/branches/1.2/src/dlg_plugin.cpp deleted file mode 100644 index 68610f42..00000000 --- a/freedv/branches/1.2/src/dlg_plugin.cpp +++ /dev/null @@ -1,148 +0,0 @@ -//========================================================================== -// Name: dlg_plugin.cpp -// Purpose: Subclasses dialog GUI for PlugIn Config. Creates simple -// wxWidgets dialog GUI to set a few text strings. -// Date: Jan 2016 -// Authors: David Rowe -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "dlg_plugin.h" -#include "fdmdv2_main.h" - -#ifdef __WIN32__ -#include -#endif -#if defined(__FreeBSD__) || defined(__WXOSX__) -#include -#include -#endif - -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlugInDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlugInDlg::PlugInDlg(const wxString& title, int numParam, wxString paramName[], wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - m_name = title; - m_numParam = numParam; - assert(m_numParam <= PLUGIN_MAX_PARAMS); - - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - this->SetSizer(mainSizer); - - int i; - for (i=0; iAdd(m_txtCtrlParam[i], 0, 0, 5); - mainSizer->Add(staticBoxSizer28a, 0, wxEXPAND, 5); - } - - //---------------------------------------------------------------------- - // OK - Cancel - Apply - //---------------------------------------------------------------------- - - wxBoxSizer* boxSizer12 = new wxBoxSizer(wxHORIZONTAL); - - m_buttonOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxSize(-1,-1), 0); - m_buttonOK->SetDefault(); - boxSizer12->Add(m_buttonOK, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonCancel, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - mainSizer->Add(boxSizer12, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL, 5); - - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - Centre(wxBOTH); - - // Connect events - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(PlugInDlg::OnInitDialog), NULL, this); - m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnOK), NULL, this); - m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnCancel), NULL, this); - -} - -//------------------------------------------------------------------------- -// ~PlugInDlg() -//------------------------------------------------------------------------- -PlugInDlg::~PlugInDlg() -{ - // Disconnect Events - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(PlugInDlg::OnInitDialog), NULL, this); - m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnOK), NULL, this); - m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnCancel), NULL, this); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void PlugInDlg::OnInitDialog(wxInitDialogEvent& event) -{ - ExchangeData(EXCHANGE_DATA_IN); -} - - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void PlugInDlg::ExchangeData(int inout) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - wxString str; - int i; - - if(inout == EXCHANGE_DATA_IN) - { - for (i=0; iSetValue(wxGetApp().m_txtPlugInParam[i]); - } - } - if(inout == EXCHANGE_DATA_OUT) - { - for (i=0; iGetValue(); - wxString configStr = "/" + m_name + "/" + m_paramName[i]; - pConfig->Write(configStr, wxGetApp().m_txtPlugInParam[i]); - } - pConfig->Flush(); - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void PlugInDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void PlugInDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - this->EndModal(wxID_OK); -} diff --git a/freedv/branches/1.2/src/dlg_plugin.h b/freedv/branches/1.2/src/dlg_plugin.h deleted file mode 100644 index 72efc7bb..00000000 --- a/freedv/branches/1.2/src/dlg_plugin.h +++ /dev/null @@ -1,65 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.h -// Purpose: Subclasses dialog GUI for PTT Config. -// -// Created: May. 11, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __PLUGIN_DIALOG__ -#define __PLUGIN_DIALOG__ - -#include "fdmdv2_main.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlugInDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlugInDlg : public wxDialog -{ - public: - PlugInDlg(const wxString& title = _("PTT Config"), int numParam = 0, wxString paramNames[]=NULL, wxWindow* parent=NULL, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(450,300), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); - virtual ~PlugInDlg(); - void ExchangeData(int inout); - - protected: - wxString m_name; - int m_numParam; - wxString m_paramName[PLUGIN_MAX_PARAMS]; - - wxTextCtrl* m_txtCtrlParam[PLUGIN_MAX_PARAMS]; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - - -protected: - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - virtual void OnInitDialog(wxInitDialogEvent& event); -}; - -#endif // __PLUGIN_DIALOG__ diff --git a/freedv/branches/1.2/src/dlg_ptt.cpp b/freedv/branches/1.2/src/dlg_ptt.cpp deleted file mode 100644 index 1a04c9c4..00000000 --- a/freedv/branches/1.2/src/dlg_ptt.cpp +++ /dev/null @@ -1,570 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.cpp -// Purpose: Subclasses dialog GUI for PTT Config. Creates simple -// wxWidgets dialog GUI to select real/virtual Comm ports. -// Date: May 11 2012 -// Authors: David Rowe, David Witten, Joel Stanley -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "dlg_ptt.h" -#include "fdmdv2_main.h" - -#ifdef __WIN32__ -#include -#endif -#if defined(__FreeBSD__) || defined(__WXOSX__) -#include -#include -#endif - -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class ComPortsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -ComPortsDlg::ComPortsDlg(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - this->SetSizer(mainSizer); - - //---------------------------------------------------------------------- - // Vox tone option - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer28 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("VOX PTT Settings")), wxHORIZONTAL); - m_ckLeftChannelVoxTone = new wxCheckBox(this, wxID_ANY, _("Left Channel Vox Tone"), wxDefaultPosition, wxSize(-1,-1), 0); - staticBoxSizer28->Add(m_ckLeftChannelVoxTone, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5); - - mainSizer->Add(staticBoxSizer28, 0, wxEXPAND, 5); - - //---------------------------------------------------------------------- - // Hamlib for CAT PTT - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer18 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Hamlib Settings")), wxHORIZONTAL); - wxGridSizer* gridSizerhl = new wxGridSizer(5, 2, 0, 0); - staticBoxSizer18->Add(gridSizerhl, 1, wxEXPAND|wxALIGN_LEFT, 5); - - /* Use Hamlib for PTT checkbox. */ - - m_ckUseHamlibPTT = new wxCheckBox(this, wxID_ANY, _("Use Hamlib PTT"), wxDefaultPosition, wxSize(-1, -1), 0); - m_ckUseHamlibPTT->SetValue(false); - gridSizerhl->Add(m_ckUseHamlibPTT, 0, wxALIGN_CENTER_VERTICAL, 0); - gridSizerhl->Add(new wxStaticText(this, -1, wxT("")), 0, wxEXPAND); - - /* Hamlib Rig Type combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Rig Model:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbRigName = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1), 0, NULL, wxCB_DROPDOWN); - wxGetApp().m_hamlib->populateComboBox(m_cbRigName); - m_cbRigName->SetSelection(wxGetApp().m_intHamlibRig); - gridSizerhl->Add(m_cbRigName, 0, wxALIGN_CENTER_VERTICAL, 0); - - /* Hamlib Serial Port combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Device:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialPort = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizerhl->Add(m_cbSerialPort, 0, wxALIGN_CENTER_VERTICAL, 0); - - /* Hamlib Serial Rate combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Rate:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialRate = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizerhl->Add(m_cbSerialRate, 0, wxALIGN_CENTER_VERTICAL, 0); - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Params:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialParams = new wxStaticText(this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, 0); - gridSizerhl->Add(m_cbSerialParams, 0, wxALIGN_CENTER_VERTICAL, 0); - - mainSizer->Add(staticBoxSizer18, 0, wxEXPAND, 5); - - //---------------------------------------------------------------------- - // Serial port PTT - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer17 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Serial Port Settings")), wxVERTICAL); - mainSizer->Add(staticBoxSizer17, 1, wxEXPAND, 5); - wxStaticBoxSizer* staticBoxSizer31 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("PTT Port")), wxVERTICAL); - staticBoxSizer17->Add(staticBoxSizer31, 1, wxEXPAND, 5); - -#ifdef __WXMSW__ - m_ckUseSerialPTT = new wxCheckBox(this, wxID_ANY, _("Use Serial Port PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckUseSerialPTT->SetValue(false); - staticBoxSizer31->Add(m_ckUseSerialPTT, 0, wxALIGN_LEFT, 20); - - wxArrayString m_listCtrlPortsArr; - m_listCtrlPorts = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1,45), m_listCtrlPortsArr, wxLB_SINGLE | wxLB_SORT); - staticBoxSizer31->Add(m_listCtrlPorts, 1, wxALIGN_CENTER, 0); -#endif - -#if defined(__WXOSX__) || defined(__WXGTK__) - wxBoxSizer* bSizer83; - bSizer83 = new wxBoxSizer(wxHORIZONTAL); - - wxGridSizer* gridSizer200 = new wxGridSizer(1, 3, 0, 0); - - m_ckUseSerialPTT = new wxCheckBox(this, wxID_ANY, _("Use Serial Port PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckUseSerialPTT->SetValue(false); - gridSizer200->Add(m_ckUseSerialPTT, 1, wxALIGN_CENTER|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 2); - - m_staticText12 = new wxStaticText(this, wxID_ANY, _("Serial Device: "), wxDefaultPosition, wxDefaultSize, 0); - m_staticText12->Wrap(-1); - gridSizer200->Add(m_staticText12, 1,wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 2); - - m_cbCtlDevicePath = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizer200->Add(m_cbCtlDevicePath, 1, wxEXPAND|wxALIGN_CENTER|wxALIGN_RIGHT, 2); - - bSizer83->Add(gridSizer200, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 2); - staticBoxSizer31->Add(bSizer83, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - wxBoxSizer* boxSizer19 = new wxBoxSizer(wxVERTICAL); - staticBoxSizer17->Add(boxSizer19, 1, wxEXPAND, 5); - wxStaticBoxSizer* staticBoxSizer16 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Signal polarity")), wxHORIZONTAL); - boxSizer19->Add(staticBoxSizer16, 1, wxEXPAND|wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - wxGridSizer* gridSizer17 = new wxGridSizer(2, 2, 0, 0); - staticBoxSizer16->Add(gridSizer17, 1, wxEXPAND|wxALIGN_RIGHT, 5); - - m_rbUseDTR = new wxRadioButton(this, wxID_ANY, _("Use DTR"), wxDefaultPosition, wxSize(-1,-1), 0); - m_rbUseDTR->SetToolTip(_("Toggle DTR line for PTT")); - m_rbUseDTR->SetValue(1); - gridSizer17->Add(m_rbUseDTR, 0, wxALIGN_CENTER|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5); - - m_ckDTRPos = new wxCheckBox(this, wxID_ANY, _("DTR = +V"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckDTRPos->SetToolTip(_("Set Polarity of the DTR line")); - m_ckDTRPos->SetValue(false); - gridSizer17->Add(m_ckDTRPos, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - m_rbUseRTS = new wxRadioButton(this, wxID_ANY, _("Use RTS"), wxDefaultPosition, wxSize(-1,-1), 0); - m_rbUseRTS->SetToolTip(_("Toggle the RTS pin for PTT")); - m_rbUseRTS->SetValue(1); - gridSizer17->Add(m_rbUseRTS, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - m_ckRTSPos = new wxCheckBox(this, wxID_ANY, _("RTS = +V"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckRTSPos->SetValue(false); - m_ckRTSPos->SetToolTip(_("Set Polarity of the RTS line")); - gridSizer17->Add(m_ckRTSPos, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - //---------------------------------------------------------------------- - // OK - Cancel - Apply - //---------------------------------------------------------------------- - - wxBoxSizer* boxSizer12 = new wxBoxSizer(wxHORIZONTAL); - - m_buttonTest = new wxButton(this, wxID_APPLY, _("Test PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonTest, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxSize(-1,-1), 0); - m_buttonOK->SetDefault(); - boxSizer12->Add(m_buttonOK, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonCancel, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonApply = new wxButton(this, wxID_APPLY, _("Apply"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonApply, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - mainSizer->Add(boxSizer12, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL, 5); - - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - Centre(wxBOTH); - - // Connect events - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(ComPortsDlg::OnInitDialog), NULL, this); - m_ckUseHamlibPTT->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseHamLibClicked), NULL, this); - m_ckUseSerialPTT->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseSerialClicked), NULL, this); - m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnOK), NULL, this); - m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnCancel), NULL, this); - m_buttonApply->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnApply), NULL, this); - m_buttonTest->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnTest), NULL, this); -} - -//------------------------------------------------------------------------- -// ~ComPortsDlg() -//------------------------------------------------------------------------- -ComPortsDlg::~ComPortsDlg() -{ - // Disconnect Events - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(ComPortsDlg::OnInitDialog), NULL, this); - m_ckUseHamlibPTT->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseHamLibClicked), NULL, this); - m_ckUseSerialPTT->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseSerialClicked), NULL, this); - m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnOK), NULL, this); - m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnCancel), NULL, this); - m_buttonApply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnApply), NULL, this); - m_buttonTest->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnTest), NULL, this); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void ComPortsDlg::OnInitDialog(wxInitDialogEvent& event) -{ - populatePortList(); - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// populatePortList() -//------------------------------------------------------------------------- -void ComPortsDlg::populatePortList() -{ - - /* populate Hamlib serial rate combo box */ - - wxString serialRates[] = {"default", "300", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"}; - for(int i=0; iAppend(serialRates[i]); - } - -#ifdef __WXMSW__ - m_listCtrlPorts->Clear(); - m_cbSerialPort->Clear(); - wxArrayString aStr; - wxRegKey key(wxRegKey::HKLM, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM")); - if(!key.Exists()) - { - return; - } - else - { - // Get the number of subkeys and enumerate them. - if(!key.Open(wxRegKey::Read)) - { - return; - } - size_t subkeys; - size_t values; - if(!key.GetKeyInfo(&subkeys, NULL, &values, NULL)) - { - return; - } - if(!key.HasValues()) - { - return; - } - wxString key_name; - long el = 1; - key.GetFirstValue(key_name, el); - wxString valType; - wxString key_data; - for(unsigned int i = 0; i < values; i++) - { - key.QueryValue(key_name, key_data); - //wxPrintf("Value: %s Data: %s\n", key_name, key_data); - aStr.Add(key_data, 1); - key.GetNextValue(key_name, el); - } - } - m_listCtrlPorts->Append(aStr); - m_cbSerialPort->Append(aStr); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - m_cbSerialPort->Clear(); - m_cbCtlDevicePath->Clear(); -#if defined(__FreeBSD__) || defined(__WXOSX__) - glob_t gl; -#ifdef __FreeBSD__ - if(glob("/dev/tty*", GLOB_MARK, NULL, &gl)==0) { -#else - if(glob("/dev/tty.*", GLOB_MARK, NULL, &gl)==0) { -#endif - for(unsigned int i=0; i= 'l' && gl.gl_pathv[i][8] <= 's') - continue; - if(gl.gl_pathv[i][8] >= 'L' && gl.gl_pathv[i][8] <= 'S') - continue; - - /* Exclude virtual TTYs */ - if(gl.gl_pathv[i][8] == 'v') - continue; - - /* Exclude initial-state and lock-state devices */ -#ifndef __WXOSX__ - if(strchr(gl.gl_pathv[i], '.') != NULL) - continue; -#endif - - m_cbSerialPort->Append(gl.gl_pathv[i]); - m_cbCtlDevicePath->Append(gl.gl_pathv[i]); - } - globfree(&gl); - } -#else - /* TODO(Joel): http://stackoverflow.com/questions/2530096/how-to-find-all-serial-devices-ttys-ttyusb-on-linux-without-opening-them */ - m_cbSerialPort->Append("/dev/ttyUSB0"); - m_cbSerialPort->Append("/dev/ttyUSB1"); - m_cbSerialPort->Append("/dev/ttyS0"); - m_cbSerialPort->Append("/dev/ttyS1"); - - m_cbCtlDevicePath->Clear(); - m_cbCtlDevicePath->Append("/dev/ttyUSB0"); - m_cbCtlDevicePath->Append("/dev/ttyUSB1"); - m_cbCtlDevicePath->Append("/dev/ttyS0"); - m_cbCtlDevicePath->Append("/dev/ttyS1"); -#endif -#endif - - -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void ComPortsDlg::ExchangeData(int inout) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - wxString str; - - if(inout == EXCHANGE_DATA_IN) { - m_ckLeftChannelVoxTone->SetValue(wxGetApp().m_leftChannelVoxTone); - - /* Hamlib */ - - m_ckUseHamlibPTT->SetValue(wxGetApp().m_boolHamlibUseForPTT); - m_cbRigName->SetSelection(wxGetApp().m_intHamlibRig); - m_cbSerialPort->SetValue(wxGetApp().m_strHamlibSerialPort); - - if (wxGetApp().m_intHamlibSerialRate == 0) { - m_cbSerialRate->SetSelection(0); - } else { - m_cbSerialRate->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intHamlibSerialRate)); - } - - /* Serial PTT */ - - m_ckUseSerialPTT->SetValue(wxGetApp().m_boolUseSerialPTT); - str = wxGetApp().m_strRigCtrlPort; -#ifdef __WXMSW__ - m_listCtrlPorts->SetStringSelection(str); -#endif -#if defined(__WXOSX__) || defined(__WXGTK__) - m_cbCtlDevicePath->SetValue(str); -#endif - m_rbUseRTS->SetValue(wxGetApp().m_boolUseRTS); - m_ckRTSPos->SetValue(wxGetApp().m_boolRTSPos); - m_rbUseDTR->SetValue(wxGetApp().m_boolUseDTR); - m_ckDTRPos->SetValue(wxGetApp().m_boolDTRPos); - } - - if (inout == EXCHANGE_DATA_OUT) { - wxGetApp().m_leftChannelVoxTone = m_ckLeftChannelVoxTone->GetValue(); - pConfig->Write(wxT("/Rig/leftChannelVoxTone"), wxGetApp().m_leftChannelVoxTone); - - /* Hamlib settings. */ - - wxGetApp().m_boolHamlibUseForPTT = m_ckUseHamlibPTT->GetValue(); - wxGetApp().m_intHamlibRig = m_cbRigName->GetSelection(); - wxGetApp().m_strHamlibSerialPort = m_cbSerialPort->GetValue(); - - wxString s = m_cbSerialRate->GetValue(); - if (s == "default") { - wxGetApp().m_intHamlibSerialRate = 0; - } else { - long tmp; - m_cbSerialRate->GetValue().ToLong(&tmp); - wxGetApp().m_intHamlibSerialRate = tmp; - } - fprintf(stderr, "serial rate: %d\n", wxGetApp().m_intHamlibSerialRate); - - pConfig->Write(wxT("/Hamlib/UseForPTT"), wxGetApp().m_boolHamlibUseForPTT); - pConfig->Write(wxT("/Hamlib/RigName"), wxGetApp().m_intHamlibRig); - pConfig->Write(wxT("/Hamlib/SerialPort"), wxGetApp().m_strHamlibSerialPort); - pConfig->Write(wxT("/Hamlib/SerialRate"), wxGetApp().m_intHamlibSerialRate); - - /* Serial settings */ - - wxGetApp().m_boolUseSerialPTT = m_ckUseSerialPTT->IsChecked(); -#ifdef __WXMSW__ - wxGetApp().m_strRigCtrlPort = m_listCtrlPorts->GetStringSelection(); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - wxGetApp().m_strRigCtrlPort = m_cbCtlDevicePath->GetValue(); -#endif - wxGetApp().m_boolUseRTS = m_rbUseRTS->GetValue(); - wxGetApp().m_boolRTSPos = m_ckRTSPos->IsChecked(); - wxGetApp().m_boolUseDTR = m_rbUseDTR->GetValue(); - wxGetApp().m_boolDTRPos = m_ckDTRPos->IsChecked(); - - pConfig->Write(wxT("/Rig/UseSerialPTT"), wxGetApp().m_boolUseSerialPTT); - pConfig->Write(wxT("/Rig/Port"), wxGetApp().m_strRigCtrlPort); - pConfig->Write(wxT("/Rig/UseRTS"), wxGetApp().m_boolUseRTS); - pConfig->Write(wxT("/Rig/RTSPolarity"), wxGetApp().m_boolRTSPos); - pConfig->Write(wxT("/Rig/UseDTR"), wxGetApp().m_boolUseDTR); - pConfig->Write(wxT("/Rig/DTRPolarity"), wxGetApp().m_boolDTRPos); - - pConfig->Flush(); - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -//------------------------------------------------------------------------- -// PTTUseHamLibClicked() -//------------------------------------------------------------------------- -void ComPortsDlg::PTTUseHamLibClicked(wxCommandEvent& event) -{ - m_ckUseSerialPTT->SetValue(false); -} - - -/* Attempt to toggle PTT for 1 second */ - -void ComPortsDlg::OnTest(wxCommandEvent& event) { - - /* Tone PTT */ - - if (m_ckLeftChannelVoxTone->GetValue()) { - wxMessageBox("Testing of tone based PTT not supported; try PTT after pressing Start on main window", - wxT("Error"), wxOK | wxICON_ERROR, this); - } - - /* Hamlib PTT */ - - if (m_ckUseHamlibPTT->GetValue()) { - - // set up current hamlib config from GUI - - int rig = m_cbRigName->GetSelection(); - wxString port = m_cbSerialPort->GetValue(); - wxString s = m_cbSerialRate->GetValue(); - int serial_rate; - if (s == "default") { - serial_rate = 0; - } else { - long tmp; - m_cbSerialRate->GetValue().ToLong(&tmp); - serial_rate = tmp; - } - - // display serial params - - fprintf(stderr, "serial rate: %d\n", serial_rate); - - // try to open rig - - Hamlib *hamlib = wxGetApp().m_hamlib; - bool status = hamlib->connect(rig, port.mb_str(wxConvUTF8), serial_rate); - if (status == false) { - wxMessageBox("Couldn't connect to Radio with hamlib", wxT("Error"), wxOK | wxICON_ERROR, this); - return; - } - else { - wxString hamlib_serial_config; - hamlib_serial_config.sprintf(" %d, %d, %d", - hamlib->get_serial_rate(), - hamlib->get_data_bits(), - hamlib->get_stop_bits()); - m_cbSerialParams->SetLabel(hamlib_serial_config); - } - - // toggle PTT - - wxString hamlibError; - if (hamlib->ptt(true, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - return; - } - - wxSleep(1); - - if (hamlib->ptt(false, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - } - - /* Serial PTT */ - - if (m_ckUseSerialPTT->IsChecked()) { - Serialport *serialport = wxGetApp().m_serialport; - - wxString ctrlport; -#ifdef __WXMSW__ - ctrlport = m_listCtrlPorts->GetStringSelection(); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - ctrlport = m_cbCtlDevicePath->GetValue(); -#endif - fprintf(stderr, "opening serial port\n"); - - bool success = serialport->openport(ctrlport.c_str(), - m_rbUseRTS->GetValue(), - m_ckRTSPos->IsChecked(), - m_rbUseDTR->GetValue(), - m_ckDTRPos->IsChecked()); - - fprintf(stderr, "serial port open\n"); - - if (!success) { - wxMessageBox("Couldn't open serial port", wxT("Error"), wxOK | wxICON_ERROR, this); - } - - // assert PTT port for 1 sec - - serialport->ptt(true); - wxSleep(1); - serialport->ptt(false); - - fprintf(stderr, "closing serial port\n"); - serialport->closeport(); - fprintf(stderr, "serial port closed\n"); - } - -} - - -//------------------------------------------------------------------------- -// PTTUseSerialClicked() -//------------------------------------------------------------------------- -void ComPortsDlg::PTTUseSerialClicked(wxCommandEvent& event) -{ - m_ckUseHamlibPTT->SetValue(false); -} - -//------------------------------------------------------------------------- -// OnApply() -//------------------------------------------------------------------------- -void ComPortsDlg::OnApply(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void ComPortsDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void ComPortsDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - this->EndModal(wxID_OK); -} diff --git a/freedv/branches/1.2/src/dlg_ptt.h b/freedv/branches/1.2/src/dlg_ptt.h deleted file mode 100644 index 26ab17e8..00000000 --- a/freedv/branches/1.2/src/dlg_ptt.h +++ /dev/null @@ -1,93 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.h -// Purpose: Subclasses dialog GUI for PTT Config. -// -// Created: May. 11, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __COMPORTS_DIALOG__ -#define __COMPORTS_DIALOG__ - -#include "fdmdv2_main.h" -#include "hamlib.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class ComPortsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class ComPortsDlg : public wxDialog -{ - public: - ComPortsDlg(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("PTT Config"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(450,300), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); - virtual ~ComPortsDlg(); - void ExchangeData(int inout); - - protected: - wxCheckBox* m_ckLeftChannelVoxTone; - - /* Hamlib settings.*/ - - wxCheckBox *m_ckUseHamlibPTT; - wxComboBox *m_cbRigName; - wxComboBox *m_cbSerialPort; - wxStaticText *m_cbSerialParams; - Hamlib *m_hamlib; - - /* Serial Settings */ - - wxListBox *m_listCtrlPorts; - wxCheckBox *m_ckUseSerialPTT; - wxStaticText *m_staticText12; - wxComboBox *m_cbCtlDevicePath; - wxRadioButton *m_rbUseDTR; - wxCheckBox *m_ckRTSPos; - wxRadioButton *m_rbUseRTS; - wxCheckBox *m_ckDTRPos; - - /* Test - Ok - Cancel - Apply */ - - wxButton* m_buttonTest; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - wxButton* m_buttonApply; - - -protected: - void populatePortList(); - - void PTTUseHamLibClicked(wxCommandEvent& event); - void PTTUseSerialClicked(wxCommandEvent& event); - - void OnTest(wxCommandEvent& event); - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnApply(wxCommandEvent& event); - virtual void OnInitDialog(wxInitDialogEvent& event); -}; - -#endif // __COMPORTS_DIALOG__ diff --git a/freedv/branches/1.2/src/fdmdv2_defines.h b/freedv/branches/1.2/src/fdmdv2_defines.h deleted file mode 100644 index a6878890..00000000 --- a/freedv/branches/1.2/src/fdmdv2_defines.h +++ /dev/null @@ -1,106 +0,0 @@ -//========================================================================== -// Name: fdmdv2_defines.h -// Purpose: Definitions used by plots derived from fdmdv2_plot class. -// Created: August 27, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_DEFINES__ -#define __FDMDV2_DEFINES__ - -#include "wx/wx.h" -#include "freedv_api.h" -#include "modem_stats.h" - -// Spectrogram and Waterfall - -#define MIN_MAG_DB -40.0 // min of spectrogram/waterfall magnitude axis -#define MAX_MAG_DB 0.0 // max of spectrogram/waterfall magnitude axis -#define STEP_MAG_DB 5.0 // magnitude axis step -#define BETA 0.95 // constant for time averaging spectrum data -#define MIN_F_HZ 0 // min freq on Waterfall and Spectrum -#define MAX_F_HZ 3000 // max freq on Waterfall and Spectrum -#define STEP_F_HZ 500 // major (e.g. text legend) freq step on Waterfall and Spectrum graticule -#define STEP_MINOR_F_HZ 100 // minor (ticks) freq step on Waterfall and Spectrum graticule -#define WATERFALL_SECS_Y 30 // number of seconds respresented by y axis of waterfall -#define WATERFALL_SECS_STEP 5 // graticule y axis steps of waterfall -#define DT 0.1 // time between real time graphing updates -#define FS 8000 // FDMDV modem sample rate - -// Scatter diagram - -#define SCATTER_MEM_SECS 10 -// (symbols/frame)/(graphics update period) = symbols/s sent to scatter memory -// memory (symbols) = secs of memory * symbols/sec -#define SCATTER_MEM_SYMS_MAX ((int)(SCATTER_MEM_SECS*((MODEM_STATS_NC_MAX+1)/DT))) -#define SCATTER_EYE_MEM_ROWS ((int)(SCATTER_MEM_SECS/DT)) - -// Waveform plotting constants - -#define WAVEFORM_PLOT_FS 400 // sample rate (points/s) of waveform plotted to screen -#define WAVEFORM_PLOT_TIME 5 // length or entire waveform on screen -#define WAVEFORM_PLOT_BUF ((int)(DT*WAVEFORM_PLOT_FS)) // number of new samples we plot per DT - -// sample rate I/O & conversion constants - -#define MAX_FPB 8096 // maximum value of portAudio framesPerBuffer -#define PA_FPB 1024 // nominal value of portAudio framesPerBuffer -#define SAMPLE_RATE 48000 // 48 kHz sampling rate rec. as we can trust accuracy of sound card -#define N8 160 // processing buffer size at 8 kHz -#define MEM8 (FDMDV_OS_TAPS/FDMDV_OS) -#define N48 (N8*SAMPLE_RATE/FS) // processing buffer size at 48 kHz -#define NUM_CHANNELS 2 // I think most sound cards prefer stereo we will convert to mono -#define VOX_TONE_FREQ 1000.0 // optional left channel vox tone freq -#define VOX_TONE_AMP 30000 // optional left channel vox tone amp - -#define MAX_BITS_PER_CODEC_FRAME 64 // 1600 bit/s mode -#define MAX_BYTES_PER_CODEC_FRAME (MAX_BITS_PER_CODEC_FRAME/8) -#define MAX_BITS_PER_FDMDV_FRAME 40 // 2000 bit/s mode - -// Squelch -#define SQ_DEFAULT_SNR 2.0 - -// Level Gauge -#define FROM_RADIO_MAX 0.8 -#define FROM_MIC_MAX 0.8 -#define LEVEL_BETA 0.99 - -// SNR -#define SNRSLOW_BETA 0.5 // time constant for slow SNR for display - -// Text messaging Data -#define MAX_CALLSIGN 80 -#define MAX_EVENT_LOG 10 -#define MAX_EVENT_RULES 100 - -enum -{ - ID_ROTATE_LEFT = wxID_HIGHEST + 1, - ID_ROTATE_RIGHT, - ID_RESIZE, - ID_PAINT_BG -}; - -// Codec 2 LPC Post Filter defaults, from codec-dev/src/quantise.c - -#define CODEC2_LPC_PF_GAMMA 0.5 -#define CODEC2_LPC_PF_BETA 0.2 - -// PlugIns ... - -#define PLUGIN_MAX_PARAMS 4 - -#endif //__FDMDV2_DEFINES__ diff --git a/freedv/branches/1.2/src/fdmdv2_main.cpp b/freedv/branches/1.2/src/fdmdv2_main.cpp deleted file mode 100644 index 47a6e77c..00000000 --- a/freedv/branches/1.2/src/fdmdv2_main.cpp +++ /dev/null @@ -1,3943 +0,0 @@ -//========================================================================== -// Name: fdmdv2_main.cpp -// -// Purpose: FreeDV main() -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "fdmdv2_main.h" - -#define wxUSE_FILEDLG 1 -#define wxUSE_LIBPNG 1 -#define wxUSE_LIBJPEG 1 -#define wxUSE_GIF 1 -#define wxUSE_PCX 1 -#define wxUSE_LIBTIFF 1 - -//------------------------------------------------------------------- -// Bunch of globals used for communication with sound card call -// back functions -// ------------------------------------------------------------------ - -int g_in, g_out; - -// Global Codec2 & modem states - just one reqd for tx & rx -int g_Nc; -int g_mode; -struct freedv *g_pfreedv; -struct MODEM_STATS g_stats; -float g_pwr_scale; -int g_clip; - -// test Frames -int g_testFrames; -int g_test_frame_sync_state; -int g_test_frame_count; -int g_total_bits; -int g_total_bit_errors; -int g_channel_noise; -float g_sig_pwr_av = 0.0; -struct FIFO *g_error_pattern_fifo; -short *g_error_hist; - -// time averaged magnitude spectrum used for waterfall and spectrum display -float g_avmag[MODEM_STATS_NSPEC]; - -// GUI controls that affect rx and tx processes -int g_SquelchActive; -float g_SquelchLevel; -int g_analog; -int g_split; -int g_tx; -float g_snr; -bool g_half_duplex; -bool g_modal; - -// sending and receiving Call Sign data -struct FIFO *g_txDataInFifo; -struct FIFO *g_rxDataOutFifo; - -// tx/rx processing states -int g_State; -paCallBackData *g_rxUserdata; - -// FIFOs used for plotting waveforms -struct FIFO *g_plotDemodInFifo; -struct FIFO *g_plotSpeechOutFifo; -struct FIFO *g_plotSpeechInFifo; - -// Soundcard config -int g_nSoundCards; -int g_soundCard1InDeviceNum; -int g_soundCard1OutDeviceNum; -int g_soundCard1SampleRate; -int g_soundCard2InDeviceNum; -int g_soundCard2OutDeviceNum; -int g_soundCard2SampleRate; - -// playing and recording from sound files - -SNDFILE *g_sfPlayFile; -bool g_playFileToMicIn; -bool g_loopPlayFileToMicIn; -int g_playFileToMicInEventId; - -SNDFILE *g_sfRecFile; -bool g_recFileFromRadio; -unsigned int g_recFromRadioSamples; -int g_recFileFromRadioEventId; - -SNDFILE *g_sfPlayFileFromRadio; -bool g_playFileFromRadio; -int g_sfFs; -bool g_loopPlayFileFromRadio; -int g_playFileFromRadioEventId; -float g_blink; - -wxWindow *g_parent; - -// Click to tune rx and tx frequency offset states -float g_RxFreqOffsetHz; -COMP g_RxFreqOffsetPhaseRect; -float g_TxFreqOffsetHz; -COMP g_TxFreqOffsetPhaseRect; - -// experimental mutex to make sound card callbacks mutually exclusive -// TODO: review code and see if we need this any more, as fifos should -// now be thread safe - -wxMutex g_mutexProtectingCallbackData; - -// Speex pre-processor states - -SpeexPreprocessState *g_speex_st; - -// WxWidgets - initialize the application -IMPLEMENT_APP(MainApp); - -//FILE *ftest; -FILE *g_logfile; - -//------------------------------------------------------------------------- -// OnInit() -//------------------------------------------------------------------------- -bool MainApp::OnInit() -{ - if(!wxApp::OnInit()) - { - return false; - } - SetVendorName(wxT("CODEC2-Project")); - SetAppName(wxT("FreeDV")); // not needed, it's the default value - -#ifdef FILE_RATHER_THAN_REGISTRY - // Force use of file-based configuration persistance on Windows platforma - wxConfig *pConfig = new wxConfig(); - wxFileConfig *pFConfig = new wxFileConfig(wxT("FreeDV"), wxT("CODEC2-Project"), wxT("FreeDV.conf"), wxT("FreeDV.conf"), wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH); - pConfig->Set(pFConfig); - pConfig->SetRecordDefaults(); -#else - wxConfigBase *pConfig = wxConfigBase::Get(); - pConfig->SetRecordDefaults(); -#endif - - m_rTopWindow = wxRect(0, 0, 0, 0); - m_strRxInAudio.Empty(); - m_strRxOutAudio.Empty(); - m_textVoiceInput.Empty(); - m_textVoiceOutput.Empty(); - m_strSampleRate.Empty(); - m_strBitrate.Empty(); - - // Look for Plug In - - m_plugIn = false; - #ifdef __WXMSW__ - wchar_t dll_path[] = L"afreedvplugin.dll"; - m_plugInHandle = LoadLibrary(dll_path); - #else - m_plugInHandle = dlopen("afreedvplugin.so", RTLD_LAZY); - #endif - - if (m_plugInHandle) { - printf("plugin: .so found\n"); - - // lets get some information abt the plugIn - - void (*plugin_namefp)(char s[]); - void *(*plugin_openfp)(char *param_names[], int *nparams, int (*aplugin_get_persistant)(char *, char *)); - - #ifdef __WXMSW__ - plugin_namefp = (void (*)(char*))GetProcAddress((HMODULE)m_plugInHandle, "plugin_name"); - plugin_openfp = (void* (*)(char**,int *, int (*)(char *, char *)))GetProcAddress((HMODULE)m_plugInHandle, "plugin_open"); - m_plugin_startfp = (void (*)(void *))GetProcAddress((HMODULE)m_plugInHandle, "plugin_start"); - m_plugin_stopfp = (void (*)(void *))GetProcAddress((HMODULE)m_plugInHandle, "plugin_stop"); - m_plugin_rx_samplesfp = (void (*)(void *, short *, int))GetProcAddress((HMODULE)m_plugInHandle, "plugin_rx_samples"); - #else - plugin_namefp = (void (*)(char*))dlsym(m_plugInHandle, "plugin_name"); - plugin_openfp = (void* (*)(char**,int *, int (*)(char *, char *)))dlsym(m_plugInHandle, "plugin_open"); - m_plugin_startfp = (void (*)(void *))dlsym(m_plugInHandle, "plugin_start"); - m_plugin_stopfp = (void (*)(void *))dlsym(m_plugInHandle, "plugin_stop"); - m_plugin_rx_samplesfp = (void (*)(void *, short *, int))dlsym(m_plugInHandle, "plugin_rx_samples"); - #endif - - if ((plugin_namefp != NULL) && (plugin_openfp != NULL)) { - - char s[256]; - m_plugIn = true; - (plugin_namefp)(s); - fprintf(stderr, "plugin name: %s\n", s); - m_plugInName = s; - - char param_name1[80], param_name2[80]; - char *param_names[2] = {param_name1, param_name2}; - int nparams, i; - m_plugInStates = (plugin_openfp)(param_names, &nparams, plugin_get_persistant); - m_numPlugInParam = nparams; - for(i=0; iRead(configStr, wxT("")); - //fprintf(stderr, " plugin param name[%d]: %s\n", i, param_names[i]); - fprintf(stderr, " plugin param name[%d]: %s values: %s\n", i, m_plugInParamName[i].mb_str().data(), m_txtPlugInParam[i].mb_str().data()); - } - } - - else { - fprintf(stderr, "plugin: fps not found...\n"); - } - } - else { - fprintf(stderr, "plugin not found...\n"); - } - - // Create the main application window - - frame = new MainFrame(m_plugInName, NULL); - SetTopWindow(frame); - - // Should guarantee that the first plot tab defined is the one - // displayed. But it doesn't when built from command line. Why? - - frame->m_auiNbookCtrl->ChangeSelection(0); - frame->Layout(); - frame->Show(); - g_parent =frame; - - - return true; -} - -//------------------------------------------------------------------------- -// OnExit() -//------------------------------------------------------------------------- -int MainApp::OnExit() -{ - fprintf(stderr, "MainApp::OnExit\n"); - if (m_plugIn) { - #ifdef __WXMSW__ - FreeLibrary((HMODULE)m_plugInHandle); - #else - dlclose(m_plugInHandle); - #endif - } - - return 0; -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainFrame(wxFrame* pa->ent) : TopFrame(parent) -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -MainFrame::MainFrame(wxString plugInName, wxWindow *parent) : TopFrame(plugInName, parent) -{ - m_zoom = 1.; - - #ifdef __WXMSW__ - g_logfile = stderr; - #else - g_logfile = stderr; - #endif - - - SetMinSize(wxSize(400,400)); - - // Init Hamlib library, but we dont start talking to any rigs yet - - wxGetApp().m_hamlib = new Hamlib(); - - // Init Serialport library, but as for Hamlib we dont start talking to any rigs yet - - wxGetApp().m_serialport = new Serialport(); - - tools->AppendSeparator(); - wxMenuItem* m_menuItemToolsConfigDelete; - m_menuItemToolsConfigDelete = new wxMenuItem(tools, wxID_ANY, wxString(_("&Restore defaults")) , wxT("Delete config file/keys and restore defaults"), wxITEM_NORMAL); - this->Connect(m_menuItemToolsConfigDelete->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::OnDeleteConfig)); - - tools->Append(m_menuItemToolsConfigDelete); - - wxConfigBase *pConfig = wxConfigBase::Get(); - - // restore frame position and size - int x = pConfig->Read(wxT("/MainFrame/left"), 20); - int y = pConfig->Read(wxT("/MainFrame/top"), 20); - int w = pConfig->Read(wxT("/MainFrame/width"), 800); - int h = pConfig->Read(wxT("/MainFrame/height"), 550); - - // sanitise frame position as a first pass at Win32 registry bug - - fprintf(g_logfile, "x = %d y = %d w = %d h = %d\n", x,y,w,h); - if (x < 0 || x > 2048) x = 20; - if (y < 0 || y > 2048) y = 20; - if (w < 0 || w > 2048) w = 800; - if (h < 0 || h > 2048) h = 550; - - wxGetApp().m_show_wf = pConfig->Read(wxT("/MainFrame/show_wf"), 1); - wxGetApp().m_show_spect = pConfig->Read(wxT("/MainFrame/show_spect"), 1); - wxGetApp().m_show_scatter = pConfig->Read(wxT("/MainFrame/show_scatter"), 1); - wxGetApp().m_show_timing = pConfig->Read(wxT("/MainFrame/show_timing"), 1); - wxGetApp().m_show_freq = pConfig->Read(wxT("/MainFrame/show_freq"), 1); - wxGetApp().m_show_speech_in = pConfig->Read(wxT("/MainFrame/show_speech_in"), 1); - wxGetApp().m_show_speech_out = pConfig->Read(wxT("/MainFrame/show_speech_out"), 1); - wxGetApp().m_show_demod_in = pConfig->Read(wxT("/MainFrame/show_demod_in"), 1); - wxGetApp().m_show_test_frame_errors = pConfig->Read(wxT("/MainFrame/show_test_frame_errors"), 1); - wxGetApp().m_show_test_frame_errors_hist = pConfig->Read(wxT("/MainFrame/show_test_frame_errors_hist"), 1); - - wxGetApp().m_rxNbookCtrl = pConfig->Read(wxT("/MainFrame/rxNbookCtrl"), (long)0); - - g_SquelchActive = pConfig->Read(wxT("/Audio/SquelchActive"), (long)0); - g_SquelchLevel = pConfig->Read(wxT("/Audio/SquelchLevel"), (int)(SQ_DEFAULT_SNR*2)); - g_SquelchLevel /= 2.0; - - Move(x, y); - SetClientSize(w, h); - - if(wxGetApp().m_show_wf) - { - // Add Waterfall Plot window - m_panelWaterfall = new PlotWaterfall((wxFrame*) m_auiNbookCtrl, false, 0); - m_panelWaterfall->SetToolTip(_("Left click to tune, Right click to toggle mono/colour")); - m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Waterfall"), true, wxNullBitmap); - } - if(wxGetApp().m_show_spect) - { - // Add Spectrum Plot window - m_panelSpectrum = new PlotSpectrum((wxFrame*) m_auiNbookCtrl, g_avmag, - MODEM_STATS_NSPEC*((float)MAX_F_HZ/MODEM_STATS_MAX_F_HZ)); - m_panelSpectrum->SetToolTip(_("Left click to tune")); - m_auiNbookCtrl->AddPage(m_panelSpectrum, _("Spectrum"), true, wxNullBitmap); - } - if(wxGetApp().m_show_scatter) - { - // Add Scatter Plot window - m_panelScatter = new PlotScatter((wxFrame*) m_auiNbookCtrl); - m_auiNbookCtrl->AddPage(m_panelScatter, _("Scatter"), true, wxNullBitmap); - } - if(wxGetApp().m_show_demod_in) - { - // Add Demod Input window - m_panelDemodIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelDemodIn, _("Frm Radio"), true, wxNullBitmap); - g_plotDemodInFifo = fifo_create(2*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_speech_in) - { - // Add Speech Input window - m_panelSpeechIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechIn, _("Frm Mic"), true, wxNullBitmap); - g_plotSpeechInFifo = fifo_create(4*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_speech_out) - { - // Add Speech Output window - m_panelSpeechOut = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechOut, _("To Spkr/Hdphns"), true, wxNullBitmap); - g_plotSpeechOutFifo = fifo_create(2*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_timing) - { - // Add Timing Offset window - m_panelTimeOffset = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 5.0, DT, -0.5, 0.5, 1, 0.1, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelTimeOffset, L"Timing \u0394", true, wxNullBitmap); - } - if(wxGetApp().m_show_freq) - { - // Add Frequency Offset window - m_panelFreqOffset = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 5.0, DT, -200, 200, 1, 50, "%3.0fHz", 0); - m_auiNbookCtrl->AddPage(m_panelFreqOffset, L"Frequency \u0394", true, wxNullBitmap); - } - - if(wxGetApp().m_show_test_frame_errors) - { - // Add Test Frame Errors window - m_panelTestFrameErrors = new PlotScalar((wxFrame*) m_auiNbookCtrl, 2*MODEM_STATS_NC_MAX, 30.0, DT, 0, 2*MODEM_STATS_NC_MAX+2, 1, 1, "", 1); - m_auiNbookCtrl->AddPage(m_panelTestFrameErrors, L"Test Frame Errors", true, wxNullBitmap); - } - - if(wxGetApp().m_show_test_frame_errors_hist) - { - // Add Test Frame Errors window - m_panelTestFrameErrorsHist = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 1.0, 1.0/(2*FDMDV_NC_MAX), 0.0, 1.0, 1.0/FDMDV_NC_MAX, 0.1, "%3.2f", 0); - m_auiNbookCtrl->AddPage(m_panelTestFrameErrorsHist, L"Test Frame Histogram", true, wxNullBitmap); - } - - wxGetApp().m_framesPerBuffer = pConfig->Read(wxT("/Audio/framesPerBuffer"), PA_FPB); - - g_soundCard1InDeviceNum = pConfig->Read(wxT("/Audio/soundCard1InDeviceNum"), -1); - g_soundCard1OutDeviceNum = pConfig->Read(wxT("/Audio/soundCard1OutDeviceNum"), -1); - g_soundCard1SampleRate = pConfig->Read(wxT("/Audio/soundCard1SampleRate"), -1); - - g_soundCard2InDeviceNum = pConfig->Read(wxT("/Audio/soundCard2InDeviceNum"), -1); - g_soundCard2OutDeviceNum = pConfig->Read(wxT("/Audio/soundCard2OutDeviceNum"), -1); - g_soundCard2SampleRate = pConfig->Read(wxT("/Audio/soundCard2SampleRate"), -1); - - g_nSoundCards = 0; - if ((g_soundCard1InDeviceNum > -1) && (g_soundCard1OutDeviceNum > -1)) { - g_nSoundCards = 1; - if ((g_soundCard2InDeviceNum > -1) && (g_soundCard2OutDeviceNum > -1)) - g_nSoundCards = 2; - } - - wxGetApp().m_playFileToMicInPath = pConfig->Read("/File/playFileToMicInPath", wxT("")); - wxGetApp().m_recFileFromRadioPath = pConfig->Read("/File/recFileFromRadioPath", wxT("")); - wxGetApp().m_recFileFromRadioSecs = pConfig->Read("/File/recFileFromRadioSecs", 30); - wxGetApp().m_playFileFromRadioPath = pConfig->Read("/File/playFileFromRadioPath", wxT("")); - - // PTT ------------------------------------------------------------------- - - wxGetApp().m_boolHalfDuplex = pConfig->ReadBool(wxT("/Rig/HalfDuplex"), true); - wxGetApp().m_leftChannelVoxTone = pConfig->ReadBool("/Rig/leftChannelVoxTone", false); - - wxGetApp().m_txtVoiceKeyerWaveFilePath = pConfig->Read(wxT("/VoiceKeyer/WaveFilePath"), wxT("")); - wxGetApp().m_txtVoiceKeyerWaveFile = pConfig->Read(wxT("/VoiceKeyer/WaveFile"), wxT("voicekeyer.wav")); - wxGetApp().m_intVoiceKeyerRxPause = pConfig->Read(wxT("/VoiceKeyer/RxPause"), 10); - wxGetApp().m_intVoiceKeyerRepeats = pConfig->Read(wxT("/VoiceKeyer/Repeats"), 5); - - wxGetApp().m_boolHamlibUseForPTT = pConfig->ReadBool("/Hamlib/UseForPTT", false); - wxGetApp().m_intHamlibRig = pConfig->ReadLong("/Hamlib/RigName", 0); - wxGetApp().m_strHamlibSerialPort = pConfig->Read("/Hamlib/SerialPort", ""); - - wxGetApp().m_boolUseSerialPTT = pConfig->ReadBool(wxT("/Rig/UseSerialPTT"), false); - wxGetApp().m_strRigCtrlPort = pConfig->Read(wxT("/Rig/Port"), wxT("")); - wxGetApp().m_boolUseRTS = pConfig->ReadBool(wxT("/Rig/UseRTS"), true); - wxGetApp().m_boolRTSPos = pConfig->ReadBool(wxT("/Rig/RTSPolarity"), true); - wxGetApp().m_boolUseDTR = pConfig->ReadBool(wxT("/Rig/UseDTR"), false); - wxGetApp().m_boolDTRPos = pConfig->ReadBool(wxT("/Rig/DTRPolarity"), false); - - assert(wxGetApp().m_serialport != NULL); - - // ----------------------------------------------------------------------- - - bool slow = false; // prevents compile error when using default bool - wxGetApp().m_snrSlow = pConfig->Read("/Audio/snrSlow", slow); - - bool t = true; // prevents compile error when using default bool - wxGetApp().m_codec2LPCPostFilterEnable = pConfig->Read(wxT("/Filter/codec2LPCPostFilterEnable"), t); - wxGetApp().m_codec2LPCPostFilterBassBoost = pConfig->Read(wxT("/Filter/codec2LPCPostFilterBassBoost"), t); - wxGetApp().m_codec2LPCPostFilterGamma = (float)pConfig->Read(wxT("/Filter/codec2LPCPostFilterGamma"), CODEC2_LPC_PF_GAMMA*100)/100.0; - wxGetApp().m_codec2LPCPostFilterBeta = (float)pConfig->Read(wxT("/Filter/codec2LPCPostFilterBeta"), CODEC2_LPC_PF_BETA*100)/100.0; - //printf("main(): m_codec2LPCPostFilterBeta: %f\n", wxGetApp().m_codec2LPCPostFilterBeta); - - wxGetApp().m_speexpp_enable = pConfig->Read(wxT("/Filter/speexpp_enable"), t); - - wxGetApp().m_MicInBassFreqHz = (float)pConfig->Read(wxT("/Filter/MicInBassFreqHz"), 1); - wxGetApp().m_MicInBassGaindB = (float)pConfig->Read(wxT("/Filter/MicInBassGaindB"), (long)0)/10.0; - wxGetApp().m_MicInTrebleFreqHz = (float)pConfig->Read(wxT("/Filter/MicInTrebleFreqHz"), 1); - wxGetApp().m_MicInTrebleGaindB = (float)pConfig->Read(wxT("/Filter/MicInTrebleGaindB"), (long)0)/10.0; - wxGetApp().m_MicInMidFreqHz = (float)pConfig->Read(wxT("/Filter/MicInMidFreqHz"), 1); - wxGetApp().m_MicInMidGaindB = (float)pConfig->Read(wxT("/Filter/MicInMidGaindB"), (long)0)/10.0; - wxGetApp().m_MicInMidQ = (float)pConfig->Read(wxT("/Filter/MicInMidQ"), (long)100)/100.0; - - bool f = false; - wxGetApp().m_MicInEQEnable = (float)pConfig->Read(wxT("/Filter/MicInEQEnable"), f); - - wxGetApp().m_SpkOutBassFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutBassFreqHz"), 1); - wxGetApp().m_SpkOutBassGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutBassGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutTrebleFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutTrebleFreqHz"), 1); - wxGetApp().m_SpkOutTrebleGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutTrebleGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutMidFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutMidFreqHz"), 1); - wxGetApp().m_SpkOutMidGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutMidGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutMidQ = (float)pConfig->Read(wxT("/Filter/SpkOutMidQ"), (long)100)/100.0; - - wxGetApp().m_SpkOutEQEnable = (float)pConfig->Read(wxT("/Filter/SpkOutEQEnable"), f); - - wxGetApp().m_callSign = pConfig->Read("/Data/CallSign", wxT("")); - wxGetApp().m_textEncoding = pConfig->Read("/Data/TextEncoding", 1); - wxGetApp().m_enable_checksum = pConfig->Read("/Data/EnableChecksumOnMsgRx", f); - - wxGetApp().m_events = pConfig->Read("/Events/enable", f); - wxGetApp().m_events_spam_timer = (int)pConfig->Read(wxT("/Events/spam_timer"), 10); - wxGetApp().m_events_regexp_match = pConfig->Read("/Events/regexp_match", wxT("s=(.*)")); - wxGetApp().m_events_regexp_replace = pConfig->Read("/Events/regexp_replace", - wxT("curl http://qso.freedv.org/cgi-bin/onspot.cgi?s=\\1")); - // make sure regexp lists are terminated by a \n - - if (wxGetApp().m_events_regexp_match.Last() != '\n') { - wxGetApp().m_events_regexp_match = wxGetApp().m_events_regexp_match+'\n'; - } - if (wxGetApp().m_events_regexp_replace.Last() != '\n') { - wxGetApp().m_events_regexp_replace = wxGetApp().m_events_regexp_replace+'\n'; - } - - wxGetApp().m_udp_enable = (float)pConfig->Read(wxT("/UDP/enable"), f); - wxGetApp().m_udp_port = (int)pConfig->Read(wxT("/UDP/port"), 3000); - - wxGetApp().m_FreeDV700txClip = (float)pConfig->Read(wxT("/FreeDV700/txClip"), t); - - wxGetApp().m_debug_console = (float)pConfig->Read(wxT("/Debug/console"), f); - - int mode = pConfig->Read(wxT("/Audio/mode"), (long)0); - if (mode == 0) - m_rb1600->SetValue(1); - if (mode == 2) - m_rb700b->SetValue(1); - if (mode == 3) - m_rb700c->SetValue(1); - if (mode == 4) - m_rb800xa->SetValue(1); - - pConfig->SetPath(wxT("/")); - -// this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - //m_togRxID->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); - //m_togTxID->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnTxIDUI), NULL, this); - m_togBtnOnOff->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnOnOffUI), NULL, this); - m_togBtnSplit->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnSplitClickUI), NULL, this); - m_togBtnAnalog->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnAnalogClickUI), NULL, this); - //m_togBtnALC->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnALCClickUI), NULL, this); - // m_btnTogPTT->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnPTT_UI), NULL, this); - - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - m_btnTogPTT->Disable(); - m_togBtnVoiceKeyer->Disable(); - //m_togBtnALC->Disable(); - - // squelch settings - char sqsnr[15]; - m_sliderSQ->SetValue((int)((g_SquelchLevel+5.0)*2.0)); - sprintf(sqsnr, "%4.1f", g_SquelchLevel); - wxString sqsnr_string(sqsnr); - m_textSQ->SetLabel(sqsnr_string); - m_ckboxSQ->SetValue(g_SquelchActive); - - // SNR settings - - m_ckboxSNR->SetValue(wxGetApp().m_snrSlow); - setsnrBeta(wxGetApp().m_snrSlow); - -#ifdef _USE_TIMER - Bind(wxEVT_TIMER, &MainFrame::OnTimer, this); // ID_MY_WINDOW); - m_plotTimer.SetOwner(this, ID_TIMER_WATERFALL); - //m_panelWaterfall->Refresh(); -#endif - - m_RxRunning = false; - -#ifdef _USE_ONIDLE - Connect(wxEVT_IDLE, wxIdleEventHandler(MainFrame::OnIdle), NULL, this); -#endif //_USE_ONIDLE - - g_sfPlayFile = NULL; - g_playFileToMicIn = false; - g_loopPlayFileToMicIn = false; - - g_sfRecFile = NULL; - g_recFileFromRadio = false; - - g_sfPlayFileFromRadio = NULL; - g_playFileFromRadio = false; - g_loopPlayFileFromRadio = false; - - // init click-tune states - - g_RxFreqOffsetHz = 0.0; - g_RxFreqOffsetPhaseRect.real = cos(0.0); - g_RxFreqOffsetPhaseRect.imag = sin(0.0); - m_panelWaterfall->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelSpectrum->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - - g_TxFreqOffsetHz = 0.0; - g_TxFreqOffsetPhaseRect.real = cos(0.0); - g_TxFreqOffsetPhaseRect.imag = sin(0.0); - - g_tx = 0; - g_split = 0; - - // data states - g_txDataInFifo = fifo_create(MAX_CALLSIGN*VARICODE_MAX_BITS); - g_rxDataOutFifo = fifo_create(MAX_CALLSIGN*VARICODE_MAX_BITS); - - sox_biquad_start(); - - g_testFrames = 0; - g_test_frame_sync_state = 0; - g_total_bit_errors = 0; - g_total_bits = 0; - wxGetApp().m_testFrames = false; - - g_modal = false; - -#ifdef __EXPERIMENTAL_UDP__ - // Start UDP listener thread - - m_UDPThread = NULL; - startUDPThread(); -#endif - - optionsDlg = new OptionsDlg(NULL); - m_schedule_restore = false; - - vk_state = VK_IDLE; - - // Init optional Windows debug console so we can see all those printfs - -#ifdef __WXMSW__ - if (wxGetApp().m_debug_console) { - // somewhere to send printfs while developing - int ret = AllocConsole(); - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - fprintf(stderr, "AllocConsole: %d m_debug_console: %d\n", ret, wxGetApp().m_debug_console); - } -#endif - - //ftest = fopen("ftest.raw", "wb"); - //assert(ftest != NULL); -} - -//------------------------------------------------------------------------- -// ~MainFrame() -//------------------------------------------------------------------------- -MainFrame::~MainFrame() -{ - int x; - int y; - int w; - int h; - - fprintf(stderr, "MainFrame::~MainFrame()\n"); - //fclose(ftest); - #ifdef __WXMSW__ - fclose(g_logfile); - #endif - - if (optionsDlg != NULL) { - delete optionsDlg; - optionsDlg = NULL; - } - -#ifdef __EXPERIMENTAL_UDP__ - stopUDPThread(); -#endif - - if (wxGetApp().m_hamlib) delete wxGetApp().m_hamlib; - if (wxGetApp().m_serialport) delete wxGetApp().m_serialport; - - wxConfigBase *pConfig = wxConfigBase::Get(); - if(pConfig) - { - if (!IsIconized()) { - GetClientSize(&w, &h); - GetPosition(&x, &y); - printf("x = %d y = %d w = %d h = %d\n", x,y,w,h); - pConfig->Write(wxT("/MainFrame/left"), (long) x); - pConfig->Write(wxT("/MainFrame/top"), (long) y); - pConfig->Write(wxT("/MainFrame/width"), (long) w); - pConfig->Write(wxT("/MainFrame/height"), (long) h); - } - pConfig->Write(wxT("/MainFrame/show_wf"), wxGetApp().m_show_wf); - pConfig->Write(wxT("/MainFrame/show_spect"), wxGetApp().m_show_spect); - pConfig->Write(wxT("/MainFrame/show_scatter"), wxGetApp().m_show_scatter); - pConfig->Write(wxT("/MainFrame/show_timing"), wxGetApp().m_show_timing); - pConfig->Write(wxT("/MainFrame/show_freq"), wxGetApp().m_show_freq); - pConfig->Write(wxT("/MainFrame/show_speech_in"), wxGetApp().m_show_speech_in); - pConfig->Write(wxT("/MainFrame/show_speech_out"), wxGetApp().m_show_speech_out); - pConfig->Write(wxT("/MainFrame/show_demod_in"), wxGetApp().m_show_demod_in); - pConfig->Write(wxT("/MainFrame/show_test_frame_errors"), wxGetApp().m_show_test_frame_errors); - pConfig->Write(wxT("/MainFrame/show_test_frame_errors_hist"), wxGetApp().m_show_test_frame_errors_hist); - - pConfig->Write(wxT("/MainFrame/rxNbookCtrl"), wxGetApp().m_rxNbookCtrl); - - pConfig->Write(wxT("/Audio/SquelchActive"), g_SquelchActive); - pConfig->Write(wxT("/Audio/SquelchLevel"), (int)(g_SquelchLevel*2.0)); - - pConfig->Write(wxT("/Audio/framesPerBuffer"), wxGetApp().m_framesPerBuffer); - - pConfig->Write(wxT("/Audio/soundCard1InDeviceNum"), g_soundCard1InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1OutDeviceNum"), g_soundCard1OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1SampleRate"), g_soundCard1SampleRate ); - - pConfig->Write(wxT("/Audio/soundCard2InDeviceNum"), g_soundCard2InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2OutDeviceNum"), g_soundCard2OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2SampleRate"), g_soundCard2SampleRate ); - - pConfig->Write(wxT("/VoiceKeyer/WaveFilePath"), wxGetApp().m_txtVoiceKeyerWaveFilePath); - pConfig->Write(wxT("/VoiceKeyer/WaveFile"), wxGetApp().m_txtVoiceKeyerWaveFile); - pConfig->Write(wxT("/VoiceKeyer/RxPause"), wxGetApp().m_intVoiceKeyerRxPause); - pConfig->Write(wxT("/VoiceKeyer/Repeats"), wxGetApp().m_intVoiceKeyerRepeats); - - pConfig->Write(wxT("/Rig/HalfDuplex"), wxGetApp().m_boolHalfDuplex); - pConfig->Write(wxT("/Rig/leftChannelVoxTone"), wxGetApp().m_leftChannelVoxTone); - pConfig->Write("/Hamlib/UseForPTT", wxGetApp().m_boolHamlibUseForPTT); - pConfig->Write("/Hamlib/RigName", wxGetApp().m_intHamlibRig); - pConfig->Write("/Hamlib/SerialPort", wxGetApp().m_strHamlibSerialPort); - - - pConfig->Write(wxT("/File/playFileToMicInPath"), wxGetApp().m_playFileToMicInPath); - pConfig->Write(wxT("/File/recFileFromRadioPath"), wxGetApp().m_recFileFromRadioPath); - pConfig->Write(wxT("/File/recFileFromRadioSecs"), wxGetApp().m_recFileFromRadioSecs); - pConfig->Write(wxT("/File/playFileFromRadioPath"), wxGetApp().m_playFileFromRadioPath); - - pConfig->Write(wxT("/Audio/snrSlow"), wxGetApp().m_snrSlow); - - pConfig->Write(wxT("/Data/CallSign"), wxGetApp().m_callSign); - pConfig->Write(wxT("/Data/TextEncoding"), wxGetApp().m_textEncoding); - pConfig->Write(wxT("/Data/EnableChecksumOnMsgRx"), wxGetApp().m_enable_checksum); - pConfig->Write(wxT("/Events/enable"), wxGetApp().m_events); - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - pConfig->Write(wxT("/Events/regexp_match"), wxGetApp().m_events_regexp_match); - pConfig->Write(wxT("/Events/regexp_replace"), wxGetApp().m_events_regexp_replace); - - pConfig->Write(wxT("/UDP/enable"), wxGetApp().m_udp_enable); - pConfig->Write(wxT("/UDP/port"), wxGetApp().m_udp_port); - - pConfig->Write(wxT("/Filter/MicInEQEnable"), wxGetApp().m_MicInEQEnable); - pConfig->Write(wxT("/Filter/SpkOutEQEnable"), wxGetApp().m_SpkOutEQEnable); - - pConfig->Write(wxT("/FreeDV700/txClip"), wxGetApp().m_FreeDV700txClip); - - pConfig->Write(wxT("/Debug/console"), wxGetApp().m_debug_console); - - int mode; - if (m_rb1600->GetValue()) - mode = 0; - if (m_rb700b->GetValue()) - mode = 2; - if (m_rb700c->GetValue()) - mode = 3; - if (m_rb800xa->GetValue()) - mode = 4; - pConfig->Write(wxT("/Audio/mode"), mode); - } - - //m_togRxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); - //m_togTxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnTxIDUI), NULL, this); - m_togBtnOnOff->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnOnOffUI), NULL, this); - m_togBtnSplit->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnSplitClickUI), NULL, this); - m_togBtnAnalog->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnAnalogClickUI), NULL, this); - //m_togBtnALC->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnALCClickUI), NULL, this); - //m_btnTogPTT->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnPTT_UI), NULL, this); - - sox_biquad_finish(); - - if (m_RxRunning) - { - stopRxStream(); - } - if (g_sfPlayFile != NULL) - { - sf_close(g_sfPlayFile); - g_sfPlayFile = NULL; - } - if (g_sfRecFile != NULL) - { - sf_close(g_sfRecFile); - g_sfRecFile = NULL; - } -#ifdef _USE_TIMER - if(m_plotTimer.IsRunning()) - { - m_plotTimer.Stop(); - Unbind(wxEVT_TIMER, &MainFrame::OnTimer, this); - } -#endif //_USE_TIMER - -#ifdef _USE_ONIDLE - Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainFrame::OnIdle), NULL, this); -#endif // _USE_ONIDLE - - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - - -#ifdef _USE_ONIDLE -void MainFrame::OnIdle(wxIdleEvent &evt) { -} -#endif - - -#ifdef _USE_TIMER -//---------------------------------------------------------------- -// OnTimer() -// -// when the timer fires every DT seconds we update the GUI displays. -// the tabs only the plot that is visible actually gets updated, this -// keeps CPU load reasonable -//---------------------------------------------------------------- -void MainFrame::OnTimer(wxTimerEvent &evt) -{ - - int r,c; - - if (m_panelWaterfall->checkDT()) { - m_panelWaterfall->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelWaterfall->m_newdata = true; - m_panelWaterfall->Refresh(); - } - - m_panelSpectrum->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelSpectrum->m_newdata = true; - m_panelSpectrum->Refresh(); - - /* update scatter/eye plot ------------------------------------------------------------*/ - - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_800XA) { - /* FSK Mode - eye diagram ---------------------------------------------------------*/ - - /* add samples row by row */ - - int i; - for (i=0; iadd_new_samples_eye(&g_stats.rx_eye[i][0], g_stats.neyesamp); - } - } - else { - /* PSK Modes - scatter plot -------------------------------------------------------*/ - for (r=0; radd_new_samples_scatter(&g_stats.rx_symbols[r][0]); - } - - if ((freedv_get_mode(g_pfreedv) == FREEDV_MODE_700B) || (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C)) { - - /* - FreeDV 700 uses diversity, so combine symbols for - scatter plot, as combined symbols are used for - demodulation. Note we need to use a copy of the - symbols, as we are not sure when the stats will be - updated. - */ - - COMP rx_symbols_copy[g_Nc/2]; - - for(c=0; cadd_new_samples_scatter(rx_symbols_copy); - } - - } - } - - m_panelScatter->Refresh(); - - // Oscilliscope type speech plots ------------------------------------------------------- - - short speechInPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotSpeechInFifo, speechInPlotSamples, WAVEFORM_PLOT_BUF)) { - memset(speechInPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - //fprintf(stderr, "empty!\n"); - } - m_panelSpeechIn->add_new_short_samples(0, speechInPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelSpeechIn->Refresh(); - - short speechOutPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotSpeechOutFifo, speechOutPlotSamples, WAVEFORM_PLOT_BUF)) - memset(speechOutPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - m_panelSpeechOut->add_new_short_samples(0, speechOutPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelSpeechOut->Refresh(); - - short demodInPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotDemodInFifo, demodInPlotSamples, WAVEFORM_PLOT_BUF)) { - memset(demodInPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - } - m_panelDemodIn->add_new_short_samples(0,demodInPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelDemodIn->Refresh(); - - // Demod states ----------------------------------------------------------------------- - - m_panelTimeOffset->add_new_sample(0, (float)g_stats.rx_timing/FDMDV_NOM_SAMPLES_PER_FRAME); - m_panelTimeOffset->Refresh(); - - m_panelFreqOffset->add_new_sample(0, g_stats.foff); - m_panelFreqOffset->Refresh(); - - // SNR text box and gauge ------------------------------------------------------------ - - // LP filter g_stats.snr_est some more to stabilise the - // display. g_stats.snr_est already has some low pass filtering - // but we need it fairly fast to activate squelch. So we - // optionally perform some further filtering for the display - // version of SNR. The "Slow" checkbox controls the amount of - // filtering. The filtered snr also controls the squelch - - g_snr = m_snrBeta*g_snr + (1.0 - m_snrBeta)*g_stats.snr_est; - float snr_limited = g_snr; - if (snr_limited < -5.0) snr_limited = -5.0; - if (snr_limited > 20.0) snr_limited = 20.0; - - char snr[15]; - sprintf(snr, "%d", (int)(g_snr+0.5)); // round to nearest dB - - //printf("snr_est: %f m_snrBeta: %f g_snr: %f snr_limited: %f\n", g_stats.snr_est, m_snrBeta, g_snr, snr_limited); - - wxString snr_string(snr); - m_textSNR->SetLabel(snr_string); - m_gaugeSNR->SetValue((int)(snr_limited+5)); - - - // Level Gauge ----------------------------------------------------------------------- - - float tooHighThresh; - if (!g_tx && m_RxRunning) - { - // receive mode - display From Radio peaks - // peak from this DT sampling period - int maxDemodIn = 0; - for(int i=0; i m_maxLevel) - m_maxLevel = maxDemodIn; - - tooHighThresh = FROM_RADIO_MAX; - } - else - { - // transmit mode - display From Mic peaks - - // peak from this DT sampling period - int maxSpeechIn = 0; - for(int i=0; i m_maxLevel) - m_maxLevel = maxSpeechIn; - - tooHighThresh = FROM_MIC_MAX; - } - - // Peak Reading meter: updates peaks immediately, then slowly decays - int maxScaled = (int)(100.0 * ((float)m_maxLevel/32767.0)); - m_gaugeLevel->SetValue(maxScaled); - //printf("maxScaled: %d\n", maxScaled); - if (((float)maxScaled/100) > tooHighThresh) - m_textLevel->SetLabel("Too High"); - else - m_textLevel->SetLabel(""); - - m_maxLevel *= LEVEL_BETA; - - // sync LED (Colours don't work on Windows) ------------------------ - - if (g_State) { - m_rbSync->SetForegroundColour( wxColour( 0, 255, 0 ) ); // green - m_rbSync->SetValue(true); - } - else { - m_rbSync->SetForegroundColour( wxColour( 255, 0, 0 ) ); // red - m_rbSync->SetValue(false); - } - - // send Callsign ---------------------------------------------------- - - char callsign[MAX_CALLSIGN]; - strncpy(callsign, (const char*) wxGetApp().m_callSign.mb_str(wxConvUTF8), MAX_CALLSIGN-1); - - // buffer 1 txt message to ensure tx data fifo doesn't "run dry" - - if ((unsigned)fifo_used(g_txDataInFifo) < strlen(callsign)) { - unsigned int i; - - //fprintf(g_logfile, "tx callsign: %s.\n", callsign); - - /* optionally append checksum */ - - if (wxGetApp().m_enable_checksum) { - - unsigned char checksum = 0; - char callsign_checksum_cr[MAX_CALLSIGN+1]; - - for(i=0; i MAX_CALLSIGN-1)) { - // CR completes line - *m_pcallsign = 0; - - /* Checksums can be disabled, e.g. for compatability with - older vesions. In that case we print msg but don't do - any event processing. If checksums enabled, only print - out when checksum is good. */ - - if (wxGetApp().m_enable_checksum) { - // lets see if checksum is OK - - unsigned char checksum_rx = 0; - if (strlen(m_callsign) > 2) { - for(unsigned int i=0; iSetValue(s); - -#ifdef __UDP_EXPERIMENTAL__ - char s1[MAX_CALLSIGN]; - sprintf(s1,"rx_txtmsg %s", m_callsign); - processTxtEvent(s1); - - m_checksumGood++; - s.Printf("%d", m_checksumGood); - m_txtChecksumGood->SetLabel(s); -#endif - } - else { -#ifdef __UDP_EXPERIMENTAL__ - m_checksumBad++; - s.Printf("%d", m_checksumBad); - m_txtChecksumBad->SetLabel(s); -#endif - } - } - - //fprintf(g_logfile,"resetting callsign %s %ld\n", m_callsign, m_pcallsign-m_callsign); - // reset ptr to start of string - m_pcallsign = m_callsign; - } - else { - //fprintf(g_logfile, "new char %d %c\n", ashort, (char)ashort); - *m_pcallsign++ = (char)ashort; - } - - /* If checksums disabled, display txt chars as they arrive */ - - if (!wxGetApp().m_enable_checksum) { - m_txtCtrlCallSign->SetValue(m_callsign); - } - } - - // Run time update of EQ filters ----------------------------------- - if (m_newMicInFilter || m_newSpkOutFilter) { - g_mutexProtectingCallbackData.Lock(); - deleteEQFilters(g_rxUserdata); - designEQFilters(g_rxUserdata); - g_mutexProtectingCallbackData.Unlock(); - m_newMicInFilter = m_newSpkOutFilter = false; - } - g_rxUserdata->micInEQEnable = wxGetApp().m_MicInEQEnable; - g_rxUserdata->spkOutEQEnable = wxGetApp().m_SpkOutEQEnable; - - - if (g_mode != -1) { - - // Run time update of FreeDV 700 tx clipper - - freedv_set_clip(g_pfreedv, (int)wxGetApp().m_FreeDV700txClip); - - // Test Frame Bit Error Updates ------------------------------------ - - // Toggle test frame mode at run time - - if (!freedv_get_test_frames(g_pfreedv) && wxGetApp().m_testFrames) { - - // reset stats on check box off to on transition - - freedv_set_test_frames(g_pfreedv, 1); - freedv_set_total_bits(g_pfreedv, 0); - freedv_set_total_bit_errors(g_pfreedv, 0); - } - freedv_set_test_frames(g_pfreedv, wxGetApp().m_testFrames); - g_channel_noise = wxGetApp().m_channel_noise; - - if (g_State) { - char bits[80], errors[80], ber[80]; - - // update stats on main page - - sprintf(bits, "Bits: %d", freedv_get_total_bits(g_pfreedv)); wxString bits_string(bits); m_textBits->SetLabel(bits_string); - sprintf(errors, "Errs: %d", freedv_get_total_bit_errors(g_pfreedv)); wxString errors_string(errors); m_textErrors->SetLabel(errors_string); - float b = (float)freedv_get_total_bit_errors(g_pfreedv)/(1E-6+freedv_get_total_bits(g_pfreedv)); - sprintf(ber, "BER: %4.3f", b); wxString ber_string(ber); m_textBER->SetLabel(ber_string); - - // update error pattern plots if supported - - int sz_error_pattern = freedv_get_sz_error_pattern(g_pfreedv); - if (sz_error_pattern) { - short error_pattern[sz_error_pattern]; - - if (fifo_read(g_error_pattern_fifo, error_pattern, sz_error_pattern) == 0) { - int i,b; - - /* both modes map IQ to alternate bits, but one same carrier */ - - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_1600) { - /* FreeDV 1600 mapping from error pattern to bit on each carrier */ - - for(b=0; badd_new_sample(b, b + 0.8*error_pattern[i]); - g_error_hist[b] += error_pattern[i]; - } - //if (b%2) - // printf("g_error_hist[%d]: %d\n", b/2, g_error_hist[b/2]); - } - - int max_hist = 0; - for(b=0; b max_hist) - max_hist = g_error_hist[b]; - - m_panelTestFrameErrorsHist->add_new_short_samples(0, g_error_hist, 2*FDMDV_NC_MAX, max_hist); - } - - if ((freedv_get_mode(g_pfreedv) == FREEDV_MODE_700B) || (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C)) { - int c; - - /* FreeDV 700 mapping from error pattern to bit on each - carrier. Note we don't have access to carriers before - diversity re-combination, so this won't give us the full - picture, we have to assume Nc/2 carriers. */ - - for(i=0; iadd_new_sample(c, c + 0.8*error_pattern[i]); - g_error_hist[c] += error_pattern[i]; - //printf("i: %d c: %d\n", i, c); - } - - int max_hist = 0; - for(b=0; b max_hist) - max_hist = g_error_hist[b]; - m_panelTestFrameErrorsHist->add_new_short_samples(0, g_error_hist, 2*FDMDV_NC_MAX, max_hist); - } - - m_panelTestFrameErrors->Refresh(); - m_panelTestFrameErrorsHist->Refresh(); - } - } - } - } - - // command from UDP thread that is best processed in main thread to avoid seg faults - - if (m_schedule_restore) { - if (IsIconized()) - Restore(); - m_schedule_restore = false; - } - -#ifdef __UDP_EXPERIMENTAL__ - // Light Spam Timer LED if at least one timer is running - - int i; - optionsDlg->SetSpamTimerLight(false); - for(i=0; iSetSpamTimerLight(true); -#endif - - // Blink file playback status line - - if (g_playFileFromRadio) { - g_blink += DT; - //fprintf(g_logfile, "g_blink: %f\n", g_blink); - if ((g_blink >= 1.0) && (g_blink < 2.0)) - SetStatusText(wxT("Playing into from radio"), 0); - if (g_blink >= 2.0) { - SetStatusText(wxT(""), 0); - g_blink = 0.0; - } - } - - // Voice Keyer state machine - - VoiceKeyerProcessEvent(VK_DT); -} -#endif - - -//------------------------------------------------------------------------- -// OnCloseFrame() -//------------------------------------------------------------------------- -void MainFrame::OnCloseFrame(wxCloseEvent& event) -{ - fprintf(stderr, "MainFrame::OnCloseFrame()\n"); - Pa_Terminate(); - Destroy(); -} - -//------------------------------------------------------------------------- -// OnTop() -//------------------------------------------------------------------------- -void MainFrame::OnTop(wxCommandEvent& event) -{ - int style = GetWindowStyle(); - - if (style & wxSTAY_ON_TOP) - { - style &= ~wxSTAY_ON_TOP; - } - else - { - style |= wxSTAY_ON_TOP; - } - SetWindowStyle(style); -} - -//------------------------------------------------------------------------- -// OnDeleteConfig() -//------------------------------------------------------------------------- -void MainFrame::OnDeleteConfig(wxCommandEvent&) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - if(pConfig->DeleteAll()) - { - wxLogMessage(wxT("Config file/registry key successfully deleted.")); - - delete wxConfigBase::Set(NULL); - wxConfigBase::DontCreateOnDemand(); - } - else - { - wxLogError(wxT("Deleting config file/registry key failed.")); - } -} - -//------------------------------------------------------------------------- -// Paint() -//------------------------------------------------------------------------- -void MainFrame::OnPaint(wxPaintEvent& WXUNUSED(event)) -{ - wxPaintDC dc(this); - - if(GetMenuBar()->IsChecked(ID_PAINT_BG)) - { - dc.Clear(); - } - dc.SetUserScale(m_zoom, m_zoom); -} - -//------------------------------------------------------------------------- -// OnCmdSliderScroll() -//------------------------------------------------------------------------- -void MainFrame::OnCmdSliderScroll(wxScrollEvent& event) -{ - char sqsnr[15]; - g_SquelchLevel = (float)m_sliderSQ->GetValue()/2.0 - 5.0; - sprintf(sqsnr, "%4.1f", g_SquelchLevel); // 0.5 dB steps - wxString sqsnr_string(sqsnr); - m_textSQ->SetLabel(sqsnr_string); - - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnCheckSQClick() -//------------------------------------------------------------------------- -void MainFrame::OnCheckSQClick(wxCommandEvent& event) -{ - if(!g_SquelchActive) - { - g_SquelchActive = true; - } - else - { - g_SquelchActive = false; - } -} - -void MainFrame::setsnrBeta(bool snrSlow) -{ - if(snrSlow) - { - m_snrBeta = 0.95; // make this closer to 1.0 to smooth SNR est further - } - else - { - m_snrBeta = 0.0; // no smoothing of SNR estimate from demodulator - } -} - -//------------------------------------------------------------------------- -// OnCheckSQClick() -//------------------------------------------------------------------------- -void MainFrame::OnCheckSNRClick(wxCommandEvent& event) -{ - wxGetApp().m_snrSlow = m_ckboxSNR->GetValue(); - setsnrBeta(wxGetApp().m_snrSlow); - //printf("m_snrSlow: %d\n", (int)wxGetApp().m_snrSlow); -} - -// check for space bar press (only when running) - -int MainApp::FilterEvent(wxEvent& event) -{ - if ((event.GetEventType() == wxEVT_KEY_DOWN) && - (((wxKeyEvent&)event).GetKeyCode() == WXK_SPACE)) - { - // only use space to toggle PTT if we are running and no modal dialogs (like options) up - //fprintf(stderr,"frame->m_RxRunning: %d g_modal: %d\n", - // frame->m_RxRunning, g_modal); - if (frame->m_RxRunning && !g_modal) { - - // space bar controls rx/rx if keyer not running - if (frame->vk_state == VK_IDLE) { - if (frame->m_btnTogPTT->GetValue()) - frame->m_btnTogPTT->SetValue(false); - else - frame->m_btnTogPTT->SetValue(true); - - frame->togglePTT(); - } - else // spavce bar stops keyer - frame->VoiceKeyerProcessEvent(VK_SPACE_BAR); - - return true; // absorb space so we don't toggle control with focus (e.g. Start) - - } - } - - return -1; -} - -//------------------------------------------------------------------------- -// OnTogBtnPTT () -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnPTT (wxCommandEvent& event) -{ - togglePTT(); - event.Skip(); -} - -void MainFrame::togglePTT(void) { - - // Change tabbed page in centre panel depending on PTT state - - if (g_tx) - { - // tx-> rx transition, swap to the page we were on for last rx - m_auiNbookCtrl->ChangeSelection(wxGetApp().m_rxNbookCtrl); - } - else - { - // rx-> tx transition, swap to Mic In page to monitor speech - wxGetApp().m_rxNbookCtrl = m_auiNbookCtrl->GetSelection(); - m_auiNbookCtrl->ChangeSelection(m_auiNbookCtrl->GetPageIndex((wxWindow *)m_panelSpeechIn)); -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"ptt"); processTxtEvent(e); -#endif - } - - g_tx = m_btnTogPTT->GetValue(); - - // Hamlib PTT - - if (wxGetApp().m_boolHamlibUseForPTT) { - Hamlib *hamlib = wxGetApp().m_hamlib; - wxString hamlibError; - if (wxGetApp().m_boolHamlibUseForPTT && hamlib != NULL) { - if (hamlib->ptt(g_tx, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - } - } - - // Serial PTT - - if (wxGetApp().m_boolUseSerialPTT && (wxGetApp().m_serialport->isopen())) { - wxGetApp().m_serialport->ptt(g_tx); - } - - // reset level gauge - - m_maxLevel = 0; - m_textLevel->SetLabel(wxT("")); - m_gaugeLevel->SetValue(0); -} - -/* - Voice Keyer: - - + space bar turns keyer off - + 5 secs of valid sync turns it off - - [X] complete state machine and builds OK - [ ] file select dialog - [ ] test all states - [ ] restore size -*/ - -void MainFrame::OnTogBtnVoiceKeyerClick (wxCommandEvent& event) -{ - if (vk_state == VK_IDLE) - VoiceKeyerProcessEvent(VK_START); - else - VoiceKeyerProcessEvent(VK_SPACE_BAR); - - event.Skip(); -} - - -int MainFrame::VoiceKeyerStartTx(void) -{ - int next_state; - - // start playing wave file or die trying - - SF_INFO sfInfo; - sfInfo.format = 0; - - g_sfPlayFile = sf_open(wxGetApp().m_txtVoiceKeyerWaveFile, SFM_READ, &sfInfo); - if(g_sfPlayFile == NULL) { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open:") + wxGetApp().m_txtVoiceKeyerWaveFile, wxOK); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - else { - SetStatusText(wxT("Voice Keyer: Playing File") + wxGetApp().m_txtVoiceKeyerWaveFile + wxT(" to Mic Input") , 0); - g_loopPlayFileToMicIn = false; - g_playFileToMicIn = true; - - m_btnTogPTT->SetValue(true); togglePTT(); - next_state = VK_TX; - } - - return next_state; -} - - -void MainFrame::VoiceKeyerProcessEvent(int vk_event) { - int next_state = vk_state; - - switch(vk_state) { - - case VK_IDLE: - if (vk_event == VK_START) { - // sample these puppies at start just in case they are changed while VK running - vk_rx_pause = wxGetApp().m_intVoiceKeyerRxPause; - vk_repeats = wxGetApp().m_intVoiceKeyerRepeats; - fprintf(stderr, "vk_rx_pause: %d vk_repeats: %d\n", vk_rx_pause, vk_repeats); - - vk_repeat_counter = 0; - next_state = VoiceKeyerStartTx(); - } - break; - - case VK_TX: - - // In this state we are transmitting and playing a wave file - // to Mic In - - if (vk_event == VK_SPACE_BAR) { - m_btnTogPTT->SetValue(false); togglePTT(); - StopPlayFileToMicIn(); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if (vk_event == VK_PLAY_FINISHED) { - m_btnTogPTT->SetValue(false); togglePTT(); - vk_repeat_counter++; - if (vk_repeat_counter > vk_repeats) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - else { - vk_rx_time = 0.0; - next_state = VK_RX; - } - } - - break; - - case VK_RX: - - // in this state we are receiving and waiting for - // delay timer or valid sync - - if (vk_event == VK_DT) { - if (freedv_get_sync(g_pfreedv) == 1) { - // if we detect sync simulate a smooth transition to SYNC_WAIT State - TODO: review - if (vk_rx_time >= DT) { - vk_rx_time -= DT; - } else { - next_state = VK_SYNC_WAIT; - } - } else { - vk_rx_time += DT; - if (vk_rx_time >= vk_rx_pause) { - next_state = VoiceKeyerStartTx(); - } - } - } - - if (vk_event == VK_SPACE_BAR) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - break; - - case VK_SYNC_WAIT: - - // In this state we wait for valid sync to last - // VK_SYNC_WAIT_TIME seconds - - if (vk_event == VK_SPACE_BAR) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if (vk_event == VK_DT) { - if (freedv_get_sync(g_pfreedv) == 0) { - // if we lose sync simulate a smooth transition to return in RX State - TODO: review - if (vk_rx_time >= DT) { - vk_rx_time -= DT; - } else { - next_state = VK_RX; - } - } else { - vk_rx_time += DT; - } - - // drop out of voice keyer if we get a few seconds of valid sync - - if (vk_rx_time >= VK_SYNC_WAIT_TIME) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - } - break; - - default: - // catch anything we missed - - m_btnTogPTT->SetValue(false); togglePTT(); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if ((vk_event != VK_DT) || (vk_state != next_state)) - fprintf(stderr, "VoiceKeyerProcessEvent: vk_state: %d vk_event: %d next_state: %d vk_repeat_counter: %d\n", vk_state, vk_event, next_state, vk_repeat_counter); - vk_state = next_state; -} - - -//------------------------------------------------------------------------- -// OnTogBtnRxID() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnRxID(wxCommandEvent& event) -{ - // empty any junk in rx data FIFO - short junk; - while(fifo_read(g_rxDataOutFifo,&junk,1) == 0); - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnTogBtnTxID() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnTxID(wxCommandEvent& event) -{ - event.Skip(); -} - -void MainFrame::OnTogBtnSplitClick(wxCommandEvent& event) { - if (g_split) - g_split = 0; - else - g_split = 1; - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnTogBtnAnalogClick() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnAnalogClick (wxCommandEvent& event) -{ - if (g_analog == 0) { - g_analog = 1; - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(FS/2))); - m_panelWaterfall->setFs(FS); - } - else { - g_analog = 0; - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(freedv_get_modem_sample_rate(g_pfreedv)/2))); - m_panelWaterfall->setFs(freedv_get_modem_sample_rate(g_pfreedv)); - } - - g_State = 0; - g_stats.snr_est = 0; - - event.Skip(); -} - -void MainFrame::OnCallSignReset(wxCommandEvent& event) -{ - m_pcallsign = m_callsign; - memset(m_callsign, 0, MAX_CALLSIGN); - wxString s; - s.Printf("%s", m_callsign); - m_txtCtrlCallSign->SetValue(s); -#ifdef __UDP__EXPERIMENTAL__ - m_checksumGood = m_checksumBad = 0; - m_txtChecksumGood->SetLabel(_("0")); - m_txtChecksumBad->SetLabel(_("0")); -#endif -} - -void MainFrame::OnBerReset(wxCommandEvent& event) -{ - freedv_set_total_bits(g_pfreedv, 0); - freedv_set_total_bit_errors(g_pfreedv, 0); - int i; - for(i=0; i<2*g_Nc; i++) - g_error_hist[i] = 0; - -} - -#ifdef ALC -//------------------------------------------------------------------------- -// OnTogBtnALCClick() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnALCClick(wxCommandEvent& event) -{ - wxMessageBox(wxT("Got Click!"), wxT("OnTogBtnALCClick"), wxOK); - - event.Skip(); -} -#endif - -// extra panel added to file open dialog to add loop checkbox -MyExtraPlayFilePanel::MyExtraPlayFilePanel(wxWindow *parent): wxPanel(parent) -{ - m_cb = new wxCheckBox(this, -1, wxT("Loop")); - m_cb->SetToolTip(_("When checked file will repeat forever")); - m_cb->SetValue(g_loopPlayFileToMicIn); - - // bug: I can't this to align right..... - wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL); - sizerTop->Add(m_cb, 0, wxALIGN_RIGHT, 0); - SetSizerAndFit(sizerTop); -} - -static wxWindow* createMyExtraPlayFilePanel(wxWindow *parent) -{ - return new MyExtraPlayFilePanel(parent); -} - -void MainFrame::StopPlayFileToMicIn(void) -{ - g_mutexProtectingCallbackData.Lock(); - g_playFileToMicIn = false; - sf_close(g_sfPlayFile); - SetStatusText(wxT("")); - g_mutexProtectingCallbackData.Unlock(); -} - -//------------------------------------------------------------------------- -// OnPlayFileToMicIn() -//------------------------------------------------------------------------- -void MainFrame::OnPlayFileToMicIn(wxCommandEvent& event) -{ - wxUnusedVar(event); - - if(g_playFileToMicIn) { - StopPlayFileToMicIn(); - VoiceKeyerProcessEvent(VK_PLAY_FINISHED); - } - else - { - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Play File to Mic In"), - wxGetApp().m_playFileToMicInPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_OPEN | wxFD_FILE_MUST_EXIST - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraPlayFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_playFileToMicInPath, &fileName, &extension); - //wxLogDebug("m_playFileToMicInPath: %s", wxGetApp().m_playFileToMicInPath); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = FS; - } - } - g_sfPlayFile = sf_open(soundFile.c_str(), SFM_READ, &sfInfo); - if(g_sfPlayFile == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - - // Huh?! I just copied wxWidgets-2.9.4/samples/dialogs .... - g_loopPlayFileToMicIn = static_cast(ctrl)->getLoopPlayFileToMicIn(); - - SetStatusText(wxT("Playing File: ") + fileName + wxT(" to Mic Input") , 0); - g_playFileToMicIn = true; - } -} - -//------------------------------------------------------------------------- -// OnPlayFileFromRadio() -// This puppy "plays" a recorded file into the demodulator input, allowing us -// to replay off air signals. -//------------------------------------------------------------------------- -void MainFrame::OnPlayFileFromRadio(wxCommandEvent& event) -{ - wxUnusedVar(event); - - printf("OnPlayFileFromRadio:: %d\n", (int)g_playFileFromRadio); - if (g_playFileFromRadio) - { - printf("OnPlayFileFromRadio:: Stop\n"); - g_mutexProtectingCallbackData.Lock(); - g_playFileFromRadio = false; - sf_close(g_sfPlayFileFromRadio); - SetStatusText(wxT(""),0); - SetStatusText(wxT(""),1); - g_mutexProtectingCallbackData.Unlock(); - } - else - { - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Play File - From Radio"), - wxGetApp().m_playFileFromRadioPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_OPEN | wxFD_FILE_MUST_EXIST - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraPlayFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_playFileFromRadioPath, &fileName, &extension); - //wxLogDebug("m_playFileToFromRadioPath: %s", wxGetApp().m_playFileFromRadioPath); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } - } - g_sfPlayFileFromRadio = sf_open(soundFile.c_str(), SFM_READ, &sfInfo); - g_sfFs = sfInfo.samplerate; - if(g_sfPlayFileFromRadio == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - - // Huh?! I just copied wxWidgets-2.9.4/samples/dialogs .... - g_loopPlayFileFromRadio = static_cast(ctrl)->getLoopPlayFileToMicIn(); - - SetStatusText(wxT("Playing into from radio"), 0); - if(extension == wxT("raw")) { - wxString stringnumber = wxString::Format(wxT("%d"), (int)sfInfo.samplerate); - SetStatusText(wxT("raw file assuming Fs=") + stringnumber, 1); - } - fprintf(g_logfile, "OnPlayFileFromRadio:: Playing File\n"); - g_playFileFromRadio = true; - g_blink = 0.0; - } -} - -// extra panel added to file save dialog to set number of seconds to record for - -MyExtraRecFilePanel::MyExtraRecFilePanel(wxWindow *parent): wxPanel(parent) -{ - wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL); - - wxStaticText* staticText = new wxStaticText(this, wxID_ANY, _("Seconds:"), wxDefaultPosition, wxDefaultSize, 0); - sizerTop->Add(staticText, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_secondsToRecord = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - m_secondsToRecord->SetToolTip(_("Number of seconds to record for")); - m_secondsToRecord->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_recFileFromRadioSecs)); - sizerTop->Add(m_secondsToRecord, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5); - SetSizerAndFit(sizerTop); -} - -static wxWindow* createMyExtraRecFilePanel(wxWindow *parent) -{ - return new MyExtraRecFilePanel(parent); -} - -//------------------------------------------------------------------------- -// OnRecFileFromRadio() -//------------------------------------------------------------------------- -void MainFrame::OnRecFileFromRadio(wxCommandEvent& event) -{ - wxUnusedVar(event); - - if (g_recFileFromRadio) { - printf("Stopping Record....\n"); - g_mutexProtectingCallbackData.Lock(); - g_recFileFromRadio = false; - sf_close(g_sfRecFile); - SetStatusText(wxT("")); - g_mutexProtectingCallbackData.Unlock(); - } - else { - - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Record File From Radio"), - wxGetApp().m_recFileFromRadioPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_SAVE - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraRecFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_recFileFromRadioPath, &fileName, &extension); - wxLogDebug("m_recFileFromRadioPath: %s", wxGetApp().m_recFileFromRadioPath); - wxLogDebug("soundFile: %s", soundFile); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } - else if(extension == wxT("wav")) - { - sfInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } else { - wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); - return; - } - } - else { - wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); - return; - } - - // Bug: on Win32 I cant read m_recFileFromRadioSecs, so have hard coded it -#ifdef __WIN32__ - long secs = wxGetApp().m_recFileFromRadioSecs; - g_recFromRadioSamples = FS*(unsigned int)secs; -#else - // work out number of samples to record - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - wxString secsString = static_cast(ctrl)->getSecondsToRecord(); - wxLogDebug("test: %s secsString: %s\n", wxT("testing 123"), secsString); - - long secs; - if (secsString.ToLong(&secs)) { - wxGetApp().m_recFileFromRadioSecs = (unsigned int)secs; - //printf(" secondsToRecord: %d\n", (unsigned int)secs); - g_recFromRadioSamples = FS*(unsigned int)secs; - //printf("g_recFromRadioSamples: %d\n", g_recFromRadioSamples); - } - else { - wxMessageBox(wxT("Invalid number of Seconds"), wxT("Record File From Radio"), wxOK); - return; - } -#endif - - g_sfRecFile = sf_open(soundFile.c_str(), SFM_WRITE, &sfInfo); - if(g_sfRecFile == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - SetStatusText(wxT("Recording File: ") + fileName + wxT(" From Radio") , 0); - g_recFileFromRadio = true; - } - -} - -//------------------------------------------------------------------------- -// OnExit() -//------------------------------------------------------------------------- -void MainFrame::OnExit(wxCommandEvent& event) -{ - fprintf(stderr, "MainFrame::OnExit\n"); - wxUnusedVar(event); -#ifdef _USE_TIMER - m_plotTimer.Stop(); -#endif // _USE_TIMER - if(g_sfPlayFile != NULL) - { - sf_close(g_sfPlayFile); - g_sfPlayFile = NULL; - } - if(g_sfRecFile != NULL) - { - sf_close(g_sfRecFile); - g_sfRecFile = NULL; - } - if(m_RxRunning) - { - stopRxStream(); - } - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - //m_togBtnALC->Disable(); - //m_btnTogPTT->Disable(); - Pa_Terminate(); - Destroy(); -} - -//------------------------------------------------------------------------- -// OnExitClick() -//------------------------------------------------------------------------- -void MainFrame::OnExitClick(wxCommandEvent& event) -{ - OnExit(event); -} - -//------------------------------------------------------------------------- -// OnToolsAudio() -//------------------------------------------------------------------------- -void MainFrame::OnToolsAudio(wxCommandEvent& event) -{ - wxUnusedVar(event); - int rv = 0; - AudioOptsDialog *dlg = new AudioOptsDialog(NULL); - rv = dlg->ShowModal(); - if(rv == wxID_OK) - { - dlg->ExchangeData(EXCHANGE_DATA_OUT); - } - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsAudioUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsAudioUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning); -} - -//------------------------------------------------------------------------- -// OnToolsFilter() -//------------------------------------------------------------------------- -void MainFrame::OnToolsFilter(wxCommandEvent& event) -{ - wxUnusedVar(event); - FilterDlg *dlg = new FilterDlg(NULL, m_RxRunning, &m_newMicInFilter, &m_newSpkOutFilter); - dlg->ShowModal(); - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsOptions() -//------------------------------------------------------------------------- -void MainFrame::OnToolsOptions(wxCommandEvent& event) -{ - wxUnusedVar(event); - g_modal = true; - //fprintf(stderr,"g_modal: %d\n", g_modal); - optionsDlg->Show(); -} - -//------------------------------------------------------------------------- -// OnToolsOptionsUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsOptionsUI(wxUpdateUIEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnToolsComCfg() -//------------------------------------------------------------------------- -void MainFrame::OnToolsComCfg(wxCommandEvent& event) -{ - wxUnusedVar(event); - - ComPortsDlg *dlg = new ComPortsDlg(NULL); - - dlg->ShowModal(); - - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsComCfgUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsComCfgUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning); -} - -//------------------------------------------------------------------------- -// OnToolsPlugInCfg() -//------------------------------------------------------------------------- -void MainFrame::OnToolsPlugInCfg(wxCommandEvent& event) -{ - wxUnusedVar(event); - PlugInDlg *dlg = new PlugInDlg(wxGetApp().m_plugInName, wxGetApp().m_numPlugInParam, wxGetApp().m_plugInParamName); - dlg->ShowModal(); - delete dlg; -} - -void MainFrame::OnToolsPlugInCfgUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning && wxGetApp().m_plugIn); -} - - -//------------------------------------------------------------------------- -// OnHelpCheckUpdates() -//------------------------------------------------------------------------- -void MainFrame::OnHelpCheckUpdates(wxCommandEvent& event) -{ - wxMessageBox("Got Click!", "OnHelpCheckUpdates", wxOK); - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnHelpCheckUpdatesUI() -//------------------------------------------------------------------------- -void MainFrame::OnHelpCheckUpdatesUI(wxUpdateUIEvent& event) -{ - event.Enable(false); -} - -//------------------------------------------------------------------------- -//OnHelpAbout() -//------------------------------------------------------------------------- -void MainFrame::OnHelpAbout(wxCommandEvent& event) -{ - wxUnusedVar(event); - wxString msg; - msg.Printf( wxT("FreeDV %s\n\n") - wxT("Open Source Digital Voice\n\n") - wxT("For Help and Support visit: http://freedv.org\n\n") - - wxT("GNU Public License V2.1\n\n") - wxT("Copyright (c) David Witten KD0EAG and David Rowe VK5DGR\n\n") - wxT("svn revision: %s\n"), FREEDV_VERSION, SVN_REVISION); - - wxMessageBox(msg, wxT("About"), wxOK | wxICON_INFORMATION, this); -} - - -// Attempt to talk to rig using Hamlib - -bool MainFrame::OpenHamlibRig() { - if (wxGetApp().m_boolHamlibUseForPTT != true) - return false; - if (wxGetApp().m_intHamlibRig == 0) - return false; - if (wxGetApp().m_hamlib == NULL) - return false; - - int rig = wxGetApp().m_intHamlibRig; - wxString port = wxGetApp().m_strHamlibSerialPort; - bool status = wxGetApp().m_hamlib->connect(rig, port.mb_str(wxConvUTF8)); - if (status == false) - wxMessageBox("Couldn't connect to Radio with hamlib", wxT("Error"), wxOK | wxICON_ERROR, this); - - return status; -} - -//------------------------------------------------------------------------- -// OnTogBtnOnOff() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnOnOff(wxCommandEvent& event) -{ - wxString startStop = m_togBtnOnOff->GetLabel(); - - // we are attempting to start - - if (startStop.IsSameAs("Start")) - { - // - // Start Running ------------------------------------------------- - // - - // modify some button states when running - - m_togBtnSplit->Enable(); - m_togBtnAnalog->Enable(); - m_togBtnOnOff->SetLabel(wxT("Stop")); - m_btnTogPTT->Enable(); - m_togBtnVoiceKeyer->Enable(); - vk_state = VK_IDLE; - - m_rb1600->Disable(); - m_rb700b->Disable(); - m_rb700c->Disable(); - m_rb800xa->Disable(); - if (m_rbPlugIn != NULL) - m_rbPlugIn->Disable(); - - // determine what mode we are using - - if (m_rb1600->GetValue()) { - g_mode = FREEDV_MODE_1600; - g_Nc = 16; - m_panelScatter->setNc(g_Nc); - } - if (m_rb700b->GetValue()) { - g_mode = FREEDV_MODE_700B; - g_Nc = 14; - m_panelScatter->setNc(g_Nc/2-1); /* due to diversity, -1 due to no pilot like FreeDV 1600 */ - } - if (m_rb700c->GetValue()) { - g_mode = FREEDV_MODE_700C; - g_Nc = 14; - m_panelScatter->setNc(g_Nc/2-1); /* due to diversity, -1 due to no pilot like FreeDV 1600 */ - } - if (m_rb800xa->GetValue()) { - g_mode = FREEDV_MODE_800XA; - } - if (m_rbPlugIn != NULL) { - if (m_rbPlugIn->GetValue()) { - g_mode = -1; /* TODO; a better way of handling (enumarating?) non-freedv modes */ - - /* scale plots assuming Fs = 8000 Hz for now */ - - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ)/8000.0); - m_panelWaterfall->setFs(8000.0); - - (wxGetApp().m_plugin_startfp)(wxGetApp().m_plugInStates); - } - } - - if (g_mode != -1) { - // init freedv states - - g_pfreedv = freedv_open(g_mode); - freedv_set_callback_txt(g_pfreedv, &my_put_next_rx_char, &my_get_next_tx_char, NULL); - - freedv_set_callback_error_pattern(g_pfreedv, my_freedv_put_error_pattern, (void*)m_panelTestFrameErrors); - g_error_pattern_fifo = fifo_create(2*freedv_get_sz_error_pattern(g_pfreedv)); - g_error_hist = new short[FDMDV_NC_MAX*2]; - int i; - for(i=0; i<2*FDMDV_NC_MAX; i++) - g_error_hist[i] = 0; - - assert(g_pfreedv != NULL); - - // init Codec 2 LPC Post Filter - - codec2_set_lpc_post_filter(freedv_get_codec2(g_pfreedv), - wxGetApp().m_codec2LPCPostFilterEnable, - wxGetApp().m_codec2LPCPostFilterBassBoost, - wxGetApp().m_codec2LPCPostFilterBeta, - wxGetApp().m_codec2LPCPostFilterGamma); - - // Init Speex pre-processor states - // by inspecting Speex source it seems that only denoiser is on be default - - g_speex_st = speex_preprocess_state_init(freedv_get_n_speech_samples(g_pfreedv), FS); - - // adjust spectrum and waterfall freq scaling base on mode - - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(freedv_get_modem_sample_rate(g_pfreedv)/2))); - m_panelWaterfall->setFs(freedv_get_modem_sample_rate(g_pfreedv)); - - // Init text msg decoding - - freedv_set_varicode_code_num(g_pfreedv, wxGetApp().m_textEncoding); - } - - modem_stats_open(&g_stats); - g_State = 0; - g_snr = 0.0; - g_half_duplex = wxGetApp().m_boolHalfDuplex; - - if (g_mode == FREEDV_MODE_800XA) { - m_panelScatter->setEyeScatter(PLOT_SCATTER_MODE_EYE); - } - else { - m_panelScatter->setEyeScatter(PLOT_SCATTER_MODE_SCATTER); - } - - m_pcallsign = m_callsign; - memset(m_callsign, 0, sizeof(m_callsign)); -#ifdef __UDP_EXPERIMENTAL__ - m_checksumGood = m_checksumBad = 0; - wxString s; - s.Printf("%d", m_checksumGood); - m_txtChecksumGood->SetLabel(s); - s.Printf("%d", m_checksumBad); - m_txtChecksumBad->SetLabel(s); -#endif - - m_maxLevel = 0; - m_textLevel->SetLabel(wxT("")); - m_gaugeLevel->SetValue(0); - - //printf("m_textEncoding = %d\n", wxGetApp().m_textEncoding); - //printf("g_stats.snr: %f\n", g_stats.snr_est); - - // attempt to start PTT ...... - - if (wxGetApp().m_boolHamlibUseForPTT) - OpenHamlibRig(); - if (wxGetApp().m_boolUseSerialPTT) { - OpenSerialPort(); - } - - // attempt to start sound cards and tx/rx processing - - startRxStream(); - - if (m_RxRunning) - { -#ifdef _USE_TIMER - m_plotTimer.Start(_REFRESH_TIMER_PERIOD, wxTIMER_CONTINUOUS); -#endif // _USE_TIMER - } -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"start"); processTxtEvent(e); -#endif - } - - // Stop was pressed or start up failed - - if (startStop.IsSameAs("Stop") || !m_RxRunning ) { - - // - // Stop Running ------------------------------------------------- - // - -#ifdef __UDP_EXPERIMENTAL__ - optionsDlg->SetSpamTimerLight(false); -#endif - -#ifdef _USE_TIMER - m_plotTimer.Stop(); -#endif // _USE_TIMER - - // ensure we are not transmitting and shut down audio processing - - if (wxGetApp().m_boolHamlibUseForPTT) { - Hamlib *hamlib = wxGetApp().m_hamlib; - wxString hamlibError; - if (wxGetApp().m_boolHamlibUseForPTT && hamlib != NULL) { - if (hamlib->ptt(false, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - hamlib->close(); - } - } - - if (wxGetApp().m_boolUseSerialPTT) { - CloseSerialPort(); - } - - m_btnTogPTT->SetValue(false); - VoiceKeyerProcessEvent(VK_SPACE_BAR); - - stopRxStream(); - modem_stats_close(&g_stats); - - // free up states, clean up - - if (g_mode == -1) { - // PlugIn clean up - (wxGetApp().m_plugin_stopfp)(wxGetApp().m_plugInStates); - } - else { - // FreeDV clean up - delete g_error_hist; - fifo_destroy(g_error_pattern_fifo); - freedv_close(g_pfreedv); - speex_preprocess_state_destroy(g_speex_st); - } - - m_newMicInFilter = m_newSpkOutFilter = true; - - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - m_btnTogPTT->Disable(); - m_togBtnVoiceKeyer->Disable(); - m_togBtnOnOff->SetLabel(wxT("Start")); - m_rb1600->Enable(); - m_rb700b->Enable(); - m_rb700c->Enable(); - m_rb800xa->Enable(); - if (m_rbPlugIn != NULL) - m_rbPlugIn->Enable(); - -#ifdef DISABLED_FEATURE - m_rb700->Enable(); - m_rb1400old->Enable(); - m_rb1600Wide->Enable(); - m_rb1400->Enable(); - m_rb2000->Enable(); -#endif -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"stop"); processTxtEvent(e); -#endif - } -} - -//------------------------------------------------------------------------- -// stopRxStream() -//------------------------------------------------------------------------- -void MainFrame::stopRxStream() -{ - if(m_RxRunning) - { - m_RxRunning = false; - - fprintf(stderr, "waiting for thread to stop\n"); - m_txRxThread->m_run = 0; - m_txRxThread->Wait(); - fprintf(stderr, "thread stopped\n"); - - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(m_rxOutPa != m_rxInPa) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - - if (g_nSoundCards == 2) { - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - if(m_txInPa != m_txOutPa) { - m_txOutPa->stop(); - m_txOutPa->streamClose(); - delete m_txOutPa; - } - } - - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - } -} - -void MainFrame::destroy_fifos(void) -{ - fifo_destroy(g_rxUserdata->infifo1); - fifo_destroy(g_rxUserdata->outfifo1); - fifo_destroy(g_rxUserdata->infifo2); - fifo_destroy(g_rxUserdata->outfifo2); - fifo_destroy(g_rxUserdata->rxinfifo); - fifo_destroy(g_rxUserdata->rxoutfifo); -} - -void MainFrame::destroy_src(void) -{ - src_delete(g_rxUserdata->insrc1); - src_delete(g_rxUserdata->outsrc1); - src_delete(g_rxUserdata->insrc2); - src_delete(g_rxUserdata->outsrc2); - src_delete(g_rxUserdata->insrcsf); -} - -void MainFrame::initPortAudioDevice(PortAudioWrap *pa, int inDevice, int outDevice, - int soundCard, int sampleRate, int inputChannels) -{ - // Note all of the wrapper functions below just set values in a - // portaudio struct so can't return any errors. So no need to trap - // any errors in this function. - - // init input params - - pa->setInputDevice(inDevice); - if(inDevice != paNoDevice) { - pa->setInputChannelCount(inputChannels); // stereo input - pa->setInputSampleFormat(PA_SAMPLE_TYPE); - pa->setInputLatency(pa->getInputDefaultLowLatency()); - pa->setInputHostApiStreamInfo(NULL); - } - - pa->setOutputDevice(paNoDevice); - - // init output params - - pa->setOutputDevice(outDevice); - if(outDevice != paNoDevice) { - pa->setOutputChannelCount(2); // stereo output - pa->setOutputSampleFormat(PA_SAMPLE_TYPE); - pa->setOutputLatency(pa->getOutputDefaultLowLatency()); - pa->setOutputHostApiStreamInfo(NULL); - } - - // init params that affect input and output - - /* - On Linux, setting this to wxGetApp().m_framesPerBuffer caused - intermittant break up on the audio from my IC7200 on Ubuntu 14. - After a day of bug hunting I found that 0, as recommended by the - PortAudio docunmentation, fixed the problem. - */ - - //pa->setFramesPerBuffer(wxGetApp().m_framesPerBuffer); - pa->setFramesPerBuffer(0); - - pa->setSampleRate(sampleRate); - pa->setStreamFlags(paClipOff); -} - -//------------------------------------------------------------------------- -// startRxStream() -//------------------------------------------------------------------------- -void MainFrame::startRxStream() -{ - int src_error; - const PaDeviceInfo *deviceInfo1 = NULL, *deviceInfo2 = NULL; - int inputChannels1, inputChannels2; - bool two_rx=false; - bool two_tx=false; - - if(!m_RxRunning) { - m_RxRunning = true; - - if(Pa_Initialize()) - { - wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK); - } - - m_rxInPa = new PortAudioWrap(); - if(g_soundCard1InDeviceNum != g_soundCard1OutDeviceNum) - two_rx=true; - if(g_soundCard2InDeviceNum != g_soundCard2OutDeviceNum) - two_tx=true; - - fprintf(g_logfile, "two_rx: %d two_tx: %d\n", two_rx, two_tx); - if(two_rx) - m_rxOutPa = new PortAudioWrap(); - else - m_rxOutPa = m_rxInPa; - - if (g_nSoundCards == 0) { - wxMessageBox(wxT("No Sound Cards configured, use Tools - Audio Config to configure"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - - // Init Sound card 1 ---------------------------------------------- - // sanity check on sound card device numbers - - if ((m_rxInPa->getDeviceCount() <= g_soundCard1InDeviceNum) || - (m_rxOutPa->getDeviceCount() <= g_soundCard1OutDeviceNum)) { - wxMessageBox(wxT("Sound Card 1 not present"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - - // work out how many input channels this device supports. - - deviceInfo1 = Pa_GetDeviceInfo(g_soundCard1InDeviceNum); - if (deviceInfo1 == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card 1"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - if (deviceInfo1->maxInputChannels == 1) - inputChannels1 = 1; - else - inputChannels1 = 2; - - if(two_rx) { - initPortAudioDevice(m_rxInPa, g_soundCard1InDeviceNum, paNoDevice, 1, - g_soundCard1SampleRate, inputChannels1); - initPortAudioDevice(m_rxOutPa, paNoDevice, g_soundCard1OutDeviceNum, 1, - g_soundCard1SampleRate, inputChannels1); - } - else - initPortAudioDevice(m_rxInPa, g_soundCard1InDeviceNum, g_soundCard1OutDeviceNum, 1, - g_soundCard1SampleRate, inputChannels1); - - // Init Sound Card 2 ------------------------------------------------ - - if (g_nSoundCards == 2) { - - m_txInPa = new PortAudioWrap(); - if(two_tx) - m_txOutPa = new PortAudioWrap(); - else - m_txOutPa = m_txInPa; - - // sanity check on sound card device numbers - - //printf("m_txInPa->getDeviceCount(): %d\n", m_txInPa->getDeviceCount()); - //printf("g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - //printf("g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - - if ((m_txInPa->getDeviceCount() <= g_soundCard2InDeviceNum) || - (m_txOutPa->getDeviceCount() <= g_soundCard2OutDeviceNum)) { - wxMessageBox(wxT("Sound Card 2 not present"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - m_RxRunning = false; - return; - } - - deviceInfo2 = Pa_GetDeviceInfo(g_soundCard2InDeviceNum); - if (deviceInfo2 == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card 1"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - m_RxRunning = false; - return; - } - if (deviceInfo2->maxInputChannels == 1) - inputChannels2 = 1; - else - inputChannels2 = 2; - - if(two_tx) { - initPortAudioDevice(m_txInPa, g_soundCard2InDeviceNum, paNoDevice, 2, - g_soundCard2SampleRate, inputChannels2); - initPortAudioDevice(m_txOutPa, paNoDevice, g_soundCard2OutDeviceNum, 2, - g_soundCard2SampleRate, inputChannels2); - } - else - initPortAudioDevice(m_txInPa, g_soundCard2InDeviceNum, g_soundCard2OutDeviceNum, 2, - g_soundCard2SampleRate, inputChannels2); - } - - // Init call back data structure ---------------------------------------------- - - g_rxUserdata = new paCallBackData; - g_rxUserdata->inputChannels1 = inputChannels1; - if (deviceInfo2 != NULL) - g_rxUserdata->inputChannels2 = inputChannels2; - - // init sample rate conversion states - - g_rxUserdata->insrc1 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrc1 != NULL); - g_rxUserdata->outsrc1 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->outsrc1 != NULL); - g_rxUserdata->insrc2 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrc2 != NULL); - g_rxUserdata->outsrc2 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->outsrc2 != NULL); - - g_rxUserdata->insrcsf = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrcsf != NULL); - - // create FIFOs used to interface between different buffer sizes - - g_rxUserdata->infifo1 = fifo_create(8*N48); - g_rxUserdata->outfifo1 = fifo_create(10*N48); - g_rxUserdata->outfifo2 = fifo_create(8*N48); - g_rxUserdata->infifo2 = fifo_create(8*N48); - printf("N48: %d\n", N48); - - g_rxUserdata->rxinfifo = fifo_create(10 * N8); - g_rxUserdata->rxoutfifo = fifo_create(10 * N8); - - // Init Equaliser Filters ------------------------------------------------------ - - m_newMicInFilter = m_newSpkOutFilter = true; - designEQFilters(g_rxUserdata); - g_rxUserdata->micInEQEnable = wxGetApp().m_MicInEQEnable; - g_rxUserdata->spkOutEQEnable = wxGetApp().m_SpkOutEQEnable; - - // optional tone in left channel to reliably trigger vox - - g_rxUserdata->leftChannelVoxTone = wxGetApp().m_leftChannelVoxTone; - g_rxUserdata->voxTonePhase = 0; - - // Start sound card 1 ---------------------------------------------------------- - - m_rxInPa->setUserData(g_rxUserdata); - m_rxErr = m_rxInPa->setCallback(rxCallback); - - m_rxErr = m_rxInPa->streamOpen(); - - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Open/Setup error."), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - m_rxErr = m_rxInPa->streamStart(); - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Stream Start Error."), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - // Start separate output stream if needed - - if(two_rx) { - m_rxOutPa->setUserData(g_rxUserdata); - m_rxErr = m_rxOutPa->setCallback(rxCallback); - - m_rxErr = m_rxOutPa->streamOpen(); - - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Second Stream Open/Setup error."), wxT("Error"), wxOK); - delete m_rxInPa; - delete m_rxOutPa; - delete m_txOutPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - m_rxErr = m_rxOutPa->streamStart(); - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Second Stream Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - delete m_rxOutPa; - delete m_txOutPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - } - - // Start sound card 2 ---------------------------------------------------------- - - if (g_nSoundCards == 2) { - - // question: can we use same callback data - // (g_rxUserdata)or both sound card callbacks? Is there a - // chance of them both being called at the same time? We - // could need a mutex ... - - m_txInPa->setUserData(g_rxUserdata); - m_txErr = m_txInPa->setCallback(txCallback); - m_txErr = m_txInPa->streamOpen(); - - if(m_txErr != paNoError) { - fprintf(stderr, "Err: %d\n", m_txErr); - wxMessageBox(wxT("Sound Card 2 Open/Setup error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - m_txErr = m_txInPa->streamStart(); - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - // Start separate output stream if needed - - if (two_tx) { - - // question: can we use same callback data - // (g_rxUserdata)or both sound card callbacks? Is there a - // chance of them both being called at the same time? We - // could need a mutex ... - - m_txOutPa->setUserData(g_rxUserdata); - m_txErr = m_txOutPa->setCallback(txCallback); - m_txErr = m_txOutPa->streamOpen(); - - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Second Stream Open/Setup error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - m_txErr = m_txOutPa->streamStart(); - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Second Stream Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - } - } - - // start tx/rx processing thread - - m_txRxThread = new txRxThread; - - if ( m_txRxThread->Create() != wxTHREAD_NO_ERROR ) - { - wxLogError(wxT("Can't create thread!")); - } - - m_txRxThread->SetPriority(WXTHREAD_MAX_PRIORITY); - - if ( m_txRxThread->Run() != wxTHREAD_NO_ERROR ) - { - wxLogError(wxT("Can't start thread!")); - } - - } -} - - -void MainFrame::processTxtEvent(char event[]) { - int rule = 0; - - //printf("processTxtEvent:\n"); - //printf(" event: %s\n", event); - - // process with regexp and issue system command - - // Each regexp in our list is separated by a newline. We want to try all of them. - - wxString event_str(event); - int match_end, replace_end; - match_end = replace_end = 0; - wxString regexp_match_list = wxGetApp().m_events_regexp_match; - wxString regexp_replace_list = wxGetApp().m_events_regexp_replace; - - bool found_match = false; - - while (((match_end = regexp_match_list.Find('\n')) != wxNOT_FOUND) && (rule < MAX_EVENT_RULES)) { - //printf("match_end: %d\n", match_end); - if ((replace_end = regexp_replace_list.Find('\n')) != wxNOT_FOUND) { - //printf("replace_end = %d\n", replace_end); - - // candidate match and replace regexps strings exist, so lets try them - - wxString regexp_match = regexp_match_list.SubString(0, match_end-1); - wxString regexp_replace = regexp_replace_list.SubString(0, replace_end-1); - //printf("match: %s replace: %s\n", (const char *)regexp_match.c_str(), (const char *)regexp_replace.c_str()); - wxRegEx re(regexp_match); - //printf(" checking for match against: %s\n", (const char *)regexp_match.c_str()); - - // if we found a match, lets run the replace regexp and issue the system command - - wxString event_str_rep = event_str; - - if (re.Replace(&event_str_rep, regexp_replace) != 0) { - fprintf(stderr, " found match! event_str: %s\n", (const char *)event_str.c_str()); - found_match = true; - - bool enableSystem = false; - if (wxGetApp().m_events) - enableSystem = true; - - // no syscall if spam timer still running - - if (spamTimer[rule].IsRunning()) { - enableSystem = false; - fprintf(stderr, " spam timer running\n"); - } - - const char *event_out = event_str_rep.ToUTF8(); - wxString event_out_with_return_code; - - if (enableSystem) { - int ret = wxExecute(event_str_rep); - event_out_with_return_code.Printf(_T("%s -> returned %d"), event_out, ret); - spamTimer[rule].Start((wxGetApp().m_events_spam_timer)*1000, wxTIMER_ONE_SHOT); - } - else - event_out_with_return_code.Printf(_T("%s T: %d"), event_out, spamTimer[rule].IsRunning()); - - // update event log GUI if currently displayed - - if (optionsDlg != NULL) { - optionsDlg->updateEventLog(wxString(event), event_out_with_return_code); - } - } - } - regexp_match_list = regexp_match_list.SubString(match_end+1, regexp_match_list.length()); - regexp_replace_list = regexp_replace_list.SubString(replace_end+1, regexp_replace_list.length()); - - rule++; - } - - if ((optionsDlg != NULL) && !found_match) { - optionsDlg->updateEventLog(wxString(event), _("")); - } -} - - -#define SBQ_MAX_ARGS 4 - -void *MainFrame::designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q) -{ - char *arg[SBQ_MAX_ARGS]; - char argstorage[SBQ_MAX_ARGS][80]; - void *sbq; - int i, argc; - - assert((strcmp(filterType, "bass") == 0) || - (strcmp(filterType, "treble") == 0) || - (strcmp(filterType, "equalizer") == 0)); - - for(i=0; isbqMicInBass = designAnEQFilter("bass", wxGetApp().m_MicInBassFreqHz, wxGetApp().m_MicInBassGaindB); - cb->sbqMicInTreble = designAnEQFilter("treble", wxGetApp().m_MicInTrebleFreqHz, wxGetApp().m_MicInTrebleGaindB); - cb->sbqMicInMid = designAnEQFilter("equalizer", wxGetApp().m_MicInMidFreqHz, wxGetApp().m_MicInMidGaindB, wxGetApp().m_MicInMidQ); - } - - // init Spk Out Equaliser Filters - - if (m_newSpkOutFilter) { - //printf("designing new Spk Out filters\n"); - //printf("designEQFilters: wxGetApp().m_SpkOutBassFreqHz: %f\n",wxGetApp().m_SpkOutBassFreqHz); - cb->sbqSpkOutBass = designAnEQFilter("bass", wxGetApp().m_SpkOutBassFreqHz, wxGetApp().m_SpkOutBassGaindB); - cb->sbqSpkOutTreble = designAnEQFilter("treble", wxGetApp().m_SpkOutTrebleFreqHz, wxGetApp().m_SpkOutTrebleGaindB); - cb->sbqSpkOutMid = designAnEQFilter("equalizer", wxGetApp().m_SpkOutMidFreqHz, wxGetApp().m_SpkOutMidGaindB, wxGetApp().m_SpkOutMidQ); - } -} - -void MainFrame::deleteEQFilters(paCallBackData *cb) -{ - if (m_newMicInFilter) { - sox_biquad_destroy(cb->sbqMicInBass); - sox_biquad_destroy(cb->sbqMicInTreble); - sox_biquad_destroy(cb->sbqMicInMid); - } - if (m_newSpkOutFilter) { - sox_biquad_destroy(cb->sbqSpkOutBass); - sox_biquad_destroy(cb->sbqSpkOutTreble); - sox_biquad_destroy(cb->sbqSpkOutMid); - } -} - -// returns number of output samples generated by resampling -int resample(SRC_STATE *src, - short output_short[], - short input_short[], - int output_sample_rate, - int input_sample_rate, - int length_output_short, // maximum output array length in samples - int length_input_short - ) -{ - SRC_DATA src_data; - float input[N48*4]; - float output[N48*4]; - int ret; - - assert(src != NULL); - assert(length_input_short <= N48*4); - assert(length_output_short <= N48*4); - - src_short_to_float_array(input_short, input, length_input_short); - - src_data.data_in = input; - src_data.data_out = output; - src_data.input_frames = length_input_short; - src_data.output_frames = length_output_short; - src_data.end_of_input = 0; - src_data.src_ratio = (float)output_sample_rate/input_sample_rate; - - ret = src_process(src, &src_data); - assert(ret == 0); - - assert(src_data.output_frames_gen <= length_output_short); - src_float_to_short_array(output, output_short, src_data.output_frames_gen); - - return src_data.output_frames_gen; -} - - -// Decimates samples using an algorithm that produces nice plots of -// speech signals at a low sample rate. We want a low sample rate so -// we don't hammer the graphics system too hard. Saves decimated data -// to a fifo for plotting on screen. -void resample_for_plot(struct FIFO *plotFifo, short buf[], int length, int fs) -{ - int decimation = fs/WAVEFORM_PLOT_FS; - int nSamples, sample; - int i, st, en, max, min; - short dec_samples[length]; - - nSamples = length/decimation; - - for(sample = 0; sample < nSamples; sample += 2) - { - st = decimation*sample; - en = decimation*(sample+2); - max = min = 0; - for(i=st; i buf[i]) min = buf[i]; - } - dec_samples[sample] = max; - dec_samples[sample+1] = min; - } - fifo_write(plotFifo, dec_samples, nSamples); -} - -void txRxProcessing() -{ - - paCallBackData *cbData = g_rxUserdata; - - // Buffers re-used by tx and rx processing - // signals in in48k/out48k are at a maximum sample rate of 48k, could be 44.1kHz - // depending on sound hardware. - - short in8k_short[4*N8]; - short in48k_short[4*N48]; - short out8k_short[4*N8]; - short out48k_short[4*N48]; - int nout, samplerate, n_samples; - - //fprintf(g_logfile, "start infifo1: %5d outfifo2: %5d\n", fifo_used(cbData->infifo1), fifo_used(cbData->outfifo2)); - - // FreeDV 700 uses a modem sample rate of 7500 Hz which requires some special treatment - - if (g_analog || g_mode == -1) - samplerate = FS; - else - samplerate = freedv_get_modem_sample_rate(g_pfreedv); - - // - // RX side processing -------------------------------------------- - // - - // while we have enough input samples available ... - - int nsam = g_soundCard1SampleRate * (float)N8/FS; - assert(nsam <= N48); - g_mutexProtectingCallbackData.Lock(); - while ((fifo_read(cbData->infifo1, in48k_short, nsam) == 0) && ((g_half_duplex && !g_tx) || !g_half_duplex)) - { - g_mutexProtectingCallbackData.Unlock(); - unsigned int n8k; - - n8k = resample(cbData->insrc1, in8k_short, in48k_short, samplerate, g_soundCard1SampleRate, N8, nsam); - assert(n8k <= N8); - - // optionally save "from radio" signal (write demod input to file) - // Really useful for testing and development as it allows us - // to repeat tests using off air signals - - g_mutexProtectingCallbackData.Lock(); - if (g_recFileFromRadio && (g_sfRecFile != NULL)) { - //printf("g_recFromRadioSamples: %d n8k: %d \n", g_recFromRadioSamples); - if (g_recFromRadioSamples < n8k) { - sf_write_short(g_sfRecFile, in8k_short, g_recFromRadioSamples); - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_recFileFromRadioEventId ); - // call stop/start record menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - g_recFromRadioSamples = 0; - //printf("finished recording g_recFromRadioSamples: %d n8k: %d!\n", g_recFileFromRadio, n8k); - } - else { - sf_write_short(g_sfRecFile, in8k_short, n8k); - g_recFromRadioSamples -= n8k; - } - } - g_mutexProtectingCallbackData.Unlock(); - - // optionally read "from radio" signal from file (read demod input from file) - - g_mutexProtectingCallbackData.Lock(); - if (g_playFileFromRadio && (g_sfPlayFileFromRadio != NULL)) { - unsigned int nsf = n8k*g_sfFs/samplerate; - short insf_short[nsf]; - unsigned int n = sf_read_short(g_sfPlayFileFromRadio, insf_short, nsf); - n8k = resample(cbData->insrcsf, in8k_short, insf_short, samplerate, g_sfFs, N8, nsf); - //fprintf(g_logfile, "n: %d nsf: %d n8k: %d samplerate: %d\n", n, nsf, n8k, samplerate); - assert(n8k <= N8); - - if (n == 0) { - if (g_loopPlayFileFromRadio) - sf_seek(g_sfPlayFileFromRadio, 0, SEEK_SET); - else { - printf("playFileFromRadio finished, issuing event!\n"); - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_playFileFromRadioEventId ); - // call stop/start play menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - } - } - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotDemodInFifo, in8k_short, n8k, samplerate); - - if (g_mode != -1) { - // send latest squelch level to FreeDV API, as it handles squelch internally - - freedv_set_squelch_en(g_pfreedv, g_SquelchActive); - freedv_set_snr_squelch_thresh(g_pfreedv, g_SquelchLevel); - } - - //fprintf(g_logfile, "snr_squelch_thresh: %f\n", g_pfreedv->snr_squelch_thresh); - - // compute rx spectrum - do here so update rate in constant - - COMP rx_fdm[n8k]; - float rx_spec[MODEM_STATS_NSPEC]; - unsigned int i; - - for(i=0; irxinfifo, in8k_short, n8k); - per_frame_rx_processing(cbData->rxoutfifo, cbData->rxinfifo); - memset(out8k_short, 0, sizeof(short)*N8); - fifo_read(cbData->rxoutfifo, out8k_short, N8); - //printf("out8k_short: %d\n", out8k_short[0]); - } - - - // Optional Spk Out EQ Filtering, need mutex as filter can change at run time - g_mutexProtectingCallbackData.Lock(); - if (cbData->spkOutEQEnable) { - sox_biquad_filter(cbData->sbqSpkOutBass, out8k_short, out8k_short, N8); - sox_biquad_filter(cbData->sbqSpkOutTreble, out8k_short, out8k_short, N8); - sox_biquad_filter(cbData->sbqSpkOutMid, out8k_short, out8k_short, N8); - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotSpeechOutFifo, out8k_short, N8, FS); - - g_mutexProtectingCallbackData.Lock(); - if (g_nSoundCards == 1) { - nout = resample(cbData->outsrc2, out48k_short, out8k_short, g_soundCard1SampleRate, FS, N48, N8); - fifo_write(cbData->outfifo1, out48k_short, nout); - } - else { - nout = resample(cbData->outsrc2, out48k_short, out8k_short, g_soundCard2SampleRate, FS, N48, N8); - fifo_write(cbData->outfifo2, out48k_short, nout); - } - } - g_mutexProtectingCallbackData.Unlock(); - - // - // TX side processing -------------------------------------------- - // - - if ((g_mode != -1) && ((g_nSoundCards == 2) && ((g_half_duplex && g_tx) || !g_half_duplex))) { - int ret; - - // Make sure we have q few frames of modulator output - // samples. This also locks the modulator to the sample rate - // of sound card 1. We want to make sure that modulator - // samples are uninterrupted by differences in sample rate - // between this sound card and sound card 2. - - g_mutexProtectingCallbackData.Lock(); - while((unsigned)fifo_used(cbData->outfifo1) < 6*N48) - { - g_mutexProtectingCallbackData.Unlock(); - - int nsam = g_soundCard2SampleRate * freedv_get_n_speech_samples(g_pfreedv)/FS; - assert(nsam <= 4*N48); - - // infifo2 is written to by another sound card so it may - // over or underflow, but we don't realy care. It will - // just result in a short interruption in audio being fed - // to codec2_enc, possibly making a click every now and - // again in the decoded audio at the other end. - - // zero speech input just in case infifo2 underflows - memset(in48k_short, 0, nsam*sizeof(short)); - fifo_read(cbData->infifo2, in48k_short, nsam); - - nout = resample(cbData->insrc2, in8k_short, in48k_short, FS, g_soundCard2SampleRate, 4*N8, nsam); - - // optionally use file for mic input signal - - g_mutexProtectingCallbackData.Lock(); - if (g_playFileToMicIn && (g_sfPlayFile != NULL)) { - int n = sf_read_short(g_sfPlayFile, in8k_short, nout); - //fprintf(stderr, "n: %d nout: %d\n", n, nout); - if (n != nout) { - if (g_loopPlayFileToMicIn) - sf_seek(g_sfPlayFile, 0, SEEK_SET); - else { - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_playFileToMicInEventId ); - // call stop/start play menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - } - } - } - g_mutexProtectingCallbackData.Unlock(); - - // Optional Speex pre-processor for acoustic noise reduction - - if (wxGetApp().m_speexpp_enable) { - speex_preprocess_run(g_speex_st, in8k_short); - } - - // Optional Mic In EQ Filtering, need mutex as filter can change at run time - - g_mutexProtectingCallbackData.Lock(); - if (cbData->micInEQEnable) { - sox_biquad_filter(cbData->sbqMicInBass, in8k_short, in8k_short, nout); - sox_biquad_filter(cbData->sbqMicInTreble, in8k_short, in8k_short, nout); - sox_biquad_filter(cbData->sbqMicInMid, in8k_short, in8k_short, nout); - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotSpeechInFifo, in8k_short, nout, FS); - - n_samples = freedv_get_n_nom_modem_samples(g_pfreedv); - - if (g_analog) { - n_samples = freedv_get_n_speech_samples(g_pfreedv); - - // Boost the "from mic" -> "to radio" audio in analog - // mode. The need for the gain was found by - // experiment - analog SSB sounded too quiet compared - // to digital. With digital voice we generally drive - // the "to radio" (SSB radio mic input) at about 25% - // of the peak level for normal SSB voice. So we - // introduce 6dB gain to make analog SSB sound the - // same level as the digital. Watch out for clipping. - for(int i=0; i 32767) out = 32767.0; - if (out < -32767) out = -32767.0; - out8k_short[i] = out; - } - } - else { - COMP tx_fdm[freedv_get_n_nom_modem_samples(g_pfreedv)]; - COMP tx_fdm_offset[freedv_get_n_nom_modem_samples(g_pfreedv)]; - int i; - - if (g_mode == FREEDV_MODE_800XA) { - /* 800XA doesn't support complex output just yet */ - freedv_tx(g_pfreedv, out8k_short, in8k_short); - } - else { - freedv_comptx(g_pfreedv, tx_fdm, in8k_short); - - freq_shift_coh(tx_fdm_offset, tx_fdm, g_TxFreqOffsetHz, freedv_get_modem_sample_rate(g_pfreedv), &g_TxFreqOffsetPhaseRect, freedv_get_n_nom_modem_samples(g_pfreedv)); - for(i=0; ioutsrc1, out48k_short, out8k_short, g_soundCard1SampleRate, samplerate, N48*4, n_samples); - g_mutexProtectingCallbackData.Lock(); - ret = fifo_write(cbData->outfifo1, out48k_short, nout); - //fprintf(stderr,"nout: %d ret: %d N48*4: %d\n", nout, ret, N48*4); - - assert(ret != -1); - } - g_mutexProtectingCallbackData.Unlock(); - } - - //fprintf(g_logfile, " end infifo1: %5d outfifo2: %5d\n", fifo_used(cbData->infifo1), fifo_used(cbData->outfifo2)); - -} - -//---------------------------------------------------------------- -// per_frame_rx_processing() -//---------------------------------------------------------------- - -void per_frame_rx_processing( - FIFO *output_fifo, // decoded speech samples - FIFO *input_fifo - ) -{ - #ifdef OLDSPEC - float rx_spec[MODEM_STATS_NSPEC]; - #endif - int i; - - if (g_mode == -1) { - // PlugIn processing --------------------------------------------------- - - int nin = 160; // TODO: hard code for now - some sort of plugin supplied param in future - short input_buf[nin]; - - while (fifo_read(input_fifo, input_buf, nin) == 0) { - (wxGetApp().m_plugin_rx_samplesfp)(wxGetApp().m_plugInStates, input_buf, nin); - } - - #ifdef OLD_SPEC - COMP rx_fdm[nin]; - - for(i=0; isnr); - - // grab extended stats so we can plot spectrum, scatter diagram etc - - freedv_get_modem_extended_stats(g_pfreedv, &g_stats); - - #ifdef OLD_SPEC - // compute rx spectrum - - modem_stats_get_rx_spectrum(&g_stats, rx_spec, rx_fdm, nin_prev); - - // Average rx spectrum data using a simple IIR low pass filter - - for(i = 0; iinputChannels1) - indata[i] = rptr[0]; - if (fifo_write(cbData->infifo1, indata, framesPerBuffer)) { - //fprintf(g_logfile, "infifo1 full\n"); - } - //fifo_write(cbData->outfifo1, indata, framesPerBuffer); - } - - // OK now set up output samples for this callback - - if(wptr) { - //fprintf(g_logfile,"out %ld %d\n", framesPerBuffer, g_out++); - if (fifo_read(cbData->outfifo1, outdata, framesPerBuffer) == 0) { - - // write signal to both channels - - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - if (cbData->leftChannelVoxTone) { - cbData->voxTonePhase += 2.0*M_PI*VOX_TONE_FREQ/g_soundCard1SampleRate; - cbData->voxTonePhase -= 2.0*M_PI*floor(cbData->voxTonePhase/(2.0*M_PI)); - wptr[0] = VOX_TONE_AMP*cos(cbData->voxTonePhase); - //printf("%f %d\n", cbData->voxTonePhase, wptr[0]); - } - else - wptr[0] = outdata[i]; - - wptr[1] = outdata[i]; - } - } - else { - //fprintf(g_logfile, "outfifo1 empty\n"); - // zero output if no data available - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = 0; - wptr[1] = 0; - } - } - } - - return paContinue; -} - - -//------------------------------------------------------------------------- -// txCallback() -//------------------------------------------------------------------------- -int MainFrame::txCallback( - const void *inputBuffer, - void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ) -{ - paCallBackData *cbData = (paCallBackData*)userData; - unsigned int i; - short *rptr = (short*)inputBuffer; - short *wptr = (short*)outputBuffer; - short indata[MAX_FPB]; - short outdata[MAX_FPB]; - - wxMutexLocker lock(g_mutexProtectingCallbackData); - - // if (statusFlags) - // printf("cb2 statusFlags: 0x%x\n", (int)statusFlags); - - // assemble a mono buffer and write to FIFO - - assert(framesPerBuffer < MAX_FPB); - - if(rptr) { - for(i = 0; i < framesPerBuffer; i++, rptr += cbData->inputChannels2) - indata[i] = rptr[0]; - } - - //#define SC2_LOOPBACK -#ifdef SC2_LOOPBACK - //TODO: This doesn't work unless using the same soundcard! - - if(wptr) { - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = indata[i]; - wptr[1] = indata[i]; - } - } - -#else - if(rptr) { - if (fifo_write(cbData->infifo2, indata, framesPerBuffer)) { - //fprintf(g_logfile, "infifo2 full\n"); - } - } - - // OK now set up output samples for this callback - - if(wptr) { - if (fifo_read(cbData->outfifo2, outdata, framesPerBuffer) == 0) { - - // write signal to both channels */ - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = outdata[i]; - wptr[1] = outdata[i]; - } - } - else { - //fprintf(g_logfile, "outfifo2 empty\n"); - // zero output if no data available - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = 0; - wptr[1] = 0; - } - } - } -#endif - return paContinue; -} - -// Callback from plot_spectrum & plot_waterfall. would be nice to -// work out a way to do this without globals. - -void fdmdv2_clickTune(float freq) { - - // The demod is hard-wired to expect a centre frequency of - // FDMDV_FCENTRE. So we want to take the signal centered on the - // click tune freq and re-centre it on FDMDV_FCENTRE. For example - // if the click tune freq is 1500Hz, and FDMDV_CENTRE is 1200 Hz, - // we need to shift the input signal centred on 1500Hz down to - // 1200Hz, an offset of -300Hz. - - // Bit of an "indent" as we are often trying to get it back - // exactly in the centre - - if (fabs(FDMDV_FCENTRE - freq) < 10.0) { - freq = FDMDV_FCENTRE; - fprintf(stderr, "indent!\n"); - } - - if (g_split) { - g_RxFreqOffsetHz = FDMDV_FCENTRE - freq; - } - else { - g_TxFreqOffsetHz = freq - FDMDV_FCENTRE; - g_RxFreqOffsetHz = FDMDV_FCENTRE - freq; - } -} - -//---------------------------------------------------------------- -// OpenSerialPort() -//---------------------------------------------------------------- - -void MainFrame::OpenSerialPort(void) -{ - Serialport *serialport = wxGetApp().m_serialport; - - if(!wxGetApp().m_strRigCtrlPort.IsEmpty()) { - serialport->openport(wxGetApp().m_strRigCtrlPort.c_str(), - wxGetApp().m_boolUseRTS, - wxGetApp().m_boolRTSPos, - wxGetApp().m_boolUseDTR, - wxGetApp().m_boolDTRPos); - if (serialport->isopen()) { - // always start PTT in Rx state - serialport->ptt(false); - } - else { - wxMessageBox("Couldn't open Serial Port", wxT("About"), wxOK | wxICON_ERROR, this); - } - } -} - - -//---------------------------------------------------------------- -// CloseSerialPort() -//---------------------------------------------------------------- - -void MainFrame::CloseSerialPort(void) -{ - Serialport *serialport = wxGetApp().m_serialport; - if (serialport->isopen()) { - // always end with PTT in rx state - - serialport->ptt(false); - serialport->closeport(); - } -} - - -#ifdef __UDP_SUPPORT__ - -//---------------------------------------------------------------- -// PollUDP() - see if any commands on UDP port -//---------------------------------------------------------------- - -// test this puppy with netcat: -// $ echo "hello" | nc -u -q1 localhost 3000 - -int MainFrame::PollUDP(void) -{ - // this will block until message received, so we put it in it's own thread - - char buf[1024]; - char reply[80]; - size_t n = m_udp_sock->RecvFrom(m_udp_addr, buf, sizeof(buf)).LastCount(); - - if (n) { - wxString bufstr = wxString::From8BitData(buf, n); - bufstr.Trim(); - wxString ipaddr = m_udp_addr.IPAddress(); - printf("Received: \"%s\" from %s:%u\n", - (const char *)bufstr.c_str(), - (const char *)ipaddr.c_str(), m_udp_addr.Service()); - - // for security only accept commands from local host - - sprintf(reply,"nope\n"); - if (ipaddr.Cmp(_("127.0.0.1")) == 0) { - - // process commands - - if (bufstr.Cmp(_("restore")) == 0) { - m_schedule_restore = true; // Make Restore happen in main thread to avoid crashing - sprintf(reply,"ok\n"); - } - - wxString itemToSet, val; - if (bufstr.StartsWith(_("set "), &itemToSet)) { - if (itemToSet.StartsWith("txtmsg ", &val)) { - // note: if options dialog is open this will get overwritten - wxGetApp().m_callSign = val; - } - sprintf(reply,"ok\n"); - } - if (bufstr.StartsWith(_("ptton"), &itemToSet)) { - // note: if options dialog is open this will get overwritten - m_btnTogPTT->SetValue(true); - togglePTT(); - sprintf(reply,"ok\n"); - } - if (bufstr.StartsWith(_("pttoff"), &itemToSet)) { - // note: if options dialog is open this will get overwritten - m_btnTogPTT->SetValue(false); - togglePTT(); - sprintf(reply,"ok\n"); - } - - } - else { - printf("We only accept messages from locahost!\n"); - } - - if ( m_udp_sock->SendTo(m_udp_addr, reply, strlen(reply)).LastCount() != strlen(reply)) { - printf("ERROR: failed to send data\n"); - } - } - - return n; -} - -void MainFrame::startUDPThread(void) { - fprintf(stderr, "starting UDP thread!\n"); - m_UDPThread = new UDPThread; - m_UDPThread->mf = this; - if (m_UDPThread->Create() != wxTHREAD_NO_ERROR ) { - wxLogError(wxT("Can't create thread!")); - } - if (m_UDPThread->Run() != wxTHREAD_NO_ERROR ) { - wxLogError(wxT("Can't start thread!")); - delete m_UDPThread; - } -} - -void MainFrame::stopUDPThread(void) { - printf("stopping UDP thread!\n"); - if ((m_UDPThread != NULL) && m_UDPThread->m_run) { - m_UDPThread->m_run = 0; - m_UDPThread->Wait(); - m_UDPThread = NULL; - } -} - -void *UDPThread::Entry() { - printf("UDP thread started!\n"); - while (m_run) { - if (wxGetApp().m_udp_enable) { - printf("m_udp_enable\n"); - mf->m_udp_addr.Service(wxGetApp().m_udp_port); - mf->m_udp_sock = new wxDatagramSocket(mf->m_udp_addr, wxSOCKET_NOWAIT); - - while (m_run && wxGetApp().m_udp_enable) { - if (mf->PollUDP() == 0) { - wxThread::Sleep(20); - } - } - - delete mf->m_udp_sock; - } - wxThread::Sleep(20); - } - return NULL; -} - -#endif - -char my_get_next_tx_char(void *callback_state) { - short ch = 0; - - fifo_read(g_txDataInFifo, &ch, 1); - //fprintf(stderr, "get_next_tx_char: %c\n", (char)ch); - return (char)ch; -} - -void my_put_next_rx_char(void *callback_state, char c) { - short ch = (short)c; - //fprintf(stderr, "put_next_rx_char: %c\n", (char)c); - fifo_write(g_rxDataOutFifo, &ch, 1); -} - -// Callback from FreeDv API to update error plots - -void my_freedv_put_error_pattern(void *state, short error_pattern[], int sz_error_pattern) { - fifo_write(g_error_pattern_fifo, error_pattern, sz_error_pattern); -} - -void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin) -{ - COMP foff_rect; - float mag; - int i; - - foff_rect.real = cosf(2.0*M_PI*foff/Fs); - foff_rect.imag = sinf(2.0*M_PI*foff/Fs); - for(i=0; ireal /= mag; - foff_phase_rect->imag /= mag; -} - -int plugin_get_persistant(char name[], char value[]) { - wxString n,v; - int i; - - for(i=0; i. -// -//========================================================================== -#ifndef __FDMDV2_MAIN__ -#define __FDMDV2_MAIN__ - -#include "version.h" -#ifndef _NO_AUTOTOOLS_ -#include "../config.h" -#endif -#include - -#include -#include -#include "wx/rawbmp.h" -#include "wx/file.h" -#include "wx/filename.h" -#include "wx/config.h" -#include -#include "wx/graphics.h" -#include "wx/mstream.h" -#include "wx/wfstream.h" -#include "wx/quantize.h" -#include "wx/scopedptr.h" -#include "wx/stopwatch.h" -#include "wx/versioninfo.h" -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -#include "codec2.h" -#include "codec2_fifo.h" -#include "modem_stats.h" - -#include "topFrame.h" -#include "dlg_ptt.h" -#include "dlg_options.h" -#include "fdmdv2_plot.h" -#include "fdmdv2_plot_scalar.h" -#include "fdmdv2_plot_scatter.h" -#include "fdmdv2_plot_waterfall.h" -#include "fdmdv2_plot_spectrum.h" -#include "fdmdv2_pa_wrapper.h" -#include "sndfile.h" -#include "portaudio.h" -#include "dlg_audiooptions.h" -#include "dlg_filter.h" -#include "dlg_options.h" -#include "varicode.h" -#include "sox_biquad.h" -#include "comp_prim.h" -#include "dlg_plugin.h" -#include "hamlib.h" -#include "serialport.h" - -#define _USE_TIMER 1 -#define _USE_ONIDLE 1 -#define _DUMMY_DATA 1 -//#define _AUDIO_PASSTHROUGH 1 -#define _REFRESH_TIMER_PERIOD (DT*1000) -//#define _USE_ABOUT_DIALOG 1 - -enum { - ID_START = wxID_HIGHEST, - ID_TIMER_WATERFALL, - ID_TIMER_SPECTRUM, - ID_TIMER_SCATTER, - ID_TIMER_SCALAR - }; - -#define EXCHANGE_DATA_IN 0 -#define EXCHANGE_DATA_OUT 1 - - -extern int g_nSoundCards; -extern int g_soundCard1InDeviceNum; -extern int g_soundCard1OutDeviceNum; -extern int g_soundCard1SampleRate; -extern int g_soundCard2InDeviceNum; -extern int g_soundCard2OutDeviceNum; -extern int g_soundCard2SampleRate; - -// Voice Keyer Constants - -#define VK_SYNC_WAIT_TIME 5.0 - -// Voice Keyer States - -#define VK_IDLE 0 -#define VK_TX 1 -#define VK_RX 2 -#define VK_SYNC_WAIT 3 - -// Voice Keyer Events - -#define VK_START 0 -#define VK_SPACE_BAR 1 -#define VK_PLAY_FINISHED 2 -#define VK_DT 3 -#define VK_SYNC 4 - -class MainFrame; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainApp -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MainApp : public wxApp -{ - public: - virtual bool OnInit(); - virtual int OnExit(); - - wxString m_strVendName; - wxString m_StrAppName; - - wxString m_textNumChOut; - wxString m_textNumChIn; - - wxString m_strRxInAudio; - wxString m_strRxOutAudio; - wxString m_textVoiceInput; - wxString m_textVoiceOutput; - wxString m_strSampleRate; - wxString m_strBitrate; - - // PTT ----------------------------------- - - bool m_boolHalfDuplex; - - wxString m_txtVoiceKeyerWaveFilePath; - wxString m_txtVoiceKeyerWaveFile; - int m_intVoiceKeyerRxPause; - int m_intVoiceKeyerRepeats; - - bool m_boolHamlibUseForPTT; - unsigned int m_intHamlibRig; - wxString m_strHamlibSerialPort; - unsigned int m_intHamlibSerialRate; - Hamlib *m_hamlib; - - bool m_boolUseSerialPTT; - wxString m_strRigCtrlPort; - bool m_boolUseRTS; - bool m_boolRTSPos; - bool m_boolUseDTR; - bool m_boolDTRPos; - Serialport *m_serialport; - - // Play/Rec files - - wxString m_playFileToMicInPath; - wxString m_recFileFromRadioPath; - unsigned int m_recFileFromRadioSecs; - wxString m_playFileFromRadioPath; - - // Options dialog - - wxString m_callSign; - bool m_events; - int m_events_spam_timer; - unsigned int m_textEncoding; - bool m_enable_checksum; - wxString m_events_regexp_match; - wxString m_events_regexp_replace; - - bool m_snrSlow; - - // LPC Post Filter - bool m_codec2LPCPostFilterEnable; - bool m_codec2LPCPostFilterBassBoost; - float m_codec2LPCPostFilterGamma; - float m_codec2LPCPostFilterBeta; - - // Speex Pre-Processor - bool m_speexpp_enable; - - // Mic In Equaliser - float m_MicInBassFreqHz; - float m_MicInBassGaindB; - float m_MicInTrebleFreqHz; - float m_MicInTrebleGaindB; - float m_MicInMidFreqHz; - float m_MicInMidGaindB; - float m_MicInMidQ; - bool m_MicInEQEnable; - - // Spk Out Equaliser - float m_SpkOutBassFreqHz; - float m_SpkOutBassGaindB; - float m_SpkOutTrebleFreqHz; - float m_SpkOutTrebleGaindB; - float m_SpkOutMidFreqHz; - float m_SpkOutMidGaindB; - float m_SpkOutMidQ; - bool m_SpkOutEQEnable; - - // Flags for displaying windows - int m_show_wf; - int m_show_spect; - int m_show_scatter; - int m_show_timing; - int m_show_freq; - int m_show_speech_in; - int m_show_speech_out; - int m_show_demod_in; - int m_show_test_frame_errors; - int m_show_test_frame_errors_hist; - - // optional vox trigger tone - bool m_leftChannelVoxTone; - - // UDP control port - bool m_udp_enable; - int m_udp_port; - - // notebook display after tx->rxtransition - int m_rxNbookCtrl; - - wxRect m_rTopWindow; - - int m_framesPerBuffer; - - bool loadConfig(); - bool saveConfig(); - - // Plugins ----------------------------------- - - wxString m_txtPlugInParam[PLUGIN_MAX_PARAMS]; - - // plugin details - - void *m_plugInHandle; - bool m_plugIn; - wxString m_plugInName; - int m_numPlugInParam; - wxString m_plugInParamName[PLUGIN_MAX_PARAMS]; - void *m_plugInStates; - void (*m_plugin_startfp)(void *); - void (*m_plugin_stopfp)(void *); - void (*m_plugin_rx_samplesfp)(void *, short *, int); - - // misc - - bool m_testFrames; - bool m_channel_noise; - float m_channel_snr_dB; - - int FilterEvent(wxEvent& event); - MainFrame *frame; - - // 700 options - - bool m_FreeDV700txClip; - bool m_FreeDV700Combine; - - // Noise simulation - - int m_noise_snr; - - // carrier attenuation - - bool m_attn_carrier_en; - int m_attn_carrier; - - // tone interferer simulation - - bool m_tone; - int m_tone_freq_hz; - int m_tone_amplitude; - - // Windows debug console - - bool m_debug_console; - protected: -}; - -// declare global static function wxGetApp() -DECLARE_APP(MainApp) - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// paCallBackData -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -typedef struct -{ - // libresample states for 48 to 8 kHz conversions - - SRC_STATE *insrc1; - SRC_STATE *outsrc1; - SRC_STATE *insrc2; - SRC_STATE *outsrc2; - SRC_STATE *insrcsf; - - // FIFOs attached to first sound card - - struct FIFO *infifo1; - struct FIFO *outfifo1; - - // FIFOs attached to second sound card - struct FIFO *infifo2; - struct FIFO *outfifo2; - - // FIFOs for rx process - struct FIFO *rxinfifo; - struct FIFO *rxoutfifo; - - int inputChannels1, inputChannels2; - - // EQ filter states - void *sbqMicInBass; - void *sbqMicInTreble; - void *sbqMicInMid; - void *sbqSpkOutBass; - void *sbqSpkOutTreble; - void *sbqSpkOutMid; - - bool micInEQEnable; - bool spkOutEQEnable; - - // optional loud tone on left channel to reliably trigger vox - bool leftChannelVoxTone; - float voxTonePhase; - -} paCallBackData; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// panel with custom loop checkbox for play file dialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MyExtraPlayFilePanel : public wxPanel -{ -public: - MyExtraPlayFilePanel(wxWindow *parent); - void setLoopPlayFileToMicIn(bool checked) { m_cb->SetValue(checked); } - bool getLoopPlayFileToMicIn(void) { return m_cb->GetValue(); } -private: - wxCheckBox *m_cb; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// panel with custom Seconds-to-record control for record file dialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MyExtraRecFilePanel : public wxPanel -{ -public: - MyExtraRecFilePanel(wxWindow *parent); - ~MyExtraRecFilePanel() - { - wxLogDebug("Destructor\n"); - } - void setSecondsToRecord(wxString value) { m_secondsToRecord->SetValue(value); } - wxString getSecondsToRecord(void) - { - wxLogDebug("getSecondsToRecord: %s\n",m_secondsToRecord->GetValue()); - return m_secondsToRecord->GetValue(); - } -private: - wxTextCtrl *m_secondsToRecord; -}; - -class txRxThread; -class UDPThread; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainFrame -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MainFrame : public TopFrame -{ - public: - MainFrame(wxString plugInName, wxWindow *parent); - virtual ~MainFrame(); - - PlotSpectrum* m_panelSpectrum; - PlotWaterfall* m_panelWaterfall; - PlotScatter* m_panelScatter; - PlotScalar* m_panelTimeOffset; - PlotScalar* m_panelFreqOffset; - PlotScalar* m_panelSpeechIn; - PlotScalar* m_panelSpeechOut; - PlotScalar* m_panelDemodIn; - PlotScalar* m_panelTestFrameErrors; - PlotScalar* m_panelTestFrameErrorsHist; - - bool m_RxRunning; - - PortAudioWrap *m_rxInPa; - PortAudioWrap *m_rxOutPa; - PortAudioWrap *m_txInPa; - PortAudioWrap *m_txOutPa; - - PaError m_rxErr; - PaError m_txErr; - - txRxThread* m_txRxThread; - - bool OpenHamlibRig(); - void OpenSerialPort(void); - void CloseSerialPort(void); - void SerialPTTRx(void); - - bool m_modal; - -#ifdef _USE_TIMER - wxTimer m_plotTimer; -#endif - - void destroy_fifos(void); - void destroy_src(void); - void autoDetectSoundCards(PortAudioWrap *pa); - - static int rxCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); - - static int txCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); - - - void initPortAudioDevice(PortAudioWrap *pa, int inDevice, int outDevice, - int soundCard, int sampleRate, int inputChannels); - - void togglePTT(void); - - wxIPV4address m_udp_addr; - wxDatagramSocket *m_udp_sock; - UDPThread *m_UDPThread; - void startUDPThread(void); - void stopUDPThread(void); - int PollUDP(); - bool m_schedule_restore; - - int vk_state; - void VoiceKeyerProcessEvent(int vk_event); - - protected: - - void setsnrBeta(bool snrSlow); - - // protected event handlers - virtual void OnCloseFrame(wxCloseEvent& event); - void OnExitClick(wxCommandEvent& event); - - void startTxStream(); - void startRxStream(); - void stopTxStream(); - void stopRxStream(); - void abortTxStream(); - void abortRxStream(); - - void OnTop(wxCommandEvent& event); - void OnExit( wxCommandEvent& event ); - - void OnToolsAudio( wxCommandEvent& event ); - void OnToolsAudioUI( wxUpdateUIEvent& event ); - void OnToolsComCfg( wxCommandEvent& event ); - void OnToolsComCfgUI( wxUpdateUIEvent& event ); - void OnToolsFilter( wxCommandEvent& event ); - void OnToolsOptions(wxCommandEvent& event); - void OnToolsOptionsUI(wxUpdateUIEvent& event); - - void OnToolsPlugInCfg( wxCommandEvent& event ); - void OnToolsPlugInCfgUI( wxUpdateUIEvent& event ); - - void OnPlayFileToMicIn( wxCommandEvent& event ); - void StopPlayFileToMicIn(void); - void OnRecFileFromRadio( wxCommandEvent& event ); - void OnPlayFileFromRadio( wxCommandEvent& event ); - - void OnHelpCheckUpdates( wxCommandEvent& event ); - void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ); - void OnHelpAbout( wxCommandEvent& event ); - void OnCmdSliderScroll( wxScrollEvent& event ); -// void OnSliderScrollBottom( wxScrollEvent& event ); -// void OnCmdSliderScrollChanged( wxScrollEvent& event ); -// void OnSliderScrollTop( wxScrollEvent& event ); - void OnCheckSQClick( wxCommandEvent& event ); - void OnCheckSNRClick( wxCommandEvent& event ); - - // Toggle Buttons - void OnTogBtnSplitClick(wxCommandEvent& event); - void OnTogBtnAnalogClick(wxCommandEvent& event); - void OnTogBtnRxID( wxCommandEvent& event ); - void OnTogBtnTxID( wxCommandEvent& event ); - void OnTogBtnPTT( wxCommandEvent& event ); - void OnTogBtnVoiceKeyerClick (wxCommandEvent& event); - void OnTogBtnOnOff( wxCommandEvent& event ); - - void OnCallSignReset( wxCommandEvent& event ); - void OnBerReset( wxCommandEvent& event ); - - //System Events - void OnPaint(wxPaintEvent& event); - void OnSize( wxSizeEvent& event ); - void OnUpdateUI( wxUpdateUIEvent& event ); - void OnDeleteConfig(wxCommandEvent&); -#ifdef _USE_TIMER - void OnTimer(wxTimerEvent &evt); -#endif -#ifdef _USE_ONIDLE - void OnIdle(wxIdleEvent &evt); -#endif - - int VoiceKeyerStartTx(void); - - private: - bool m_useMemory; - wxTextCtrl* m_tc; - int m_zoom; - float m_snrBeta; - - // Callsign/text messaging - char m_callsign[MAX_CALLSIGN]; - char *m_pcallsign; - unsigned int m_checksumGood; - unsigned int m_checksumBad; - - // Events - void processTxtEvent(char event[]); - class OptionsDlg *optionsDlg; - wxTimer spamTimer[MAX_EVENT_RULES]; - - // level Gauge - float m_maxLevel; - - // flags to indicate when new EQ filters need to be designed - - bool m_newMicInFilter; - bool m_newSpkOutFilter; - - void* designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q = 0.0); - void designEQFilters(paCallBackData *cb); - void deleteEQFilters(paCallBackData *cb); - - // Voice Keyer States - - int vk_rx_pause; - int vk_repeats, vk_repeat_counter; - float vk_rx_time; -}; - -void txRxProcessing(); - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class txRxThread - experimental tx/rx processing thread -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class txRxThread : public wxThread -{ -public: - txRxThread(void) : wxThread(wxTHREAD_JOINABLE) { m_run = 1; } - - // thread execution starts here - void *Entry() - { - while (m_run) - { - txRxProcessing(); - wxThread::Sleep(20); - } - return NULL; - } - - // called when the thread exits - whether it terminates normally or is - // stopped with Delete() (but not when it is Kill()ed!) - void OnExit() { } - -public: - bool m_run; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class UDPThread - waits for UDP messages -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class UDPThread : public wxThread -{ -public: - UDPThread(void) : wxThread(wxTHREAD_JOINABLE) { m_run = 1; } - - // thread execution starts here - void *Entry(); - - // called when the thread exits - whether it terminates normally or is - // stopped with Delete() (but not when it is Kill()ed!) - void OnExit() { } - -public: - MainFrame *mf; - bool m_run; -}; - -void resample_for_plot(struct FIFO *plotFifo, short buf[], int length, int fs); - -int resample(SRC_STATE *src, - short output_short[], - short input_short[], - int output_sample_rate, - int input_sample_rate, - int length_output_short, // maximum output array length in samples - int length_input_short - ); -void txRxProcessing(); -void per_frame_rx_processing( - FIFO *output_fifo, // decoded speech samples - FIFO *input_fifo // modem samples input to demod - ); - -// FreeDv API calls this when there is a test frame that needs a-plottin' - -void my_freedv_put_error_pattern(void *state, short error_pattern[], int sz_error_pattern); - -// FreeDv API calls these puppies when it needs/receives a text char - -char my_get_next_tx_char(void *callback_state); -void my_put_next_rx_char(void *callback_state, char c); - -// helper complex freq shift function - -void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin); - -// Helper function called by plugin - -int plugin_get_persistant(char name[], char value[]); - -#endif //__FDMDV2_MAIN__ diff --git a/freedv/branches/1.2/src/fdmdv2_pa_wrapper.cpp b/freedv/branches/1.2/src/fdmdv2_pa_wrapper.cpp deleted file mode 100644 index 2f67ca26..00000000 --- a/freedv/branches/1.2/src/fdmdv2_pa_wrapper.cpp +++ /dev/null @@ -1,324 +0,0 @@ -//========================================================================== -// Name: fdmdv2_pa_wrapper.cpp -// Purpose: Implements a wrapper class around the PortAudio library. -// Created: August 12, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "fdmdv2_pa_wrapper.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// PortAudioWrap() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PortAudioWrap::PortAudioWrap() -{ - m_pStream = NULL; - m_pUserData = NULL; - m_samplerate = 0; - m_framesPerBuffer = 0; - m_statusFlags = 0; - m_pStreamCallback = NULL; - m_pStreamFinishedCallback = NULL; - m_pTimeInfo = 0; - m_newdata = false; - -// loadData(); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// ~PortAudioWrap() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PortAudioWrap::~PortAudioWrap() -{ -} - -//---------------------------------------------------------------- -// streamOpen() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamOpen() -{ - return Pa_OpenStream( - &m_pStream, - m_inputBuffer.device == paNoDevice ? NULL : &m_inputBuffer, - m_outputBuffer.device == paNoDevice ? NULL : &m_outputBuffer, - m_samplerate, - m_framesPerBuffer, - m_statusFlags, - *m_pStreamCallback, - m_pUserData - ); -} - -//---------------------------------------------------------------- -// streamStart() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamStart() -{ - return Pa_StartStream(m_pStream); -} - -//---------------------------------------------------------------- -// streamClose() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamClose() -{ - if(isOpen()) - { - PaError rv = Pa_CloseStream(m_pStream); - return rv; - } - else - { - return paNoError; - } -} - -//---------------------------------------------------------------- -// terminate() -//---------------------------------------------------------------- -void PortAudioWrap::terminate() -{ - if(Pa_IsStreamStopped(m_pStream) != paNoError) - { - Pa_StopStream(m_pStream); - } - Pa_Terminate(); -} - -//---------------------------------------------------------------- -// stop() -//---------------------------------------------------------------- -void PortAudioWrap::stop() -{ - Pa_StopStream(m_pStream); -} - -//---------------------------------------------------------------- -// abort() -//---------------------------------------------------------------- -void PortAudioWrap::abort() -{ - Pa_AbortStream(m_pStream); -} - -//---------------------------------------------------------------- -// isStopped() -//---------------------------------------------------------------- -bool PortAudioWrap::isStopped() const -{ - PaError ret = Pa_IsStreamStopped(m_pStream); - return ret; -} - -//---------------------------------------------------------------- -// isActive() -//---------------------------------------------------------------- -bool PortAudioWrap::isActive() const -{ - PaError ret = Pa_IsStreamActive(m_pStream); - return ret; -} - -//---------------------------------------------------------------- -// isOpen() -//---------------------------------------------------------------- -bool PortAudioWrap::isOpen() const -{ - return (m_pStream != NULL); -} - -//---------------------------------------------------------------- -// getDefaultInputDevice() -//---------------------------------------------------------------- -PaDeviceIndex PortAudioWrap::getDefaultInputDevice() -{ - return Pa_GetDefaultInputDevice(); -} - -//---------------------------------------------------------------- -// getDefaultOutputDevice() -//---------------------------------------------------------------- -PaDeviceIndex PortAudioWrap::getDefaultOutputDevice() -{ - return Pa_GetDefaultOutputDevice(); -} - -//---------------------------------------------------------------- -// setInputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputChannelCount(int count) -{ - m_inputBuffer.channelCount = count; - return paNoError; -} - -//---------------------------------------------------------------- -// getInputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::getInputChannelCount() -{ - return m_inputBuffer.channelCount; -} - -//---------------------------------------------------------------- -// setInputSampleFormat() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputSampleFormat(PaSampleFormat format) -{ - m_inputBuffer.sampleFormat = format; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputLatency() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputLatency(PaTime latency) -{ - m_inputBuffer.suggestedLatency = latency; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputHostApiStreamInfo() -//---------------------------------------------------------------- -void PortAudioWrap::setInputHostApiStreamInfo(void *info) -{ - m_inputBuffer.hostApiSpecificStreamInfo = info; -} - -//---------------------------------------------------------------- -// getInputDefaultLowLatency() -//---------------------------------------------------------------- -PaTime PortAudioWrap::getInputDefaultLowLatency() -{ - return Pa_GetDeviceInfo(m_inputBuffer.device)->defaultLowInputLatency; -} - -//---------------------------------------------------------------- -// setOutputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputChannelCount(int count) -{ - m_outputBuffer.channelCount = count; - return paNoError; -} - -//---------------------------------------------------------------- -// getOutputChannelCount() -//---------------------------------------------------------------- -const int PortAudioWrap::getOutputChannelCount() -{ - return m_outputBuffer.channelCount; -} - -//---------------------------------------------------------------- -// getDeviceName() -//---------------------------------------------------------------- -const char *PortAudioWrap::getDeviceName(PaDeviceIndex dev) -{ - const PaDeviceInfo *info; - info = Pa_GetDeviceInfo(dev); - return info->name; -} - -//---------------------------------------------------------------- -// setOutputSampleFormat() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputSampleFormat(PaSampleFormat format) -{ - m_outputBuffer.sampleFormat = format; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputLatency() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputLatency(PaTime latency) -{ - m_outputBuffer.suggestedLatency = latency; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputHostApiStreamInfo() -//---------------------------------------------------------------- -void PortAudioWrap::setOutputHostApiStreamInfo(void *info) -{ - m_outputBuffer.hostApiSpecificStreamInfo = info; -} - -//---------------------------------------------------------------- -// getOutputDefaultLowLatency() -//---------------------------------------------------------------- -PaTime PortAudioWrap::getOutputDefaultLowLatency() -{ - return Pa_GetDeviceInfo(m_outputBuffer.device)->defaultLowOutputLatency; -} - -//---------------------------------------------------------------- -// setFramesPerBuffer() -//---------------------------------------------------------------- -PaError PortAudioWrap::setFramesPerBuffer(unsigned long size) -{ - m_framesPerBuffer = size; - return paNoError; -} - -//---------------------------------------------------------------- -// setSampleRate() -//---------------------------------------------------------------- -PaError PortAudioWrap::setSampleRate(unsigned long rate) -{ - m_samplerate = rate; - return paNoError; -} - -//---------------------------------------------------------------- -// setStreamFlags() -//---------------------------------------------------------------- -PaError PortAudioWrap::setStreamFlags(PaStreamFlags flags) -{ - m_statusFlags = flags; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputDevice() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputDevice(PaDeviceIndex index) -{ - m_inputBuffer.device = index; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputDevice() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputDevice(PaDeviceIndex index) -{ - m_outputBuffer.device = index; - return paNoError; -} - -//---------------------------------------------------------------- -// setCallback() -//---------------------------------------------------------------- -PaError PortAudioWrap::setCallback(PaStreamCallback *callback) -{ - m_pStreamCallback = callback; - return paNoError; -} - diff --git a/freedv/branches/1.2/src/fdmdv2_pa_wrapper.h b/freedv/branches/1.2/src/fdmdv2_pa_wrapper.h deleted file mode 100644 index 3d216c0d..00000000 --- a/freedv/branches/1.2/src/fdmdv2_pa_wrapper.h +++ /dev/null @@ -1,115 +0,0 @@ -//========================================================================== -// Name: fdmdv2_pa_wrapper.h -// Purpose: Defines a wrapper class around PortAudio -// Created: August 12, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include -#include "fdmdv2_defines.h" -#include "codec2_fdmdv.h" -#include "codec2.h" -#include "portaudio.h" - -#define PA_SAMPLE_TYPE paInt16 //paFloat32 -#define FRAMES_PER_BUFFER (64) - -typedef float SAMPLE; - -class PortAudioWrap -{ - public: - PortAudioWrap(); - ~PortAudioWrap(); - -// float m_av_mag[FDMDV_NSPEC]; - - private: - PaStream *m_pStream; - void *m_pUserData; - PaStreamCallback *m_pStreamCallback; - PaStreamFinishedCallback *m_pStreamFinishedCallback; - const PaStreamCallbackTimeInfo *m_pTimeInfo; - struct FDMDV *m_pFDMDV_state; - PaStreamParameters m_inputBuffer; - PaStreamParameters m_outputBuffer; - int m_samplerate; - unsigned long m_framesPerBuffer; - PaStreamCallbackFlags m_statusFlags; - bool m_newdata; - - public: - - void averageData(float mag_dB[]); - - int getDeviceCount() { return Pa_GetDeviceCount(); } - PaDeviceIndex getDefaultInputDevice(); - PaDeviceIndex getDefaultOutputDevice(); - PaStreamParameters *getDeviceInfo(PaDeviceIndex idx); - - PaError setFramesPerBuffer(unsigned long size); - PaError setSampleRate(unsigned long size); - - PaError setStreamFlags(PaStreamFlags flags); - PaError setCallback(PaStreamCallback *m_pStreamCallback); - PaError setStreamCallback(PaStream *stream, PaStreamCallback* callback) { m_pStreamCallback = callback; return 0;} - PaError setStreamFinishedCallback(PaStream *stream, PaStreamFinishedCallback* m_pStreamFinishedCallback); - - void setInputBuffer(const PaStreamParameters& inputBuffer) {this->m_inputBuffer = inputBuffer;} - PaError setInputDevice(PaDeviceIndex dev); - PaError setInputChannelCount(int count); - int getInputChannelCount(); - PaError setInputSampleFormat(PaSampleFormat format); - PaError setInputSampleRate(PaSampleFormat format); - PaError setInputLatency(PaTime latency); - void setInputHostApiStreamInfo(void *info = NULL); - PaTime getInputDefaultLowLatency(); - const char *getDeviceName(PaDeviceIndex dev); - - PaError setOutputDevice(PaDeviceIndex dev); - PaError setOutputChannelCount(int count); - const int getOutputChannelCount(); - PaError setOutputSampleFormat(PaSampleFormat format); - PaError setOutputLatency(PaTime latency); - void setOutputHostApiStreamInfo(void *info = NULL); - PaTime getOutputDefaultLowLatency(); - - void setFdmdvState(FDMDV* fdmdv_state) {this->m_pFDMDV_state = fdmdv_state;} - void setOutputBuffer(const PaStreamParameters& outputBuffer) {this->m_outputBuffer = outputBuffer;} - void setTimeInfo(PaStreamCallbackTimeInfo* timeInfo) {this->m_pTimeInfo = timeInfo;} - void setUserData(void* userData) {this->m_pUserData = userData;} - unsigned long getFramesPerBuffer() const {return m_framesPerBuffer;} - const PaStreamParameters& getInputBuffer() const {return m_inputBuffer;} - const PaStreamParameters& getOutputBuffer() const {return m_outputBuffer;} - const PaStreamCallbackFlags& getStatusFlags() const {return m_statusFlags;} - - FDMDV* getFdmdvState() {return m_pFDMDV_state;} - int getSamplerate() const {return m_samplerate;} - PaStream* getStream() {return m_pStream;} - void *getUserData() {return m_pUserData;} - bool getDataAvail() {return m_newdata;} - PaError streamStart(); - PaError streamClose(); - PaError streamOpen(); - void terminate(); - void stop(); - void abort(); - bool isOpen() const; - bool isStopped() const; - bool isActive() const; -// void loadData(); -}; diff --git a/freedv/branches/1.2/src/fdmdv2_plot.cpp b/freedv/branches/1.2/src/fdmdv2_plot.cpp deleted file mode 100644 index 1b85cd90..00000000 --- a/freedv/branches/1.2/src/fdmdv2_plot.cpp +++ /dev/null @@ -1,283 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot.cpp -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "fdmdv2_plot.h" - -BEGIN_EVENT_TABLE(PlotPanel, wxPanel) - EVT_PAINT (PlotPanel::OnPaint) - EVT_MOTION (PlotPanel::OnMouseMove) - EVT_LEFT_DOWN (PlotPanel::OnMouseLeftDown) - EVT_LEFT_UP (PlotPanel::OnMouseLeftUp) - EVT_RIGHT_DOWN (PlotPanel::OnMouseRightDown) - EVT_MOUSEWHEEL (PlotPanel::OnMouseWheelMoved) - EVT_SIZE (PlotPanel::OnSize) - EVT_SHOW (PlotPanel::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotPanel(wxFrame* parent) : wxPanel(parent) -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotPanel::PlotPanel(wxFrame* parent) : wxPanel(parent) -{ - m_pNoteBook = (wxAuiNotebook *) parent; - m_pTopFrame = (MainFrame *)m_pNoteBook->GetParent(); - m_zoomFactor = 1.0; - m_pBmp = NULL; - m_pPix = NULL; - m_firstPass = true; - m_line_color = 0; - m_newdata = false; - m_clip = false; - m_use_bitmap = true; - m_rubberBand = false; - m_mouseDown = false; - m_penShortDash = wxPen(wxColor(0xA0, 0xA0, 0xA0), 1, wxPENSTYLE_SHORT_DASH); - m_penDotDash = wxPen(wxColor(0xD0, 0xD0, 0xD0), 1, wxPENSTYLE_DOT_DASH); - m_penSolid = wxPen(wxColor(0x00, 0x00, 0x00), 1, wxPENSTYLE_SOLID); - SetBackgroundStyle(wxBG_STYLE_PAINT); - SetLabelSize(10.0); -} - -//------------------------------------------------------------------------- -// ~PlotPanel() -//------------------------------------------------------------------------- -PlotPanel::~PlotPanel() -{ - if(m_pBmp != NULL) - { - delete m_pBmp; - } -} - -//------------------------------------------------------------------------- -// GetLabelSize() -//------------------------------------------------------------------------- -double PlotPanel::GetLabelSize() -{ - return m_label_size; -} - -//------------------------------------------------------------------------- -// SetLabelSize() -//------------------------------------------------------------------------- -void PlotPanel::SetLabelSize(double size) -{ - m_label_size = size; -} - -//------------------------------------------------------------------------- -// OnShow() -//------------------------------------------------------------------------- -void PlotPanel::OnShow(wxShowEvent& event) -{ - this->Refresh(); -} - -//------------------------------------------------------------------------- -// OnErase() -//------------------------------------------------------------------------- -void PlotPanel::OnErase(wxEraseEvent& event) -{ - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnSize() -//------------------------------------------------------------------------- -void PlotPanel::OnSize(wxSizeEvent& event) -{ - m_rCtrlPrev = m_rCtrl; - m_rCtrl = GetClientRect(); - if(m_use_bitmap) - { - if(!m_oImage.IsOk()) - { - m_oImage.Create(m_rCtrl.GetWidth(), m_rCtrl.GetHeight(), true); - } - else - { - m_oImage.Rescale(m_rCtrl.GetWidth(), m_rCtrl.GetHeight()); - } - m_pBmp = new wxBitmap(m_oImage, wxBITMAP_SCREEN_DEPTH); - m_firstPass = true; - } - this->Refresh(); -} - -//------------------------------------------------------------------------- -// OnMouseMove() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseMove(wxMouseEvent& event) -{ -// if(m_mouseDown) -// { -// paintNow(); -// } -} - -//------------------------------------------------------------------------- -// OnMouseLeftDown() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseLeftDown(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseRightDown() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseRightDown(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseWheelMoved() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseWheelMoved(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseLeftUp() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseLeftUp(wxMouseEvent& event) -{ - m_mouseDown = false; -} - -//------------------------------------------------------------------------- -// SetZoomFactor() -//------------------------------------------------------------------------- -double PlotPanel::SetZoomFactor(double zf) -{ - if((zf > 0) && (zf < 5.0)) - { - m_zoomFactor = zf; - } - return zf; -} - -//------------------------------------------------------------------------- -// GetZoomFactor() -//------------------------------------------------------------------------- -double PlotPanel::GetZoomFactor(double zf) -{ - return m_zoomFactor; -} - -//------------------------------------------------------------------------- -// draw() -//------------------------------------------------------------------------- -void PlotPanel::draw(wxAutoBufferedPaintDC& pDC) -{ - printf("PlotPanel::draw()"); - wxMemoryDC m_mDC; - m_mDC.SelectObject(*m_pBmp); - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - m_rGrid.Offset(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER); - - pDC.Clear(); - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - if(m_firstPass) - { - m_firstPass = false; - m_mDC.FloodFill(0, 0, VERY_LTGREY_COLOR); - - // Draw a filled rectangle with aborder - wxBrush ltGraphBkgBrush = wxBrush(DARK_BLUE_COLOR); - m_mDC.SetBrush(ltGraphBkgBrush); - m_mDC.SetPen(wxPen(BLACK_COLOR, 0)); - m_mDC.DrawRectangle(m_rPlot); - } - if(m_newdata) - { - m_newdata = false; - int t = m_rPlot.GetTop(); - int l = m_rPlot.GetLeft(); -// int r = m_rPlot.GetRight(); - int h = m_rPlot.GetHeight(); - int w = m_rPlot.GetWidth(); - pDC.Blit(l, t, w, h, &m_mDC, l, t); - } - drawGraticule(pDC); - m_mDC.SetBrush(wxNullBrush); - m_mDC.SelectObject(wxNullBitmap); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotPanel::drawGraticule(wxAutoBufferedPaintDC& pDC) -{ - int p; - char buf[15]; - wxString s; - - // Vertical gridlines - pDC.SetPen(m_penShortDash); - for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < ((m_rGrid.GetWidth() - XLEFT_OFFSET) + GRID_INCREMENT); p += GRID_INCREMENT) - { - pDC.DrawLine(p, (m_rGrid.GetHeight() + PLOT_BORDER), p, PLOT_BORDER); - } - // Horizontal gridlines - pDC.SetPen(m_penDotDash); - for(p = (m_rGrid.GetHeight() - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT) - { - pDC.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (p + PLOT_BORDER), (m_rGrid.GetWidth() + PLOT_BORDER + XLEFT_OFFSET), (p + PLOT_BORDER)); - } - // Label the X-Axis - pDC.SetPen(wxPen(GREY_COLOR, 1)); - for(p = GRID_INCREMENT; p < (m_rGrid.GetWidth() - YBOTTOM_OFFSET); p += GRID_INCREMENT) - { - sprintf(buf, "%1.1f Hz",(double)(p / 10)); - pDC.DrawText(buf, p - PLOT_BORDER + XLEFT_OFFSET, m_rGrid.GetHeight() + YBOTTOM_OFFSET/2); - } - // Label the Y-Axis - //for(p = GRID_INCREMENT; p < (h - YBOTTOM_OFFSET); p += GRID_INCREMENT) - for(p = (m_rGrid.GetHeight() - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT) - { - sprintf(buf, "%1.0f", (double)((m_rGrid.GetHeight() - p) * -10)); - pDC.DrawText(buf, XLEFT_TEXT_OFFSET, p); - } -} - -//------------------------------------------------------------------------- -// paintEvent() -// -// Called by the system of by wxWidgets when the panel needs -// to be redrawn. You can also trigger this call by calling -// Refresh()/Update(). -//------------------------------------------------------------------------- -void PlotPanel::OnPaint(wxPaintEvent & evt) -{ - wxAutoBufferedPaintDC pdc(this); - draw(pdc); -} - diff --git a/freedv/branches/1.2/src/fdmdv2_plot.h b/freedv/branches/1.2/src/fdmdv2_plot.h deleted file mode 100644 index 25309d3a..00000000 --- a/freedv/branches/1.2/src/fdmdv2_plot.h +++ /dev/null @@ -1,150 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot.h -// Purpose: Declares simple wxWidgets application with GUI -// Created: Apr. 10, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -//#include "fdmdv2_main.h" -#ifndef __FDMDV2_PLOT__ -#define __FDMDV2_PLOT__ -#include -#include -#include -#include -#include - -#define MAX_ZOOM 7 -#define MAX_BMP_X (400 * MAX_ZOOM) -#define MAX_BMP_Y (400 * MAX_ZOOM) -#define DATA_LINE_HEIGHT 10 -#define TEXT_BASELINE_OFFSET_Y -5 - - -#define wxUSE_FILEDLG 1 -#define wxUSE_LIBPNG 1 -#define wxUSE_LIBJPEG 1 -#define wxUSE_GIF 1 -#define wxUSE_PCX 1 -#define wxUSE_LIBTIFF 1 - -#define PLOT_BORDER 12 -#define XLEFT_OFFSET 40 -#define XLEFT_TEXT_OFFSET 6 -#define YBOTTOM_OFFSET 20 -#define YBOTTOM_TEXT_OFFSET 15 -#define GRID_INCREMENT 50 - -#define BLACK_COLOR wxColor(0x00, 0x00, 0x00) -#define GREY_COLOR wxColor(0x80, 0x80, 0x80) -#define DARK_GREY_COLOR wxColor(0x40, 0x40, 0x40) -#define MEDIUM_GREY_COLOR wxColor(0xC0, 0xC0, 0xC0) -#define LIGHT_GREY_COLOR wxColor(0xE0, 0xE0, 0xE0) -#define VERY_LTGREY_COLOR wxColor(0xF8, 0xF8, 0xF8) -#define WHITE_COLOR wxColor(0xFF, 0xFF, 0xFF) - -#define DARK_BLUE_COLOR wxColor(0x00, 0x00, 0x60) -#define BLUE_COLOR wxColor(0x00, 0x00, 0xFF) -#define LIGHT_BLUE_COLOR wxColor(0x80, 0x80, 0xFF) - -#define RED_COLOR wxColor(0xFF, 0x5E, 0x5E) -#define LIGHT_RED_COLOR wxColor(0xFF, 0xE0, 0xE0) -#define DARK_RED_COLOR wxColor(0xFF, 0x00, 0x00) -#define PINK_COLOR wxColor(0xFF, 0x80, 0xFF) - -#define LIGHT_GREEN_COLOR wxColor(0xE3, 0xFF, 0xE0) -#define GREEN_COLOR wxColor(0x95, 0xFF, 0x8A) -#define DARK_GREEN_COLOR wxColor(0x20, 0xFF, 0x08) -#define VERY_GREEN_COLOR wxColor(0x00, 0xFF, 0x00) - -#define YELLOW_COLOR wxColor(0xFF, 0xFF, 0x5E) -#define LIGHT_YELLOW_COLOR wxColor(0xFF, 0xFF, 0xB5) -#define DARK_YELLOW_COLOR wxColor(0xFF, 0xFF, 0x08) - -class MainFrame; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotPanel -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotPanel : public wxPanel -{ - public: - PlotPanel(wxFrame* parent); - ~PlotPanel(); - wxPen m_penShortDash; - wxPen m_penDotDash; - wxPen m_penSolid; - wxRect m_rCtrlPrev; - wxRect m_rCtrl; - wxRect m_rGrid; - wxRect m_rPlot; - MainFrame *m_pTopFrame; - wxAuiNotebook *m_pNoteBook; - double m_label_size; - wxSize m_Bufsz; - bool m_newdata; - wxImage m_oImage; - wxBitmap *m_pBmp; - wxNativePixelData *m_pPix; - - // some useful events - void OnMouseMove(wxMouseEvent& event); - virtual void OnMouseLeftDown(wxMouseEvent& event); - void OnMouseLeftUp(wxMouseEvent& event); - virtual void OnMouseRightDown(wxMouseEvent& event); - void OnMouseWheelMoved(wxMouseEvent& event); - void OnClose(wxCloseEvent& event ){ event.Skip(); } - void OnSize( wxSizeEvent& event ); - void OnErase(wxEraseEvent& event); - void OnPaint(wxPaintEvent& event); - //void OnUpdateUI( wxUpdateUIEvent& event ){ event.Skip(); } - - void paintEvent(wxPaintEvent & evt); - virtual void draw(wxAutoBufferedPaintDC& pdc); - virtual void drawGraticule(wxAutoBufferedPaintDC& pdc); - virtual double SetZoomFactor(double zf); - virtual double GetZoomFactor(double zf); - virtual void OnShow(wxShowEvent& event); - virtual double GetLabelSize(); - virtual void SetLabelSize(double size); - - protected: - int m_x; - int m_y; - int m_left; - int m_top; - int m_prev_w; - int m_prev_h; - int m_prev_x; - int m_prev_y; - bool m_use_bitmap; - bool m_clip; - bool m_rubberBand; - bool m_mouseDown; - bool m_firstPass; - double m_zoomFactor; - int m_greyscale; - int m_line_color; - DECLARE_EVENT_TABLE() -}; -#endif //__FDMDV2_PLOT__ diff --git a/freedv/branches/1.2/src/fdmdv2_plot_scalar.cpp b/freedv/branches/1.2/src/fdmdv2_plot_scalar.cpp deleted file mode 100644 index 7a07f46f..00000000 --- a/freedv/branches/1.2/src/fdmdv2_plot_scalar.cpp +++ /dev/null @@ -1,281 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_scalar.cpp -// Purpose: Plots scalar amplitude against time -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_main.h" -#include "fdmdv2_plot_scalar.h" - -BEGIN_EVENT_TABLE(PlotScalar, PlotPanel) - EVT_PAINT (PlotScalar::OnPaint) - EVT_MOTION (PlotScalar::OnMouseMove) - EVT_MOUSEWHEEL (PlotScalar::OnMouseWheelMoved) - EVT_SIZE (PlotScalar::OnSize) - EVT_SHOW (PlotScalar::OnShow) -// EVT_ERASE_BACKGROUND(PlotScalar::OnErase) -END_EVENT_TABLE() - -//---------------------------------------------------------------- -// PlotScalar() -//---------------------------------------------------------------- -PlotScalar::PlotScalar(wxFrame* parent, - int channels, // number on channels to plot - float t_secs, // time covered by entire x axis in seconds - float sample_period_secs, // time between each sample in seconds - float a_min, // min ampltude of samples being plotted - float a_max, // max ampltude of samples being plotted - float graticule_t_step, // time step of x (time) axis graticule in seconds - float graticule_a_step, // step of amplitude axis graticule - const char a_fmt[], // printf format string for amplitude axis labels - int mini // true for mini-plot - don't draw graticule - ): PlotPanel(parent) -{ - int i; - - m_rCtrl = GetClientRect(); - - m_channels = channels; - m_t_secs = t_secs; - m_sample_period_secs = sample_period_secs; - m_a_min = a_min; - m_a_max = a_max; - m_graticule_t_step = graticule_t_step; - m_graticule_a_step = graticule_a_step; - assert(strlen(a_fmt) < 15); - strcpy(m_a_fmt, a_fmt); - m_mini = mini; - - // work out number of samples we will store and allocate storage - - m_samples = m_t_secs/m_sample_period_secs; - m_mem = new float[m_samples*m_channels]; - - for(i = 0; i < m_samples*m_channels; i++) - { - m_mem[i] = 0.0; - } -} - -//---------------------------------------------------------------- -// ~PlotScalar() -//---------------------------------------------------------------- -PlotScalar::~PlotScalar() -{ - delete m_mem; -} - -//---------------------------------------------------------------- -// add_new_sample() -//---------------------------------------------------------------- -void PlotScalar::add_new_sample(int channel, float sample) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-1; i++) - { - m_mem[offset+i] = m_mem[offset+i+1]; - } - m_mem[offset+m_samples-1] = sample; -} - -//---------------------------------------------------------------- -// add_new_samples() -//---------------------------------------------------------------- -void PlotScalar::add_new_short_samples(int channel, short samples[], int length, float scale_factor) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-length; i++) - m_mem[offset+i] = m_mem[offset+i+length]; - for(; i < m_samples; i++) - m_mem[offset+i] = (float)*samples++/scale_factor; -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotScalar::draw(wxAutoBufferedPaintDC& dc) -{ - float index_to_px; - float a_to_py; - int i; - int x, y; - int prev_x, prev_y; - float a; - - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - if (!m_mini) - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - //printf("h %d w %d\n", m_rCtrl.GetWidth(), m_rCtrl.GetHeight()); - //printf("h %d w %d\n", m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - // black background - - dc.Clear(); - if (m_mini) - m_rPlot = wxRect(0, 0, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - else - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - index_to_px = (float)m_rGrid.GetWidth()/m_samples; - a_to_py = (float)m_rGrid.GetHeight()/(m_a_max - m_a_min); - - wxPen pen; - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - // draw all samples - - prev_x = prev_y = 0; // stop warning - - // plot each channel - - int offset; - for(offset=0; offset m_a_max) a = m_a_max; - - // invert y axis and offset by minimum - - y = m_rGrid.GetHeight() - a_to_py * a + m_a_min*a_to_py; - - // put inside plot window - - if (!m_mini) { - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - } - - if (i) - dc.DrawLine(x, y, prev_x, prev_y); - prev_x = x; prev_y = y; - } - } - - drawGraticule(dc); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotScalar::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - float t, a; - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float sec_to_px; - float a_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - sec_to_px = (float)m_rGrid.GetWidth()/m_t_secs; - a_to_py = (float)m_rGrid.GetHeight()/(m_a_max - m_a_min); - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Vertical gridlines - - dc.SetPen(m_penShortDash); - for(t=0; t<=m_t_secs; t+=m_graticule_t_step) { - x = t*sec_to_px; - if (m_mini) { - dc.DrawLine(x, m_rGrid.GetHeight(), x, 0); - } - else { - x += PLOT_BORDER + XLEFT_OFFSET; - dc.DrawLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, PLOT_BORDER); - } - if (!m_mini) { - sprintf(buf, "%2.1fs", t); - GetTextExtent(buf, &text_w, &text_h); - dc.DrawText(buf, x - text_w/2, m_rGrid.GetHeight() + PLOT_BORDER + YBOTTOM_TEXT_OFFSET); - } - } - - // Horizontal gridlines - - dc.SetPen(m_penDotDash); - for(a=m_a_min; a. -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SCALAR__ -#define __FDMDV2_PLOT_SCALAR__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotScalar -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotScalar: public PlotPanel -{ - public: - - PlotScalar(wxFrame* parent, - int channels, - float t_secs, - float sample_period_secs, - float a_min, - float a_max, - float graticule_t_step, - float graticule_a_step, - const char a_fmt[], - int mini - ); - ~PlotScalar(); - void add_new_sample(int channel, float sample); - void add_new_short_samples(int channel, short samples[], int length, float scale_factor); - - protected: - - int m_channels; - float m_t_secs; - float m_sample_period_secs; - float m_a_min; - float m_a_max; - float m_graticule_t_step; - float m_graticule_a_step; - char m_a_fmt[15]; - int m_mini; - int m_samples; - float *m_mem; - - void draw(wxAutoBufferedPaintDC& dc); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - - DECLARE_EVENT_TABLE() -}; - -#endif // __FDMDV2_PLOT_SCALAR__ - diff --git a/freedv/branches/1.2/src/fdmdv2_plot_scatter.cpp b/freedv/branches/1.2/src/fdmdv2_plot_scatter.cpp deleted file mode 100644 index 12957f44..00000000 --- a/freedv/branches/1.2/src/fdmdv2_plot_scatter.cpp +++ /dev/null @@ -1,289 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_scatter.cpp -// Purpose: A scatter plot derivative of fdmdv2_plot. -// Created: June 24, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_plot_scatter.h" - -BEGIN_EVENT_TABLE(PlotScatter, PlotPanel) - EVT_PAINT (PlotScatter::OnPaint) - EVT_MOTION (PlotScatter::OnMouseMove) - EVT_MOUSEWHEEL (PlotScatter::OnMouseWheelMoved) - EVT_SIZE (PlotScatter::OnSize) - EVT_SHOW (PlotScatter::OnShow) -// EVT_ERASE_BACKGROUND(PlotScatter::OnErase) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// PlotScatter -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotScatter::PlotScatter(wxFrame* parent) : PlotPanel(parent) -{ - int i; - - for(i=0; i < SCATTER_MEM_SYMS_MAX; i++) - { - m_mem[i].real = 0.0; - m_mem[i].imag = 0.0; - } - - m_filter_max_xy = m_filter_max_y = 0.1; - - // defaults so we start off with something sensible - - Nsym = 14+1; - scatterMemSyms = ((int)(SCATTER_MEM_SECS*(Nsym/DT))); - assert(scatterMemSyms <= SCATTER_MEM_SYMS_MAX); - - Ncol = 0; - memset(eye_mem, 0, sizeof(eye_mem)); - - mode = PLOT_SCATTER_MODE_SCATTER; -} - -// changing number of carriers changes number of symbols to plot -void PlotScatter::setNc(int Nc) { - Nsym = Nc+1; - assert(Nsym <= (MODEM_STATS_NC_MAX+1)); - scatterMemSyms = ((int)(SCATTER_MEM_SECS*(Nsym/DT))); - assert(scatterMemSyms <= SCATTER_MEM_SYMS_MAX); -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotScatter::draw(wxAutoBufferedPaintDC& dc) -{ - float x_scale; - float y_scale; - int i,j; - int x; - int y; - wxColour sym_to_colour[] = {wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255), - wxColor(255,255,0), - wxColor(255,255,255), - wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255), - wxColor(255,255,0), - wxColor(255,255,255), - wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255) - }; - - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - // black background - - dc.Clear(); - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - wxPen pen; - pen.SetWidth(1); // note this is ignored by DrawPoint - - if (mode == PLOT_SCATTER_MODE_SCATTER) { - - // automatically scale, first measure the maximum value - - float max_xy = 1E-12; - float real,imag; - for(i=0; i< scatterMemSyms; i++) { - real = fabs(m_mem[i].real); - imag = fabs(m_mem[i].imag); - if (real > max_xy) - max_xy = real; - if (imag > max_xy) - max_xy = imag; - } - - // smooth it out and set a lower limit to prevent divide by 0 issues - - m_filter_max_xy = BETA*m_filter_max_xy + (1 - BETA)*2.5*max_xy; - if (m_filter_max_xy < 0.001) - m_filter_max_xy = 0.001; - - // quantise to log steps to prevent scatter scaling bobbing about too - // much as scaling varies - - float quant_m_filter_max_xy = exp(floor(0.5+log(m_filter_max_xy))); - //printf("max_xy: %f m_filter_max_xy: %f quant_m_filter_max_xy: %f\n", max_xy, m_filter_max_xy, quant_m_filter_max_xy); - - x_scale = (float)m_rGrid.GetWidth()/quant_m_filter_max_xy; - y_scale = (float)m_rGrid.GetHeight()/quant_m_filter_max_xy; - - // draw all samples - - for(i = 0; i < scatterMemSyms; i++) { - x = x_scale * m_mem[i].real + m_rGrid.GetWidth()/2; - y = y_scale * m_mem[i].imag + m_rGrid.GetHeight()/2; - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - pen.SetColour(sym_to_colour[i%Nsym]); - dc.SetPen(pen); - dc.DrawPoint(x, y); - } - } - - if (mode == PLOT_SCATTER_MODE_EYE) { - - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - // automatically scale, first measure the maximum Y value - - float max_y = 1E-12; - float min_y = 1E+12; - for(i=0; i max_y) { - max_y = eye_mem[i][j]; - } - if (eye_mem[i][j] < min_y) { - min_y = eye_mem[i][j]; - } - } - } - - // smooth it out and set a lower limit to prevent divide by 0 issues - - m_filter_max_y = BETA*m_filter_max_y + (1 - BETA)*2.5*max_y; - if (m_filter_max_y < 0.001) - m_filter_max_y = 0.001; - - // quantise to log steps to prevent scatter scaling bobbing about too - // much as scaling varies - - float quant_m_filter_max_y = exp(floor(0.5+log(m_filter_max_y))); - //printf("min_y: %4.3f max_y: %4.3f quant_m_filter_max_y: %4.3f\n", min_y, max_y, quant_m_filter_max_y); - - x_scale = (float)m_rGrid.GetWidth()/Ncol; - y_scale = (float)m_rGrid.GetHeight()/quant_m_filter_max_y; - //printf("GetWidth(): %d GetHeight(): %d\n", m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - // plot eye traces row by row - - int prev_x, prev_y; - prev_x = prev_y = 0; - for(i=0; i. -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SCATTER__ -#define __FDMDV2_PLOT_SCATTER__ - -#include "comp.h" -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -#define PLOT_SCATTER_MODE_SCATTER 0 -#define PLOT_SCATTER_MODE_EYE 1 -#define PLOT_SCATTER_EYE_MAX_SAMPLES_ROW 80 - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotScatter -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotScatter : public PlotPanel -{ - public: - PlotScatter(wxFrame* parent); - ~PlotScatter(){}; - void add_new_samples_scatter(COMP samples[]); - void add_new_samples_eye(float samples[], int n); - void setNc(int Nc); - void setEyeScatter(int eye_mode) {mode = eye_mode;} - - protected: - int mode; - COMP m_mem[SCATTER_MEM_SYMS_MAX]; - COMP m_new_samples[MODEM_STATS_NC_MAX+1]; - float eye_mem[SCATTER_EYE_MEM_ROWS][PLOT_SCATTER_EYE_MAX_SAMPLES_ROW]; - - void draw(wxAutoBufferedPaintDC& dc); - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - - DECLARE_EVENT_TABLE() - - private: - int Nsym; - int Ncol; - int scatterMemSyms; - float m_filter_max_xy, m_filter_max_y; -}; - -#endif //__FDMDV2_PLOT_SCATTER__ diff --git a/freedv/branches/1.2/src/fdmdv2_plot_spectrum.cpp b/freedv/branches/1.2/src/fdmdv2_plot_spectrum.cpp deleted file mode 100644 index 1f5be59b..00000000 --- a/freedv/branches/1.2/src/fdmdv2_plot_spectrum.cpp +++ /dev/null @@ -1,267 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.cpp -// Purpose: Implements a waterfall plot derivative of fdmdv2_plot. -// Created: June 23, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" - -#include "fdmdv2_main.h" - -extern float g_avmag[]; // average mag data passed to draw() -void fdmdv2_clickTune(float frequency); // callback to pass new click freq - -BEGIN_EVENT_TABLE(PlotSpectrum, PlotPanel) - EVT_MOTION (PlotSpectrum::OnMouseMove) - EVT_LEFT_DOWN (PlotSpectrum::OnMouseLeftDown) - EVT_LEFT_DCLICK (PlotSpectrum::OnMouseLeftDoubleClick) - EVT_LEFT_UP (PlotSpectrum::OnMouseLeftUp) - EVT_MOUSEWHEEL (PlotSpectrum::OnMouseWheelMoved) - EVT_PAINT (PlotSpectrum::OnPaint) - EVT_SHOW (PlotSpectrum::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotSpectrum -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotSpectrum::PlotSpectrum(wxFrame* parent, float *magdB, int n_magdB, - float min_mag_db, float max_mag_db, bool clickTune): PlotPanel(parent) -{ - m_greyscale = 0; - m_Bufsz = GetMaxClientSize(); - m_newdata = false; - m_firstPass = true; - m_line_color = 0; - SetLabelSize(10.0); - - m_magdB = magdB; - m_n_magdB = n_magdB; // number of points in magdB that covers 0 ... MAX_F_HZ of spectrum - m_max_mag_db = max_mag_db; - m_min_mag_db = min_mag_db; - m_rxFreq = 0.0; - m_clickTune = clickTune; -} - -//---------------------------------------------------------------- -// ~PlotSpectrum() -//---------------------------------------------------------------- -PlotSpectrum::~PlotSpectrum() -{ -} - -//---------------------------------------------------------------- -// OnSize() -//---------------------------------------------------------------- -void PlotSpectrum::OnSize(wxSizeEvent& event) { -} - -//---------------------------------------------------------------- -// OnPaint() -//---------------------------------------------------------------- -void PlotSpectrum::OnPaint(wxPaintEvent& event) -{ - wxAutoBufferedPaintDC dc(this); - draw(dc); -} - -//---------------------------------------------------------------- -// OnShow() -//---------------------------------------------------------------- -void PlotSpectrum::OnShow(wxShowEvent& event) -{ -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotSpectrum::draw(wxAutoBufferedPaintDC& dc) -{ - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. We need to work this out every time we draw - // as OnSize() may not be called before OnPaint(), for example when a new tab - // is selected - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - dc.Clear(); - - // black background - - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - // draw spectrum - - int x, y, prev_x, prev_y, index; - float index_to_px, mag_dB_to_py, mag; - - m_newdata = false; - - wxPen pen; - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - index_to_px = (float)m_rGrid.GetWidth()/m_n_magdB; - mag_dB_to_py = (float)m_rGrid.GetHeight()/(m_max_mag_db - m_min_mag_db); - - prev_x = PLOT_BORDER + XLEFT_OFFSET; - prev_y = PLOT_BORDER; - for(index = 0; index < m_n_magdB; index++) - { - x = index*index_to_px; - mag = m_magdB[index]; - if (mag > m_max_mag_db) mag = m_max_mag_db; - if (mag < m_min_mag_db) mag = m_min_mag_db; - y = -(mag - m_max_mag_db) * mag_dB_to_py; - - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - - if (index) - dc.DrawLine(x, y, prev_x, prev_y); - prev_x = x; prev_y = y; - } - - // and finally draw Graticule - - drawGraticule(dc); - -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotSpectrum::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float f, mag, freq_hz_to_px, mag_dB_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - mag_dB_to_py = (float)m_rGrid.GetHeight()/(m_max_mag_db - m_min_mag_db); - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Check if small screen size means text will overlap - - int textXStep = STEP_F_HZ*freq_hz_to_px; - int textYStep = STEP_MAG_DB*mag_dB_to_py; - sprintf(buf, "%4.0fHz", (float)MAX_F_HZ - STEP_F_HZ); - GetTextExtent(buf, &text_w, &text_h); - int overlappedText = (text_w > textXStep) || (text_h > textYStep); - //printf("text_w: %d textXStep: %d text_h: %d textYStep: %d overlappedText: %d\n", text_w, textXStep, - // text_h, textYStep, overlappedText); - - // Vertical gridlines - - for(f=STEP_F_HZ; f= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0) && m_clickTune) { - float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - float clickFreq = (float)pt.x/freq_hz_to_px; - - // see PlotWaterfall::OnMouseDown() - - fdmdv2_clickTune(clickFreq); - } -} diff --git a/freedv/branches/1.2/src/fdmdv2_plot_spectrum.h b/freedv/branches/1.2/src/fdmdv2_plot_spectrum.h deleted file mode 100644 index 271eeb98..00000000 --- a/freedv/branches/1.2/src/fdmdv2_plot_spectrum.h +++ /dev/null @@ -1,58 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_spectrum.h -// Purpose: Defines a spectrum plot derived from fdmdv2_plot class. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SPECTRUM__ -#define __FDMDV2_PLOT_SPECTRUM__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class Waterfall -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotSpectrum : public PlotPanel -{ - public: - PlotSpectrum(wxFrame* parent, float *magdB, int n_magdB, - float min_mag_db=MIN_MAG_DB, float max_mag_db=MAX_MAG_DB, bool clickTune=true); - ~PlotSpectrum(); - void setRxFreq(float rxFreq) { m_rxFreq = rxFreq; } - void setFreqScale(int n_magdB) { m_n_magdB = n_magdB; } - - protected: - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void draw(wxAutoBufferedPaintDC& dc); - void OnMouseLeftDoubleClick(wxMouseEvent& event); - - private: - float m_rxFreq; - float m_max_mag_db; - float m_min_mag_db; - float *m_magdB; - int m_n_magdB; - bool m_clickTune; - - DECLARE_EVENT_TABLE() -}; - -#endif //__FDMDV2_PLOT_SPECTRUM__ diff --git a/freedv/branches/1.2/src/fdmdv2_plot_waterfall.cpp b/freedv/branches/1.2/src/fdmdv2_plot_waterfall.cpp deleted file mode 100644 index cdbe01e0..00000000 --- a/freedv/branches/1.2/src/fdmdv2_plot_waterfall.cpp +++ /dev/null @@ -1,483 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.cpp -// Purpose: Implements a waterfall plot derivative of fdmdv2_plot. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_main.h" - -extern float g_avmag[]; // av mag spec passed in to draw() -void fdmdv2_clickTune(float frequency); // callback to pass new click freq - -BEGIN_EVENT_TABLE(PlotWaterfall, PlotPanel) - EVT_PAINT (PlotWaterfall::OnPaint) - EVT_MOTION (PlotWaterfall::OnMouseMove) - EVT_LEFT_DCLICK (PlotWaterfall::OnMouseLeftDoubleClick) - EVT_RIGHT_DOWN (PlotWaterfall::OnMouseRightDown) - EVT_LEFT_UP (PlotWaterfall::OnMouseLeftUp) - EVT_MOUSEWHEEL (PlotWaterfall::OnMouseWheelMoved) - EVT_SIZE (PlotWaterfall::OnSize) - EVT_SHOW (PlotWaterfall::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class WaterfallPlot -// -// @class WaterfallPlot -// @author David Witten -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotWaterfall::PlotWaterfall(wxFrame* parent, bool graticule, int colour): PlotPanel(parent) -{ - - for(int i = 0; i < 255; i++) - { - m_heatmap_lut[i] = heatmap((float)i, 0.0, 255.0); - } - m_graticule = graticule; - m_colour = colour; - m_Bufsz = GetMaxClientSize(); - m_newdata = false; - m_firstPass = true; - m_line_color = 0; - m_modem_stats_max_f_hz = MODEM_STATS_MAX_F_HZ; - - SetLabelSize(10.0); - - m_pBmp = NULL; - m_max_mag = MAX_MAG_DB; - m_min_mag = MIN_MAG_DB; -} - -// When the window size gets set we can work outthe size of the window -// we plot in and allocate a bit map of the correct size -void PlotWaterfall::OnSize(wxSizeEvent& event) -{ - // resize bit map - - delete m_pBmp; - - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - // we want a bit map the size of m_rGrid - - m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24); - - m_dT = DT; -} - -//---------------------------------------------------------------- -// paintEvent() -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -// Called by the system of by wxWidgets when the panel needs -// to be redrawn. You can also trigger this call by calling -// Refresh()/Update(). -//---------------------------------------------------------------- -void PlotWaterfall::OnPaint(wxPaintEvent & evt) -{ - wxAutoBufferedPaintDC dc(this); - draw(dc); -} - -//---------------------------------------------------------------- -// OnShow() -//---------------------------------------------------------------- -void PlotWaterfall::OnShow(wxShowEvent& event) -{ -} - -//---------------------------------------------------------------- -// ~PlotWaterfall() -//---------------------------------------------------------------- -PlotWaterfall::~PlotWaterfall() -{ -} - -//---------------------------------------------------------------- -// heatmap() -// map val to a rgb colour -// from http://eddiema.ca/2011/01/21/c-sharp-heatmaps/ -//---------------------------------------------------------------- -unsigned PlotWaterfall::heatmap(float val, float min, float max) -{ - unsigned r = 0; - unsigned g = 0; - unsigned b = 0; - - val = (val - min) / (max - min); - if(val <= 0.2) - { - b = (unsigned)((val / 0.2) * 255); - } - else if(val > 0.2 && val <= 0.7) - { - b = (unsigned)((1.0 - ((val - 0.2) / 0.5)) * 255); - } - if(val >= 0.2 && val <= 0.6) - { - g = (unsigned)(((val - 0.2) / 0.4) * 255); - } - else if(val > 0.6 && val <= 0.9) - { - g = (unsigned)((1.0 - ((val - 0.6) / 0.3)) * 255); - } - if(val >= 0.5) - { - r = (unsigned)(((val - 0.5) / 0.5) * 255); - } - //printf("%f %x %x %x\n", val, r, g, b); - return (b << 16) + (g << 8) + r; -} - -bool PlotWaterfall::checkDT(void) -{ - // Check dY is > 1 pixel before proceeding. For small screens - // and large WATERFALL_SECS_Y we might have less than one - // block per pixel. In this case increase m_dT and perform draw - // less often - - float px_per_sec = (float)m_rGrid.GetHeight() / WATERFALL_SECS_Y; - float dy = m_dT * px_per_sec; - - if (dy < 1.0) { - m_dT += DT; - return false; - } - else - return true; -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotWaterfall::draw(wxAutoBufferedPaintDC& dc) -{ - - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - if (m_pBmp == NULL) - { - // we want a bit map the size of m_rGrid - m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24); - } - - dc.Clear(); - - if(m_newdata) - { - m_newdata = false; - plotPixelData(); - dc.DrawBitmap(*m_pBmp, PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER); - m_dT = DT; - } - else - { - - // no data to plot so just erase to black. Blue looks nicer - // but is same colour as low amplitude signal - - // Bug on Linux: When Stop is pressed this code doesn't erase - // the lower 25% of the Waterfall Window - - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - } - drawGraticule(dc); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotWaterfall::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float f, time, freq_hz_to_px, time_s_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - time_s_to_py = (float)m_rGrid.GetHeight()/WATERFALL_SECS_Y; - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Check if small screen size means text will overlap - - int textXStep = STEP_F_HZ*freq_hz_to_px; - int textYStep = WATERFALL_SECS_STEP*time_s_to_py; - sprintf(buf, "%4.0fHz", (float)MAX_F_HZ - STEP_F_HZ); - GetTextExtent(buf, &text_w, &text_h); - int overlappedText = (text_w > textXStep) || (text_h > textYStep); - - // Major Vertical gridlines and legend - //dc.SetPen(m_penShortDash); - for(f=STEP_F_HZ; f max_mag) - { - max_mag = g_avmag[i]; - } - } - - m_max_mag = BETA*m_max_mag + (1 - BETA)*max_mag; - m_min_mag = max_mag - 20.0; - //printf("max_mag: %f m_max_mag: %f\n", max_mag, m_max_mag); - //intensity_per_dB = (float)256 /(MAX_MAG_DB - MIN_MAG_DB); - intensity_per_dB = (float)256 /(m_max_mag - m_min_mag); - spec_index_per_px = ((float)(MAX_F_HZ)/(float)m_modem_stats_max_f_hz)*(float)MODEM_STATS_NSPEC / (float) m_rGrid.GetWidth(); - - /* - printf("h %d w %d px_per_sec %d dy %d dy_blocks %d spec_index_per_px: %f\n", - m_rGrid.GetHeight(), m_rGrid.GetWidth(), px_per_sec, - dy, dy_blocks, spec_index_per_px); - */ - - // Shift previous bit map up one row of blocks ---------------------------- - wxNativePixelData data(*m_pBmp); - wxNativePixelData::Iterator bitMapStart(data); - wxNativePixelData::Iterator p = bitMapStart; - - for(b = 0; b < dy_blocks - 1; b++) - { - wxNativePixelData::Iterator psrc = bitMapStart; - wxNativePixelData::Iterator pdest = bitMapStart; - pdest.OffsetY(data, dy * b); - psrc.OffsetY(data, dy * (b+1)); - - // copy one line of blocks - - for(py = 0; py < dy; py++) - { - wxNativePixelData::Iterator pdestRowStart = pdest; - wxNativePixelData::Iterator psrcRowStart = psrc; - - for(px = 0; px < m_rGrid.GetWidth(); px++) - { - pdest.Red() = psrc.Red(); - pdest.Green() = psrc.Green(); - pdest.Blue() = psrc.Blue(); - pdest++; - psrc++; - } - pdest = pdestRowStart; - pdest.OffsetY(data, 1); - psrc = psrcRowStart; - psrc.OffsetY(data, 1); - } - } - - // Draw last line of blocks using latest amplitude data ------------------ - p = bitMapStart; - p.OffsetY(data, dy *(dy_blocks - 1)); - for(py = 0; py < dy; py++) - { - wxNativePixelData::Iterator rowStart = p; - - for(px = 0; px < m_rGrid.GetWidth(); px++) - { - index = px * spec_index_per_px; - assert(index < MODEM_STATS_NSPEC); - - intensity = intensity_per_dB * (g_avmag[index] - m_min_mag); - if(intensity > 255) intensity = 255; - if (intensity < 0) intensity = 0; - //printf("%d %f %d \n", index, g_avmag[index], intensity); - - switch (m_colour) { - case 0: - p.Red() = m_heatmap_lut[intensity] & 0xff; - p.Green() = (m_heatmap_lut[intensity] >> 8) & 0xff; - p.Blue() = (m_heatmap_lut[intensity] >> 16) & 0xff; - break; - case 1: - p.Red() = intensity; - p.Green() = intensity; - p.Blue() = intensity; - break; - case 2: - p.Red() = intensity; - p.Green() = intensity; - if (intensity < 127) - p.Blue() = intensity*2; - else - p.Blue() = 255; - - break; - } - ++p; - } - p = rowStart; - p.OffsetY(data, 1); - } - -} - -//------------------------------------------------------------------------- -// OnMouseLeftDown() -//------------------------------------------------------------------------- -void PlotWaterfall::OnMouseLeftDoubleClick(wxMouseEvent& event) -{ - m_mouseDown = true; - wxClientDC dc(this); - - wxPoint pt(event.GetLogicalPosition(dc)); - - // map x coord to edges of actual plot - pt.x -= PLOT_BORDER + XLEFT_OFFSET; - pt.y -= PLOT_BORDER; - - // valid click if inside of plot - if ((pt.x >= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0)) - { - float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - float clickFreq = (float)pt.x/freq_hz_to_px; - - // communicate back to other threads - fdmdv2_clickTune(clickFreq); - } -} - -//------------------------------------------------------------------------- -// OnMouseRightDown() -//------------------------------------------------------------------------- -void PlotWaterfall::OnMouseRightDown(wxMouseEvent& event) -{ - m_colour++; - if (m_colour == 3) - m_colour = 0; -} - diff --git a/freedv/branches/1.2/src/fdmdv2_plot_waterfall.h b/freedv/branches/1.2/src/fdmdv2_plot_waterfall.h deleted file mode 100644 index f4896c6b..00000000 --- a/freedv/branches/1.2/src/fdmdv2_plot_waterfall.h +++ /dev/null @@ -1,73 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.h -// Purpose: Defines a waterfall plot derivative of fdmdv2_plot. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_PLOT_WATERFALL__ -#define __FDMDV2_PLOT_WATERFALL__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotWaterfall -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotWaterfall : public PlotPanel -{ - public: - PlotWaterfall(wxFrame* parent, bool graticule, int colour); - ~PlotWaterfall(); - bool checkDT(void); - void setGreyscale(bool greyscale) { m_greyscale = greyscale; } - void setRxFreq(float rxFreq) { m_rxFreq = rxFreq; } - void setFs(int fs) { m_modem_stats_max_f_hz = fs/2; } - - protected: - unsigned m_heatmap_lut[256]; - - unsigned heatmap(float val, float min, float max); - - void OnPaint(wxPaintEvent & evt); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void draw(wxAutoBufferedPaintDC& dc); - void plotPixelData(); - void OnMouseLeftDoubleClick(wxMouseEvent& event); - void OnMouseRightDown(wxMouseEvent& event); - - private: - float m_dT; - float m_rxFreq; - bool m_graticule; - float m_min_mag; - float m_max_mag; - int m_colour; - int m_modem_stats_max_f_hz; - - DECLARE_EVENT_TABLE() -}; - -#endif //__FDMDV2_PLOT_WATERFALL__ diff --git a/freedv/branches/1.2/src/freedv.icns b/freedv/branches/1.2/src/freedv.icns deleted file mode 100644 index 5190e7951ffd9152576d6569f0c7ca64927649ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92136 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

-_`&2*Z5cR;kk!g) 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 bcU%5HHl?3v;lGWz00000NkvXXu0mjfpGTzJ diff --git a/freedv/branches/1.2/src/hamlib.cpp b/freedv/branches/1.2/src/hamlib.cpp deleted file mode 100644 index ff80b24a..00000000 --- a/freedv/branches/1.2/src/hamlib.cpp +++ /dev/null @@ -1,160 +0,0 @@ -//========================================================================== -// Name: hamlib.cpp -// -// Purpose: Hamlib integration for FreeDV -// Created: May 2013 -// Authors: Joel Stanley -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include - -#include -#include - -using namespace std; - -typedef std::vector riglist_t; - -static bool rig_cmp(const struct rig_caps *rig1, const struct rig_caps *rig2); -static int build_list(const struct rig_caps *rig, rig_ptr_t); - -Hamlib::Hamlib() : m_rig(NULL) { - /* Stop hamlib from spewing info to stderr. */ - rig_set_debug(RIG_DEBUG_NONE); - - /* Create sorted list of rigs. */ - rig_load_all_backends(); - rig_list_foreach(build_list, &m_rigList); - sort(m_rigList.begin(), m_rigList.end(), rig_cmp); - - /* Reset debug output. */ - rig_set_debug(RIG_DEBUG_VERBOSE); - - m_rig = NULL; -} - -Hamlib::~Hamlib() { - if(m_rig) - close(); -} - -static int build_list(const struct rig_caps *rig, rig_ptr_t rigList) { - ((riglist_t *)rigList)->push_back(rig); - return 1; -} - -static bool rig_cmp(const struct rig_caps *rig1, const struct rig_caps *rig2) { - /* Compare manufacturer. */ - int r = strcasecmp(rig1->mfg_name, rig2->mfg_name); - if (r != 0) - return r < 0; - - /* Compare model. */ - r = strcasecmp(rig1->model_name, rig2->model_name); - if (r != 0) - return r < 0; - - /* Compare rig ID. */ - return rig1->rig_model < rig2->rig_model; -} - -void Hamlib::populateComboBox(wxComboBox *cb) { - - riglist_t::const_iterator rig = m_rigList.begin(); - for (; rig !=m_rigList.end(); rig++) { - char name[128]; - snprintf(name, 128, "%s %s", (*rig)->mfg_name, (*rig)->model_name); - cb->Append(name); - } -} - -bool Hamlib::connect(unsigned int rig_index, const char *serial_port, const int serial_rate) { - /* Look up model from index. */ - if (rig_index >= m_rigList.size()) { - return false; - } - fprintf(stderr, "rig: %s %s (%d)\n", m_rigList[rig_index]->mfg_name, - m_rigList[rig_index]->model_name, m_rigList[rig_index]->rig_model); - - if(m_rig) { - printf("Closing old hamlib instance!\n"); - close(); - } - - /* Initialise, configure and open. */ - - m_rig = rig_init(m_rigList[rig_index]->rig_model); - - if (!m_rig) - return false; - - /* TODO we may also need civaddr for Icom */ - - strncpy(m_rig->state.rigport.pathname, serial_port, FILPATHLEN - 1); - if (serial_rate) { - m_rig->state.rigport.parm.serial.rate = serial_rate; - } - fprintf(stderr, "hamlib: setting serial rate: %d\n", m_rig->state.rigport.parm.serial.rate); - - if (rig_open(m_rig) == RIG_OK) { - return true; - } - - return false; -} - -int Hamlib::get_serial_rate(void) { - return m_rig->state.rigport.parm.serial.rate; -} - -int Hamlib::get_data_bits(void) { - return m_rig->state.rigport.parm.serial.data_bits; -} - -int Hamlib::get_stop_bits(void) { - return m_rig->state.rigport.parm.serial.stop_bits; -} - -bool Hamlib::ptt(bool press, wxString &hamlibError) { - fprintf(stderr,"Hamlib::ptt: %d\n", press); - hamlibError = ""; - - if(!m_rig) - return false; - - /* TODO(Joel): make ON_DATA and ON configurable. */ - - ptt_t on = press ? RIG_PTT_ON : RIG_PTT_OFF; - - /* TODO(Joel): what should the VFO option be? */ - - int retcode = rig_set_ptt(m_rig, RIG_VFO_CURR, on); - fprintf(stderr,"Hamlib::ptt: rig_set_ptt returned: %d\n", retcode); - if (retcode != RIG_OK ) { - fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(retcode)); - hamlibError = rigerror(retcode); - } - - return retcode == RIG_OK; -} - -void Hamlib::close(void) { - if(m_rig) { - rig_close(m_rig); - rig_cleanup(m_rig); - m_rig = NULL; - } -} diff --git a/freedv/branches/1.2/src/hamlib.h b/freedv/branches/1.2/src/hamlib.h deleted file mode 100644 index 150ed86a..00000000 --- a/freedv/branches/1.2/src/hamlib.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef HAMLIB_H -#define HAMLIB_H - -extern "C" { -#include -} -#include -#include - -class Hamlib { - - public: - Hamlib(); - ~Hamlib(); - void populateComboBox(wxComboBox *cb); - bool connect(unsigned int rig_index, const char *serial_port); - bool ptt(bool press, wxString &hamlibError); - void close(void); - int get_serial_rate(void); - int get_data_bits(void); - int get_stop_bits(void); - - typedef std::vector riglist_t; - - private: - RIG *m_rig; - /* Sorted list of rigs. */ - riglist_t m_rigList; -}; - -#endif /*HAMLIB_H*/ diff --git a/freedv/branches/1.2/src/info.plist b/freedv/branches/1.2/src/info.plist deleted file mode 100644 index 8f0d4c34..00000000 --- a/freedv/branches/1.2/src/info.plist +++ /dev/null @@ -1,104 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - CFBundleIconFile - freedv - NSPrincipalClass - NSApplication - - - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - NSPrincipalClass - NSApplication - - - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - NSPrincipalClass - NSApplication - - \ No newline at end of file diff --git a/freedv/branches/1.2/src/serialport.cpp b/freedv/branches/1.2/src/serialport.cpp deleted file mode 100644 index 59dd0c93..00000000 --- a/freedv/branches/1.2/src/serialport.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include -#include -#include -#include "serialport.h" - -Serialport::Serialport() { - com_handle = COM_HANDLE_INVALID; -} - -Serialport::~Serialport() { - if (isopen()) { - closeport(); - } -} - -// returns true if comm port opened OK, false if there was a problem - -bool Serialport::openport(const char name[], bool useRTS, bool RTSPos, bool useDTR, bool DTRPos) -{ - if (com_handle != COM_HANDLE_INVALID) { - closeport(); - } - - m_useRTS = useRTS; - m_RTSPos = RTSPos; - m_useDTR = useDTR; - m_DTRPos = DTRPos; - -#ifdef _WIN32 - { - COMMCONFIG CC; - DWORD CCsize=sizeof(CC); - COMMTIMEOUTS timeouts; - DCB dcb; - - if(GetDefaultCommConfigA(name, &CC, &CCsize)) { - CC.dcb.fOutxCtsFlow = FALSE; - CC.dcb.fOutxDsrFlow = FALSE; - CC.dcb.fDtrControl = DTR_CONTROL_DISABLE; - CC.dcb.fDsrSensitivity = FALSE; - CC.dcb.fRtsControl = RTS_CONTROL_DISABLE; - SetDefaultCommConfigA(name, &CC, CCsize); - } - - if((com_handle=CreateFileA(name - ,GENERIC_READ|GENERIC_WRITE /* Access */ - ,0 /* Share mode */ - ,NULL /* Security attributes */ - ,OPEN_EXISTING /* Create access */ - ,FILE_ATTRIBUTE_NORMAL /* File attributes */ - ,NULL /* Template */ - ))==INVALID_HANDLE_VALUE) - return false; - - if(GetCommTimeouts(com_handle, &timeouts)) { - timeouts.ReadIntervalTimeout=MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier=0; - timeouts.ReadTotalTimeoutConstant=0; // No-wait read timeout - timeouts.WriteTotalTimeoutMultiplier=0; - timeouts.WriteTotalTimeoutConstant=5000; // 5 seconds - SetCommTimeouts(com_handle,&timeouts); - } - - /* Force N-8-1 mode: */ - if(GetCommState(com_handle, &dcb)==TRUE) { - dcb.ByteSize = 8; - dcb.Parity = NOPARITY; - dcb.StopBits = ONESTOPBIT; - dcb.DCBlength = sizeof(DCB); - dcb.fBinary = TRUE; - dcb.fOutxCtsFlow = FALSE; - dcb.fOutxDsrFlow = FALSE; - dcb.fDtrControl = DTR_CONTROL_DISABLE; - dcb.fDsrSensitivity = FALSE; - dcb.fTXContinueOnXoff= TRUE; - dcb.fOutX = FALSE; - dcb.fInX = FALSE; - dcb.fRtsControl = RTS_CONTROL_DISABLE; - dcb.fAbortOnError = FALSE; - SetCommState(com_handle, &dcb); - } - } -#else - { - struct termios t; - - if((com_handle=open(name, O_NONBLOCK|O_RDWR))== COM_HANDLE_INVALID) - return false; - - if(tcgetattr(com_handle, &t)==-1) { - close(com_handle); - com_handle = COM_HANDLE_INVALID; - return false; - } - - t.c_iflag = ( - IGNBRK /* ignore BREAK condition */ - | IGNPAR /* ignore (discard) parity errors */ - ); - t.c_oflag = 0; /* No output processing */ - t.c_cflag = ( - CS8 /* 8 bits */ - | CREAD /* enable receiver */ - - /* - Fun snippet from the FreeBSD manpage: - - If CREAD is set, the receiver is enabled. Otherwise, no character is - received. Not all hardware supports this bit. In fact, this flag is - pretty silly and if it were not part of the termios specification it - would be omitted. - */ - | CLOCAL /* ignore modem status lines */ - ); - - t.c_lflag = 0; /* No local modes */ - if(tcsetattr(com_handle, TCSANOW, &t)==-1) { - close(com_handle); - com_handle = COM_HANDLE_INVALID; - return false; - } - - } -#endif - return true; -} - - -// fixme: this takes about one second to close under Linux - -void Serialport::closeport() -{ -#ifdef _WIN32 - CloseHandle(com_handle); -#else - close(com_handle); -#endif - com_handle = COM_HANDLE_INVALID; -} - -//---------------------------------------------------------------- -// (raise|lower)(RTS|DTR)() -// -// Raises/lowers the specified signal -//---------------------------------------------------------------- - -void Serialport::raiseDTR(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, SETDTR); -#else - { // For C89 happiness - int flags = TIOCM_DTR; - ioctl(com_handle, TIOCMBIS, &flags); - } -#endif -} - -void Serialport::raiseRTS(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, SETRTS); -#else - { // For C89 happiness - int flags = TIOCM_RTS; - ioctl(com_handle, TIOCMBIS, &flags); - } -#endif -} - -void Serialport::lowerDTR(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, CLRDTR); -#else - { // For C89 happiness - int flags = TIOCM_DTR; - ioctl(com_handle, TIOCMBIC, &flags); - } -#endif -} - -void Serialport::lowerRTS(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, CLRRTS); -#else - { // For C89 happiness - int flags = TIOCM_RTS; - ioctl(com_handle, TIOCMBIC, &flags); - } -#endif -} - -void Serialport::ptt(bool tx) { - - /* Truth table: - - g_tx RTSPos RTS - ------------------- - 0 1 0 - 1 1 1 - 0 0 1 - 1 0 0 - - exclusive NOR - */ - - if (com_handle != COM_HANDLE_INVALID) { - if (m_useRTS) { - //fprintf(stderr, "g_tx: %d m_boolRTSPos: %d serialLine: %d\n", g_tx, wxGetApp().m_boolRTSPos, g_tx == wxGetApp().m_boolRTSPos); - if (tx == m_RTSPos) - raiseRTS(); - else - lowerRTS(); - } - if (m_useDTR) { - //fprintf(stderr, "g_tx: %d m_boolDTRPos: %d serialLine: %d\n", g_tx, wxGetApp().m_boolDTRPos, g_tx == wxGetApp().m_boolDTRPos); - if (tx == m_DTRPos) - raiseDTR(); - else - lowerDTR(); - } - - } -} diff --git a/freedv/branches/1.2/src/serialport.h b/freedv/branches/1.2/src/serialport.h deleted file mode 100644 index e5db10b4..00000000 --- a/freedv/branches/1.2/src/serialport.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef SERIALPORT_H -#define SERIALPORT_H - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -// Serial ports called com port for historic reasons, especially on Windows machines - -#ifdef _WIN32 -#define COM_HANDLE_INVALID INVALID_HANDLE_VALUE -typedef HANDLE com_handle_t; -#else -#define COM_HANDLE_INVALID -1 -typedef int com_handle_t; -#endif - -class Serialport { - - public: - Serialport(); - ~Serialport(); - bool openport(const char port[], bool useRTS, bool RTSPos, bool useDTR, bool DTRPos); - bool isopen() {return (com_handle != COM_HANDLE_INVALID);} - void closeport(); - void ptt(bool tx); - - private: - com_handle_t com_handle; - bool m_useRTS, m_RTSPos, m_useDTR, m_DTRPos; - - void raiseDTR(void); - void lowerDTR(void); - void raiseRTS(void); - void lowerRTS(void); -}; - -#endif /* SERIALPORT_H */ diff --git a/freedv/branches/1.2/src/sox/band.h b/freedv/branches/1.2/src/sox/band.h deleted file mode 100644 index 5398ff45..00000000 --- a/freedv/branches/1.2/src/sox/band.h +++ /dev/null @@ -1,47 +0,0 @@ -/* libSoX Bandpass effect file. July 5, 1991 - * Copyright 1991 Lance Norskog And Sundry Contributors - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Lance Norskog And Sundry Contributors are not responsible for - * the consequences of using this software. - * - * Algorithm: 2nd order recursive filter. - * Formula stolen from MUSIC56K, a toolkit of 56000 assembler stuff. - * Quote: - * This is a 2nd order recursive band pass filter of the form. - * y(n)= a * x(n) - b * y(n-1) - c * y(n-2) - * where : - * x(n) = "IN" - * "OUT" = y(n) - * c = EXP(-2*pi*cBW/S_RATE) - * b = -4*c/(1+c)*COS(2*pi*cCF/S_RATE) - * if cSCL=2 (i.e. noise input) - * a = SQT(((1+c)*(1+c)-b*b)*(1-c)/(1+c)) - * else - * a = SQT(1-b*b/(4*c))*(1-c) - * endif - * note : cCF is the center frequency in Hertz - * cBW is the band width in Hertz - * cSCL is a scale factor, use 1 for pitched sounds - * use 2 for noise. - * - * - * July 1, 1999 - Jan Paul Schmidt - * - * This looks like the resonator band pass in SPKit. It's a - * second order all-pole (IIR) band-pass filter described - * at the pages 186 - 189 in - * Dodge, Charles & Jerse, Thomas A. 1985: - * Computer Music -- Synthesis, Composition and Performance. - * New York: Schirmer Books. - * Reference from the SPKit manual. - */ - - p->a2 = exp(-2 * M_PI * bw_Hz / effp->in_signal.rate); - p->a1 = -4 * p->a2 / (1 + p->a2) * cos(2 * M_PI * p->fc / effp->in_signal.rate); - p->b0 = sqrt(1 - p->a1 * p->a1 / (4 * p->a2)) * (1 - p->a2); - if (p->filter_type == filter_BPF_SPK_N) { - mult = sqrt(((1+p->a2) * (1+p->a2) - p->a1*p->a1) * (1-p->a2) / (1+p->a2)) / p->b0; - p->b0 *= mult; - } diff --git a/freedv/branches/1.2/src/sox/biquad.c b/freedv/branches/1.2/src/sox/biquad.c deleted file mode 100644 index c57f1902..00000000 --- a/freedv/branches/1.2/src/sox/biquad.c +++ /dev/null @@ -1,178 +0,0 @@ -/* libSoX Biquad filter common functions (c) 2006-7 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "biquad.h" -#include - -typedef biquad_t priv_t; - -static char const * const width_str[] = { - "band-width(Hz)", - "band-width(kHz)", - "band-width(Hz, no warp)", /* deprecated */ - "band-width(octaves)", - "Q", - "slope", -}; -static char const all_width_types[] = "hkboqs"; - - -int lsx_biquad_getopts(sox_effect_t * effp, int argc, char **argv, - int min_args, int max_args, int fc_pos, int width_pos, int gain_pos, - char const * allowed_width_types, filter_t filter_type) -{ - priv_t * p = (priv_t *)effp->priv; - char width_type = *allowed_width_types; - char dummy, * dummy_p; /* To check for extraneous chars. */ - --argc, ++argv; - - p->filter_type = filter_type; - if (argc < min_args || argc > max_args || - (argc > fc_pos && ((p->fc = lsx_parse_frequency(argv[fc_pos], &dummy_p)) <= 0 || *dummy_p)) || - (argc > width_pos && ((unsigned)(sscanf(argv[width_pos], "%lf%c %c", &p->width, &width_type, &dummy)-1) > 1 || p->width <= 0)) || - (argc > gain_pos && sscanf(argv[gain_pos], "%lf %c", &p->gain, &dummy) != 1) || - !strchr(allowed_width_types, width_type) || (width_type == 's' && p->width > 1)) - return lsx_usage(effp); - p->width_type = strchr(all_width_types, width_type) - all_width_types; - if ((size_t)p->width_type >= strlen(all_width_types)) - p->width_type = 0; - if (p->width_type == width_bw_kHz) { - p->width *= 1000; - p->width_type = width_bw_Hz; - } - return SOX_SUCCESS; -} - - -static int start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - /* Simplify: */ - p->b2 /= p->a0; - p->b1 /= p->a0; - p->b0 /= p->a0; - p->a2 /= p->a0; - p->a1 /= p->a0; - - p->o2 = p->o1 = p->i2 = p->i1 = 0; - return SOX_SUCCESS; -} - - -int lsx_biquad_start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - - start(effp); - - if (effp->global_info->plot == sox_plot_octave) { - printf( - "%% GNU Octave file (may also work with MATLAB(R) )\n" - "Fs=%g;minF=10;maxF=Fs/2;\n" - "sweepF=logspace(log10(minF),log10(maxF),200);\n" - "[h,w]=freqz([%.15e %.15e %.15e],[1 %.15e %.15e],sweepF,Fs);\n" - "semilogx(w,20*log10(h))\n" - "title('SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)')\n" - "xlabel('Frequency (Hz)')\n" - "ylabel('Amplitude Response (dB)')\n" - "axis([minF maxF -35 25])\n" - "grid on\n" - "disp('Hit return to continue')\n" - "pause\n" - , effp->in_signal.rate, p->b0, p->b1, p->b2, p->a1, p->a2 - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate); - return SOX_EOF; - } - if (effp->global_info->plot == sox_plot_gnuplot) { - printf( - "# gnuplot file\n" - "set title 'SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)'\n" - "set xlabel 'Frequency (Hz)'\n" - "set ylabel 'Amplitude Response (dB)'\n" - "Fs=%g\n" - "b0=%.15e; b1=%.15e; b2=%.15e; a1=%.15e; a2=%.15e\n" - "o=2*pi/Fs\n" - "H(f)=sqrt((b0*b0+b1*b1+b2*b2+2.*(b0*b1+b1*b2)*cos(f*o)+2.*(b0*b2)*cos(2.*f*o))/(1.+a1*a1+a2*a2+2.*(a1+a1*a2)*cos(f*o)+2.*a2*cos(2.*f*o)))\n" - "set logscale x\n" - "set samples 250\n" - "set grid xtics ytics\n" - "set key off\n" - "plot [f=10:Fs/2] [-35:25] 20*log10(H(f))\n" - "pause -1 'Hit return to continue'\n" - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate, effp->in_signal.rate - , p->b0, p->b1, p->b2, p->a1, p->a2); - return SOX_EOF; - } - if (effp->global_info->plot == sox_plot_data) { - printf("# SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)\n" - "# IIR filter\n" - "# rate: %g\n" - "# name: b\n" - "# type: matrix\n" - "# rows: 3\n" - "# columns: 1\n" - "%24.16e\n%24.16e\n%24.16e\n" - "# name: a\n" - "# type: matrix\n" - "# rows: 3\n" - "# columns: 1\n" - "%24.16e\n%24.16e\n%24.16e\n" - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate, effp->in_signal.rate - , p->b0, p->b1, p->b2, 1. /* a0 */, p->a1, p->a2); - return SOX_EOF; - } - return SOX_SUCCESS; -} - - -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, - sox_sample_t *obuf, size_t *isamp, size_t *osamp) -{ - priv_t * p = (priv_t *)effp->priv; - size_t len = *isamp = *osamp = min(*isamp, *osamp); - while (len--) { - double o0 = *ibuf*p->b0 + p->i1*p->b1 + p->i2*p->b2 - p->o1*p->a1 - p->o2*p->a2; - p->i2 = p->i1, p->i1 = *ibuf++; - p->o2 = p->o1, p->o1 = o0; - *obuf++ = SOX_ROUND_CLIP_COUNT(o0, effp->clips); - } - return SOX_SUCCESS; -} - -static int create(sox_effect_t * effp, int argc, char * * argv) -{ - priv_t * p = (priv_t *)effp->priv; - double * d = &p->b0; - char c; - - --argc, ++argv; - if (argc == 6) - for (; argc && sscanf(*argv, "%lf%c", d, &c) == 1; --argc, ++argv, ++d); - return argc? lsx_usage(effp) : SOX_SUCCESS; -} - -sox_effect_handler_t const * lsx_biquad_effect_fn(void) -{ - static sox_effect_handler_t handler = { - "biquad", "b0 b1 b2 a0 a1 a2", 0, - create, lsx_biquad_start, lsx_biquad_flow, NULL, NULL, NULL, sizeof(priv_t) - }; - return &handler; -} diff --git a/freedv/branches/1.2/src/sox/biquad.h b/freedv/branches/1.2/src/sox/biquad.h deleted file mode 100644 index 8786ac83..00000000 --- a/freedv/branches/1.2/src/sox/biquad.h +++ /dev/null @@ -1,78 +0,0 @@ -/* libSoX Biquad filter common definitions (c) 2006-7 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef biquad_included -#define biquad_included - -#define LSX_EFF_ALIAS -#include "sox_i.h" - -typedef enum { - filter_LPF, - filter_HPF, - filter_BPF_CSG, - filter_BPF, - filter_notch, - filter_APF, - filter_peakingEQ, - filter_lowShelf, - filter_highShelf, - filter_LPF_1, - filter_HPF_1, - filter_BPF_SPK, - filter_BPF_SPK_N, - filter_AP1, - filter_AP2, - filter_deemph, - filter_riaa -} filter_t; - -typedef enum { - width_bw_Hz, - width_bw_kHz, - /* The old, non-RBJ, non-freq-warped band-pass/reject response; - * leaving here for now just in case anybody misses it: */ - width_bw_old, - width_bw_oct, - width_Q, - width_slope -} width_t; - -/* Private data for the biquad filter effects */ -typedef struct { - double gain; /* For EQ filters */ - double fc; /* Centre/corner/cutoff frequency */ - double width; /* Filter width; interpreted as per width_type */ - width_t width_type; - - filter_t filter_type; - - double b0, b1, b2; /* Filter coefficients */ - double a0, a1, a2; /* Filter coefficients */ - - sox_sample_t i1, i2; /* Filter memory */ - double o1, o2; /* Filter memory */ -} biquad_t; - -int lsx_biquad_getopts(sox_effect_t * effp, int n, char **argv, - int min_args, int max_args, int fc_pos, int width_pos, int gain_pos, - char const * allowed_width_types, filter_t filter_type); -int lsx_biquad_start(sox_effect_t * effp); -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf, - size_t *isamp, size_t *osamp); - -#endif diff --git a/freedv/branches/1.2/src/sox/biquads.c b/freedv/branches/1.2/src/sox/biquads.c deleted file mode 100644 index 19793a6d..00000000 --- a/freedv/branches/1.2/src/sox/biquads.c +++ /dev/null @@ -1,400 +0,0 @@ -/* libSoX Biquad filter effects (c) 2006-8 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * - * 2-pole filters designed by Robert Bristow-Johnson - * see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt - * - * 1-pole filters based on code (c) 2000 Chris Bagwell - * Algorithms: Recursive single pole low/high pass filter - * Reference: The Scientist and Engineer's Guide to Digital Signal Processing - * - * low-pass: output[N] = input[N] * A + output[N-1] * B - * X = exp(-2.0 * pi * Fc) - * A = 1 - X - * B = X - * Fc = cutoff freq / sample rate - * - * Mimics an RC low-pass filter: - * - * ---/\/\/\/\-----------> - * | - * --- C - * --- - * | - * | - * V - * - * high-pass: output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1] - * X = exp(-2.0 * pi * Fc) - * A0 = (1 + X) / 2 - * A1 = -(1 + X) / 2 - * B1 = X - * Fc = cutoff freq / sample rate - * - * Mimics an RC high-pass filter: - * - * || C - * ----||---------> - * || | - * < - * > R - * < - * | - * V - */ - - -#include "biquad.h" -#include -#include - -typedef biquad_t priv_t; - - -static int hilo1_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 1, 1, 0, 1, 2, "", - *effp->handler.name == 'l'? filter_LPF_1 : filter_HPF_1); -} - - -static int hilo2_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - if (argc > 1 && strcmp(argv[1], "-1") == 0) - return hilo1_getopts(effp, argc - 1, argv + 1); - if (argc > 1 && strcmp(argv[1], "-2") == 0) - ++argv, --argc; - p->width = sqrt(0.5); /* Default to Butterworth */ - return lsx_biquad_getopts(effp, argc, argv, 1, 2, 0, 1, 2, "qohk", - *effp->handler.name == 'l'? filter_LPF : filter_HPF); -} - - -static int bandpass_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_BPF; - if (argc > 1 && strcmp(argv[1], "-c") == 0) - ++argv, --argc, type = filter_BPF_CSG; - return lsx_biquad_getopts(effp, argc, argv, 2, 2, 0, 1, 2, "hkqob", type); -} - - -static int bandrej_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 2, 2, 0, 1, 2, "hkqob", filter_notch); -} - - -static int allpass_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_APF; - int m; - if (argc > 1 && strcmp(argv[1], "-1") == 0) - ++argv, --argc, type = filter_AP1; - else if (argc > 1 && strcmp(argv[1], "-2") == 0) - ++argv, --argc, type = filter_AP2; - m = 1 + (type == filter_APF); - return lsx_biquad_getopts(effp, argc, argv, m, m, 0, 1, 2, "hkqo", type); -} - - -static int tone_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->width = 0.5; - p->fc = *effp->handler.name == 'b'? 100 : 3000; - return lsx_biquad_getopts(effp, argc, argv, 1, 3, 1, 2, 0, "shkqo", - *effp->handler.name == 'b'? filter_lowShelf: filter_highShelf); -} - - -static int equalizer_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 3, 3, 0, 1, 2, "qohk", filter_peakingEQ); -} - - -static int band_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_BPF_SPK; - if (argc > 1 && strcmp(argv[1], "-n") == 0) - ++argv, --argc, type = filter_BPF_SPK_N; - return lsx_biquad_getopts(effp, argc, argv, 1, 2, 0, 1, 2, "hkqo", type); -} - - -static int deemph_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->fc = 5283; - p->width = 0.4845; - p->gain = -9.477; - return lsx_biquad_getopts(effp, argc, argv, 0, 0, 0, 1, 2, "s", filter_deemph); -} - - -static int riaa_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->filter_type = filter_riaa; - (void)argv; - return --argc? lsx_usage(effp) : SOX_SUCCESS; -} - - -static void make_poly_from_roots( - double const * roots, size_t num_roots, double * poly) -{ - size_t i, j; - poly[0] = 1; - poly[1] = -roots[0]; - memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly)); - for (i = 1; i < num_roots; ++i) - for (j = num_roots; j > 0; --j) - poly[j] -= poly[j - 1] * roots[i]; -} - -static int start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - double w0 = 2 * M_PI * p->fc / effp->in_signal.rate; - double A = exp(p->gain / 40 * log(10.)); - double alpha = 0, mult = dB_to_linear(max(p->gain, 0)); - - if (w0 > M_PI) { - lsx_fail("frequency must be less than half the sample-rate (Nyquist rate)"); - return SOX_EOF; - } - - /* Set defaults: */ - p->b0 = p->b1 = p->b2 = p->a1 = p->a2 = 0; - p->a0 = 1; - - if (p->width) switch (p->width_type) { - case width_slope: - alpha = sin(w0)/2 * sqrt((A + 1/A)*(1/p->width - 1) + 2); - break; - - case width_Q: - alpha = sin(w0)/(2*p->width); - break; - - case width_bw_oct: - alpha = sin(w0)*sinh(log(2.)/2 * p->width * w0/sin(w0)); - break; - - case width_bw_Hz: - alpha = sin(w0)/(2*p->fc/p->width); - break; - - case width_bw_kHz: assert(0); /* Shouldn't get here */ - - case width_bw_old: - alpha = tan(M_PI * p->width / effp->in_signal.rate); - break; - } - switch (p->filter_type) { - case filter_LPF: /* H(s) = 1 / (s^2 + s/Q + 1) */ - p->b0 = (1 - cos(w0))/2; - p->b1 = 1 - cos(w0); - p->b2 = (1 - cos(w0))/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_HPF: /* H(s) = s^2 / (s^2 + s/Q + 1) */ - p->b0 = (1 + cos(w0))/2; - p->b1 = -(1 + cos(w0)); - p->b2 = (1 + cos(w0))/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_BPF_CSG: /* H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q) */ - p->b0 = sin(w0)/2; - p->b1 = 0; - p->b2 = -sin(w0)/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_BPF: /* H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain) */ - p->b0 = alpha; - p->b1 = 0; - p->b2 = -alpha; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_notch: /* H(s) = (s^2 + 1) / (s^2 + s/Q + 1) */ - p->b0 = 1; - p->b1 = -2*cos(w0); - p->b2 = 1; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_APF: /* H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1) */ - p->b0 = 1 - alpha; - p->b1 = -2*cos(w0); - p->b2 = 1 + alpha; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_peakingEQ: /* H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1) */ - if (A == 1) - return SOX_EFF_NULL; - p->b0 = 1 + alpha*A; - p->b1 = -2*cos(w0); - p->b2 = 1 - alpha*A; - p->a0 = 1 + alpha/A; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha/A; - break; - - case filter_lowShelf: /* H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1) */ - if (A == 1) - return SOX_EFF_NULL; - p->b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha ); - p->b1 = 2*A*( (A-1) - (A+1)*cos(w0) ); - p->b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha ); - p->a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha; - p->a1 = -2*( (A-1) + (A+1)*cos(w0) ); - p->a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha; - break; - - case filter_deemph: /* See deemph.plt for documentation */ - if (effp->in_signal.rate != 44100) { - lsx_fail("Sample rate must be 44100 (audio-CD)"); - return SOX_EOF; - } - /* Falls through... */ - - case filter_highShelf: /* H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A) */ - if (!A) - return SOX_EFF_NULL; - p->b0 = A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha ); - p->b1 = -2*A*( (A-1) + (A+1)*cos(w0) ); - p->b2 = A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha ); - p->a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha; - p->a1 = 2*( (A-1) - (A+1)*cos(w0) ); - p->a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha; - break; - - case filter_LPF_1: /* single-pole */ - p->a1 = -exp(-w0); - p->b0 = 1 + p->a1; - break; - - case filter_HPF_1: /* single-pole */ - p->a1 = -exp(-w0); - p->b0 = (1 - p->a1)/2; - p->b1 = -p->b0; - break; - - case filter_BPF_SPK: case filter_BPF_SPK_N: { - double bw_Hz; - if (!p->width) - p->width = p->fc / 2; - bw_Hz = p->width_type == width_Q? p->fc / p->width : - p->width_type == width_bw_Hz? p->width : - p->fc * (pow(2., p->width) - 1) * pow(2., -0.5 * p->width); /* bw_oct */ - #include "band.h" /* Has different licence */ - break; - } - - case filter_AP1: /* Experimental 1-pole all-pass from Tom Erbe @ UCSD */ - p->b0 = exp(-w0); - p->b1 = -1; - p->a1 = -exp(-w0); - break; - - case filter_AP2: /* Experimental 2-pole all-pass from Tom Erbe @ UCSD */ - p->b0 = 1 - sin(w0); - p->b1 = -2 * cos(w0); - p->b2 = 1 + sin(w0); - p->a0 = 1 + sin(w0); - p->a1 = -2 * cos(w0); - p->a2 = 1 - sin(w0); - break; - - case filter_riaa: /* http://www.dsprelated.com/showmessage/73300/3.php */ - if (effp->in_signal.rate == 44100) { - static const double zeros[] = {-0.2014898, 0.9233820}; - static const double poles[] = {0.7083149, 0.9924091}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 48000) { - static const double zeros[] = {-0.1766069, 0.9321590}; - static const double poles[] = {0.7396325, 0.9931330}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 88200) { - static const double zeros[] = {-0.1168735, 0.9648312}; - static const double poles[] = {0.8590646, 0.9964002}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 96000) { - static const double zeros[] = {-0.1141486, 0.9676817}; - static const double poles[] = {0.8699137, 0.9966946}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else { - lsx_fail("Sample rate must be 44.1k, 48k, 88.2k, or 96k"); - return SOX_EOF; - } - { /* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */ - double y = 2 * M_PI * 1000 / effp->in_signal.rate; - double b_re = p->b0 + p->b1 * cos(-y) + p->b2 * cos(-2 * y); - double a_re = p->a0 + p->a1 * cos(-y) + p->a2 * cos(-2 * y); - double b_im = p->b1 * sin(-y) + p->b2 * sin(-2 * y); - double a_im = p->a1 * sin(-y) + p->a2 * sin(-2 * y); - double g = 1 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im))); - p->b0 *= g; p->b1 *= g; p->b2 *= g; - } - mult = (p->b0 + p->b1 + p->b2) / (p->a0 + p->a1 + p->a2); - lsx_debug("gain=%f", linear_to_dB(mult)); - break; - } - if (effp->in_signal.mult) - *effp->in_signal.mult /= mult; - return lsx_biquad_start(effp); -} - - -#define BIQUAD_EFFECT(name,group,usage,flags) \ -sox_effect_handler_t const * lsx_##name##_effect_fn(void) { \ - static sox_effect_handler_t handler = { \ - #name, usage, flags, \ - group##_getopts, start, lsx_biquad_flow, 0, 0, 0, sizeof(biquad_t)\ - }; \ - return &handler; \ -} - -BIQUAD_EFFECT(highpass, hilo2, "[-1|-2] frequency [width[q|o|h|k](0.707q)]", 0) -BIQUAD_EFFECT(lowpass, hilo2, "[-1|-2] frequency [width[q|o|h|k]](0.707q)", 0) -BIQUAD_EFFECT(bandpass, bandpass, "[-c] frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(bandreject,bandrej, "frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(allpass, allpass, "frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(bass, tone, "gain [frequency(100) [width[s|h|k|q|o]](0.5s)]", 0) -BIQUAD_EFFECT(treble, tone, "gain [frequency(3000) [width[s|h|k|q|o]](0.5s)]", 0) -BIQUAD_EFFECT(equalizer, equalizer,"frequency width[q|o|h|k] gain", 0) -BIQUAD_EFFECT(band, band, "[-n] center [width[h|k|q|o]]", 0) -BIQUAD_EFFECT(deemph, deemph, NULL, 0) -BIQUAD_EFFECT(riaa, riaa, NULL, 0) diff --git a/freedv/branches/1.2/src/sox/effects.c b/freedv/branches/1.2/src/sox/effects.c deleted file mode 100644 index 435412fa..00000000 --- a/freedv/branches/1.2/src/sox/effects.c +++ /dev/null @@ -1,544 +0,0 @@ -/* SoX Effects chain (c) 2007 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define LSX_EFF_ALIAS -#include "sox_i.h" -#include -#include -#ifdef HAVE_STRINGS_H - #include -#endif - -#define DEBUG_EFFECTS_CHAIN 0 - -/* Default effect handler functions for do-nothing situations: */ - -static int default_function(sox_effect_t * effp UNUSED) -{ - return SOX_SUCCESS; -} - -/* Pass through samples verbatim */ -int lsx_flow_copy(sox_effect_t * effp UNUSED, const sox_sample_t * ibuf, - sox_sample_t * obuf, size_t * isamp, size_t * osamp) -{ - *isamp = *osamp = min(*isamp, *osamp); - memcpy(obuf, ibuf, *isamp * sizeof(*obuf)); - return SOX_SUCCESS; -} - -/* Inform no more samples to drain */ -static int default_drain(sox_effect_t * effp UNUSED, sox_sample_t *obuf UNUSED, size_t *osamp) -{ - *osamp = 0; - return SOX_EOF; -} - -/* Check that no parameters have been given */ -static int default_getopts(sox_effect_t * effp, int argc, char **argv UNUSED) -{ - return --argc? lsx_usage(effp) : SOX_SUCCESS; -} - -/* Partially initialise the effect structure; signal info will come later */ -sox_effect_t * sox_create_effect(sox_effect_handler_t const * eh) -{ - sox_effect_t * effp = lsx_calloc(1, sizeof(*effp)); - effp->obuf = NULL; - - effp->global_info = sox_get_effects_globals(); - effp->handler = *eh; - if (!effp->handler.getopts) effp->handler.getopts = default_getopts; - if (!effp->handler.start ) effp->handler.start = default_function; - if (!effp->handler.flow ) effp->handler.flow = lsx_flow_copy; - if (!effp->handler.drain ) effp->handler.drain = default_drain; - if (!effp->handler.stop ) effp->handler.stop = default_function; - if (!effp->handler.kill ) effp->handler.kill = default_function; - - effp->priv = lsx_calloc(1, effp->handler.priv_size); - - return effp; -} /* sox_create_effect */ - -int sox_effect_options(sox_effect_t *effp, int argc, char * const argv[]) -{ - int result; - - char * * argv2 = lsx_malloc((argc + 1) * sizeof(*argv2)); - argv2[0] = (char *)effp->handler.name; - memcpy(argv2 + 1, argv, argc * sizeof(*argv2)); - result = effp->handler.getopts(effp, argc + 1, argv2); - free(argv2); - return result; -} /* sox_effect_options */ - -/* Effects chain: */ - -sox_effects_chain_t * sox_create_effects_chain( - sox_encodinginfo_t const * in_enc, sox_encodinginfo_t const * out_enc) -{ - sox_effects_chain_t * result = lsx_calloc(1, sizeof(sox_effects_chain_t)); - result->global_info = *sox_get_effects_globals(); - result->in_enc = in_enc; - result->out_enc = out_enc; - return result; -} /* sox_create_effects_chain */ - -void sox_delete_effects_chain(sox_effects_chain_t *ecp) -{ - if (ecp && ecp->length) - sox_delete_effects(ecp); - free(ecp->effects); - free(ecp); -} /* sox_delete_effects_chain */ - -/* Effect can call in start() or flow() to set minimum input size to flow() */ -int lsx_effect_set_imin(sox_effect_t * effp, size_t imin) -{ - if (imin > sox_globals.bufsiz / effp->flows) { - lsx_fail("sox_bufsiz not big enough"); - return SOX_EOF; - } - - effp->imin = imin; - return SOX_SUCCESS; -} - -/* Effects table to be extended in steps of EFF_TABLE_STEP */ -#define EFF_TABLE_STEP 8 - -/* Add an effect to the chain. *in is the input signal for this effect. *out is - * a suggestion as to what the output signal should be, but depending on its - * given options and *in, the effect can choose to do differently. Whatever - * output rate and channels the effect does produce are written back to *in, - * ready for the next effect in the chain. - */ -int sox_add_effect(sox_effects_chain_t * chain, sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t const * out) -{ - int ret, (*start)(sox_effect_t * effp) = effp->handler.start; - unsigned f; - sox_effect_t eff0; /* Copy of effect for flow 0 before calling start */ - - effp->global_info = &chain->global_info; - effp->in_signal = *in; - effp->out_signal = *out; - effp->in_encoding = chain->in_enc; - effp->out_encoding = chain->out_enc; - if (!(effp->handler.flags & SOX_EFF_CHAN)) - effp->out_signal.channels = in->channels; - if (!(effp->handler.flags & SOX_EFF_RATE)) - effp->out_signal.rate = in->rate; - if (!(effp->handler.flags & SOX_EFF_PREC)) - effp->out_signal.precision = (effp->handler.flags & SOX_EFF_MODIFY)? - in->precision : SOX_SAMPLE_PRECISION; - if (!(effp->handler.flags & SOX_EFF_GAIN)) - effp->out_signal.mult = in->mult; - - effp->flows = - (effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->in_signal.channels; - effp->clips = 0; - effp->imin = 0; - eff0 = *effp, eff0.priv = lsx_memdup(eff0.priv, eff0.handler.priv_size); - eff0.in_signal.mult = NULL; /* Only used in channel 0 */ - ret = start(effp); - if (ret == SOX_EFF_NULL) { - lsx_report("has no effect in this configuration"); - free(eff0.priv); - free(effp->priv); - effp->priv = NULL; - return SOX_SUCCESS; - } - if (ret != SOX_SUCCESS) { - free(eff0.priv); - return SOX_EOF; - } - if (in->mult) - lsx_debug("mult=%g", *in->mult); - - if (!(effp->handler.flags & SOX_EFF_LENGTH)) { - effp->out_signal.length = in->length; - if (effp->out_signal.length != SOX_UNKNOWN_LEN) { - if (effp->handler.flags & SOX_EFF_CHAN) - effp->out_signal.length = - effp->out_signal.length / in->channels * effp->out_signal.channels; - if (effp->handler.flags & SOX_EFF_RATE) - effp->out_signal.length = - effp->out_signal.length / in->rate * effp->out_signal.rate + .5; - } - } - - *in = effp->out_signal; - - if (chain->length == chain->table_size) { - chain->table_size += EFF_TABLE_STEP; - lsx_debug_more("sox_add_effect: extending effects table, " - "new size = %lu", (unsigned long)chain->table_size); - lsx_revalloc(chain->effects, chain->table_size); - } - - chain->effects[chain->length] = - lsx_calloc(effp->flows, sizeof(chain->effects[chain->length][0])); - chain->effects[chain->length][0] = *effp; - - for (f = 1; f < effp->flows; ++f) { - chain->effects[chain->length][f] = eff0; - chain->effects[chain->length][f].flow = f; - chain->effects[chain->length][f].priv = lsx_memdup(eff0.priv, eff0.handler.priv_size); - if (start(&chain->effects[chain->length][f]) != SOX_SUCCESS) { - free(eff0.priv); - return SOX_EOF; - } - } - - ++chain->length; - free(eff0.priv); - return SOX_SUCCESS; -} - -static int flow_effect(sox_effects_chain_t * chain, size_t n) -{ - sox_effect_t * effp1 = &chain->effects[n - 1][0]; - sox_effect_t * effp = &chain->effects[n][0]; - int effstatus = SOX_SUCCESS, f = 0; - size_t i; - const sox_sample_t *ibuf; - size_t idone = effp1->oend - effp1->obeg; - size_t obeg = sox_globals.bufsiz - effp->oend; -#if DEBUG_EFFECTS_CHAIN - size_t pre_idone = idone; - size_t pre_odone = obeg; -#endif - - if (effp->flows == 1) { /* Run effect on all channels at once */ - idone -= idone % effp->in_signal.channels; - effstatus = effp->handler.flow(effp, &effp1->obuf[effp1->obeg], - &effp->obuf[effp->oend], &idone, &obeg); - if (obeg % effp->out_signal.channels != 0) { - lsx_fail("multi-channel effect flowed asymmetrically!"); - effstatus = SOX_EOF; - } - } else { /* Run effect on each channel individually */ - sox_sample_t *obuf = &effp->obuf[effp->oend]; - size_t idone_last = 0, odone_last = 0; /* Initialised to prevent warning */ - - ibuf = &effp1->obuf[effp1->obeg]; - for (i = 0; i < idone; i += effp->flows) - for (f = 0; f < (int)effp->flows; ++f) - chain->ibufc[f][i / effp->flows] = *ibuf++; - -#ifdef HAVE_OPENMP - if (sox_globals.use_threads && effp->flows > 1) - { - #pragma omp parallel for - for (f = 0; f < (int)effp->flows; ++f) { - size_t idonec = idone / effp->flows; - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.flow(&chain->effects[n][f], - chain->ibufc[f], chain->obufc[f], &idonec, &odonec); - if (!f) { - idone_last = idonec; - odone_last = odonec; - } - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - } - else /* sox_globals.use_threads */ -#endif - { - for (f = 0; f < (int)effp->flows; ++f) { - size_t idonec = idone / effp->flows; - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.flow(&chain->effects[n][f], - chain->ibufc[f], chain->obufc[f], &idonec, &odonec); - if (f && (idonec != idone_last || odonec != odone_last)) { - lsx_fail("flowed asymmetrically!"); - effstatus = SOX_EOF; - } - idone_last = idonec; - odone_last = odonec; - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - } - - for (i = 0; i < odone_last; ++i) - for (f = 0; f < (int)effp->flows; ++f) - *obuf++ = chain->obufc[f][i]; - - idone = effp->flows * idone_last; - obeg = effp->flows * odone_last; - } -#if DEBUG_EFFECTS_CHAIN - lsx_report("flow: %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR, - pre_idone, pre_odone, idone, obeg); -#endif - effp1->obeg += idone; - if (effp1->obeg == effp1->oend) - effp1->obeg = effp1->oend = 0; - else if (effp1->oend - effp1->obeg < effp->imin ) { /* Need to refill? */ - memmove(effp1->obuf, &effp1->obuf[effp1->obeg], (effp1->oend - effp1->obeg) * sizeof(*effp1->obuf)); - effp1->oend -= effp1->obeg; - effp1->obeg = 0; - } - - effp->oend += obeg; - - return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF; -} - -/* The same as flow_effect but with no input */ -static int drain_effect(sox_effects_chain_t * chain, size_t n) -{ - sox_effect_t * effp = &chain->effects[n][0]; - int effstatus = SOX_SUCCESS; - size_t i, f; - size_t obeg = sox_globals.bufsiz - effp->oend; -#if DEBUG_EFFECTS_CHAIN - size_t pre_odone = obeg; -#endif - - if (effp->flows == 1) { /* Run effect on all channels at once */ - effstatus = effp->handler.drain(effp, &effp->obuf[effp->oend], &obeg); - if (obeg % effp->out_signal.channels != 0) { - lsx_fail("multi-channel effect drained asymmetrically!"); - effstatus = SOX_EOF; - } - } else { /* Run effect on each channel individually */ - sox_sample_t *obuf = &effp->obuf[effp->oend]; - size_t odone_last = 0; /* Initialised to prevent warning */ - - for (f = 0; f < effp->flows; ++f) { - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.drain(&chain->effects[n][f], chain->obufc[f], &odonec); - if (f && (odonec != odone_last)) { - lsx_fail("drained asymmetrically!"); - effstatus = SOX_EOF; - } - odone_last = odonec; - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - - for (i = 0; i < odone_last; ++i) - for (f = 0; f < effp->flows; ++f) - *obuf++ = chain->obufc[f][i]; - obeg = f * odone_last; - } -#if DEBUG_EFFECTS_CHAIN - lsx_report("drain: %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR, - (size_t)0, pre_odone, (size_t)0, obeg); -#endif - if (!obeg) /* This is the only thing that drain has and flow hasn't */ - effstatus = SOX_EOF; - - effp->oend += obeg; - - return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF; -} - -/* Flow data through the effects chain until an effect or callback gives EOF */ -int sox_flow_effects(sox_effects_chain_t * chain, int (* callback)(sox_bool all_done, void * client_data), void * client_data) -{ - int flow_status = SOX_SUCCESS; - size_t e, source_e = 0; /* effect indices */ - size_t f, max_flows = 0; - sox_bool draining = sox_true; - - for (e = 0; e < chain->length; ++e) { - chain->effects[e][0].obuf = lsx_realloc(chain->effects[e][0].obuf, - sox_globals.bufsiz * sizeof(chain->effects[e][0].obuf[0])); - /* Possibly there is already a buffer, if this is a used effect; - it may still contain samples in that case. */ - /* Memory will be freed by sox_delete_effect() later. */ - max_flows = max(max_flows, chain->effects[e][0].flows); - } - if (max_flows == 1) /* don't need interleave buffers */ - max_flows = 0; - chain->ibufc = lsx_calloc(max_flows, sizeof(*chain->ibufc)); - chain->obufc = lsx_calloc(max_flows, sizeof(*chain->obufc)); - for (f = 0; f < max_flows; ++f) { - chain->ibufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->ibufc[f][0])); - chain->obufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->obufc[f][0])); - } - - e = chain->length - 1; - while (source_e < chain->length) { -#define have_imin (e > 0 && e < chain->length && chain->effects[e - 1][0].oend - chain->effects[e - 1][0].obeg >= chain->effects[e][0].imin) - size_t osize = chain->effects[e][0].oend - chain->effects[e][0].obeg; - if (e == source_e && (draining || !have_imin)) { - if (drain_effect(chain, e) == SOX_EOF) { - ++source_e; - draining = sox_false; - } - } else if (have_imin && flow_effect(chain, e) == SOX_EOF) { - flow_status = SOX_EOF; - if (e == chain->length - 1) - break; - source_e = e; - draining = sox_true; - } - if (e < chain->length && chain->effects[e][0].oend - chain->effects[e][0].obeg > osize) /* False for output */ - ++e; - else if (e == source_e) - draining = sox_true; - else if ((int)--e < (int)source_e) - e = source_e; - - if (callback && callback(source_e == chain->length, client_data) != SOX_SUCCESS) { - flow_status = SOX_EOF; /* Client has requested to stop the flow. */ - break; - } - } - - for (f = 0; f < max_flows; ++f) { - free(chain->ibufc[f]); - free(chain->obufc[f]); - } - free(chain->obufc); - free(chain->ibufc); - - return flow_status; -} - -sox_uint64_t sox_effects_clips(sox_effects_chain_t * chain) -{ - unsigned i, f; - uint64_t clips = 0; - for (i = 1; i < chain->length - 1; ++i) - for (f = 0; f < chain->effects[i][0].flows; ++f) - clips += chain->effects[i][f].clips; - return clips; -} - -sox_uint64_t sox_stop_effect(sox_effect_t *effp) -{ - unsigned f; - uint64_t clips = 0; - - for (f = 0; f < effp->flows; ++f) { - effp[f].handler.stop(&effp[f]); - clips += effp[f].clips; - } - return clips; -} - -void sox_push_effect_last(sox_effects_chain_t *chain, sox_effect_t *effp) -{ - if (chain->length == chain->table_size) { - chain->table_size += EFF_TABLE_STEP; - lsx_debug_more("sox_push_effect_last: extending effects table, " - "new size = %lu", (unsigned long)chain->table_size); - lsx_revalloc(chain->effects, chain->table_size); - } - - chain->effects[chain->length++] = effp; -} /* sox_push_effect_last */ - -sox_effect_t *sox_pop_effect_last(sox_effects_chain_t *chain) -{ - if (chain->length > 0) - { - sox_effect_t *effp; - chain->length--; - effp = chain->effects[chain->length]; - chain->effects[chain->length] = NULL; - return effp; - } - else - return NULL; -} /* sox_pop_effect_last */ - -/* Free resources related to effect. - * Note: This currently closes down the effect which might - * not be obvious from name. - */ -void sox_delete_effect(sox_effect_t *effp) -{ - uint64_t clips; - unsigned f; - - if ((clips = sox_stop_effect(effp)) != 0) - lsx_warn("%s clipped %" PRIu64 " samples; decrease volume?", - effp->handler.name, clips); - if (effp->obeg != effp->oend) - lsx_debug("output buffer still held %" PRIuPTR " samples; dropped.", - (effp->oend - effp->obeg)/effp->out_signal.channels); - /* May or may not indicate a problem; it is normal if the user aborted - processing, or if an effect like "trim" stopped early. */ - effp->handler.kill(effp); /* N.B. only one kill; not one per flow */ - for (f = 0; f < effp->flows; ++f) - free(effp[f].priv); - free(effp->obuf); - free(effp); -} - -void sox_delete_effect_last(sox_effects_chain_t *chain) -{ - if (chain->length > 0) - { - chain->length--; - sox_delete_effect(chain->effects[chain->length]); - chain->effects[chain->length] = NULL; - } -} /* sox_delete_effect_last */ - -/* Remove all effects from the chain. - * Note: This currently closes down the effect which might - * not be obvious from name. - */ -void sox_delete_effects(sox_effects_chain_t * chain) -{ - size_t e; - - for (e = 0; e < chain->length; ++e) { - sox_delete_effect(chain->effects[e]); - chain->effects[e] = NULL; - } - chain->length = 0; -} - -/*----------------------------- Effects library ------------------------------*/ - -static sox_effect_fn_t s_sox_effect_fns[] = { -#define EFFECT(f) lsx_##f##_effect_fn, -#include "effects.h" -#undef EFFECT - NULL -}; - -const sox_effect_fn_t* -sox_get_effect_fns(void) -{ - return s_sox_effect_fns; -} - -/* Find a named effect in the effects library */ -sox_effect_handler_t const * sox_find_effect(char const * name) -{ - int e; - sox_effect_fn_t const * fns = sox_get_effect_fns(); - for (e = 0; fns[e]; ++e) { - const sox_effect_handler_t *eh = fns[e] (); - if (eh && eh->name && strcasecmp(eh->name, name) == 0) - return eh; /* Found it. */ - } - return NULL; -} diff --git a/freedv/branches/1.2/src/sox/effects.h b/freedv/branches/1.2/src/sox/effects.h deleted file mode 100644 index 8d7025c8..00000000 --- a/freedv/branches/1.2/src/sox/effects.h +++ /dev/null @@ -1,22 +0,0 @@ -/* This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* Manually edited for FreeDV to contain just the effects we need */ - - EFFECT(bass) - EFFECT(highpass) - EFFECT(treble) - EFFECT(equalizer) - diff --git a/freedv/branches/1.2/src/sox/effects_i.c b/freedv/branches/1.2/src/sox/effects_i.c deleted file mode 100644 index e5770a94..00000000 --- a/freedv/branches/1.2/src/sox/effects_i.c +++ /dev/null @@ -1,379 +0,0 @@ -/* Implements a libSoX internal interface for implementing effects. - * All public functions & data are prefixed with lsx_ . - * - * Copyright (c) 2005-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define LSX_EFF_ALIAS -#include "sox_i.h" -#include -#include - -int lsx_usage(sox_effect_t * effp) -{ - if (effp->handler.usage) - lsx_fail("usage: %s", effp->handler.usage); - else - lsx_fail("this effect takes no parameters"); - return SOX_EOF; -} - -char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n) -{ - if (!*usage) { - size_t i, len; - for (len = i = 0; i < n; len += strlen(lines[i++]) + 1); - *usage = lsx_malloc(len); /* FIXME: this memory will never be freed */ - strcpy(*usage, lines[0]); - for (i = 1; i < n; ++i) { - strcat(*usage, "\n"); - strcat(*usage, lines[i]); - } - } - return *usage; -} - -static lsx_enum_item const s_lsx_wave_enum[] = { - LSX_ENUM_ITEM(SOX_WAVE_,SINE) - LSX_ENUM_ITEM(SOX_WAVE_,TRIANGLE) - {0, 0}}; - -lsx_enum_item const * lsx_get_wave_enum(void) -{ - return s_lsx_wave_enum; -} - -void lsx_generate_wave_table( - lsx_wave_t wave_type, - sox_data_t data_type, - void *table, - size_t table_size, - double min, - double max, - double phase) -{ - uint32_t t; - uint32_t phase_offset = phase / M_PI / 2 * table_size + 0.5; - - for (t = 0; t < table_size; t++) - { - uint32_t point = (t + phase_offset) % table_size; - double d; - switch (wave_type) - { - case SOX_WAVE_SINE: - d = (sin((double)point / table_size * 2 * M_PI) + 1) / 2; - break; - - case SOX_WAVE_TRIANGLE: - d = (double)point * 2 / table_size; - switch (4 * point / table_size) - { - case 0: d = d + 0.5; break; - case 1: case 2: d = 1.5 - d; break; - case 3: d = d - 1.5; break; - } - break; - - default: /* Oops! FIXME */ - d = 0.0; /* Make sure we have a value */ - break; - } - d = d * (max - min) + min; - switch (data_type) - { - case SOX_FLOAT: - { - float *fp = (float *)table; - *fp++ = (float)d; - table = fp; - continue; - } - case SOX_DOUBLE: - { - double *dp = (double *)table; - *dp++ = d; - table = dp; - continue; - } - default: break; - } - d += d < 0? -0.5 : +0.5; - switch (data_type) - { - case SOX_SHORT: - { - short *sp = table; - *sp++ = (short)d; - table = sp; - continue; - } - case SOX_INT: - { - int *ip = table; - *ip++ = (int)d; - table = ip; - continue; - } - default: break; - } - } -} - -/* - * lsx_parsesamples - * - * Parse a string for # of samples. If string ends with a 's' - * then the string is interpreted as a user calculated # of samples. - * If string contains ':' or '.' or if it ends with a 't' then its - * treated as an amount of time. This is converted into seconds and - * fraction of seconds and then use the sample rate to calculate - * # of samples. - * Returns NULL on error, pointer to next char to parse otherwise. - */ -char const * lsx_parsesamples(sox_rate_t rate, const char *str0, uint64_t *samples, int def) -{ - int i, found_samples = 0, found_time = 0; - char const * end; - char const * pos; - sox_bool found_colon, found_dot; - char * str = (char *)str0; - - for (;*str == ' '; ++str); - for (end = str; *end && strchr("0123456789:.ets", *end); ++end); - if (end == str) - return NULL; - - pos = strchr(str, ':'); - found_colon = pos && pos < end; - - pos = strchr(str, '.'); - found_dot = pos && pos < end; - - if (found_colon || found_dot || *(end-1) == 't') - found_time = 1; - else if (*(end-1) == 's') - found_samples = 1; - - if (found_time || (def == 't' && !found_samples)) { - for (*samples = 0, i = 0; *str != '.' && i < 3; ++i) { - char * last_str = str; - long part = strtol(str, &str, 10); - if (!i && str == last_str) - return NULL; - *samples += rate * part; - if (i < 2) { - if (*str != ':') - break; - ++str; - *samples *= 60; - } - } - if (*str == '.') { - char * last_str = str; - double part = strtod(str, &str); - if (str == last_str) - return NULL; - *samples += rate * part + .5; - } - return *str == 't'? str + 1 : str; - } - { - char * last_str = str; - double part = strtod(str, &str); - if (str == last_str) - return NULL; - *samples = part + .5; - return *str == 's'? str + 1 : str; - } -} - -#if 0 - -#include - -#define TEST(st, samp, len) \ - str = st; \ - next = lsx_parsesamples(10000, str, &samples, 't'); \ - assert(samples == samp && next == str + len); - -int main(int argc, char * * argv) -{ - char const * str, * next; - uint64_t samples; - - TEST("0" , 0, 1) - TEST("1" , 10000, 1) - - TEST("0s" , 0, 2) - TEST("0s,", 0, 2) - TEST("0s/", 0, 2) - TEST("0s@", 0, 2) - - TEST("0t" , 0, 2) - TEST("0t,", 0, 2) - TEST("0t/", 0, 2) - TEST("0t@", 0, 2) - - TEST("1s" , 1, 2) - TEST("1s,", 1, 2) - TEST("1s/", 1, 2) - TEST("1s@", 1, 2) - TEST(" 01s" , 1, 4) - TEST("1e6s" , 1000000, 4) - - TEST("1t" , 10000, 2) - TEST("1t,", 10000, 2) - TEST("1t/", 10000, 2) - TEST("1t@", 10000, 2) - TEST("1.1t" , 11000, 4) - TEST("1.1t,", 11000, 4) - TEST("1.1t/", 11000, 4) - TEST("1.1t@", 11000, 4) - TEST("1e6t" , 10000, 1) - - TEST(".0", 0, 2) - TEST("0.0", 0, 3) - TEST("0:0.0", 0, 5) - TEST("0:0:0.0", 0, 7) - - TEST(".1", 1000, 2) - TEST(".10", 1000, 3) - TEST("0.1", 1000, 3) - TEST("1.1", 11000, 3) - TEST("1:1.1", 611000, 5) - TEST("1:1:1.1", 36611000, 7) - TEST("1:1", 610000, 3) - TEST("1:01", 610000, 4) - TEST("1:1:1", 36610000, 5) - TEST("1:", 600000, 2) - TEST("1::", 36000000, 3) - - TEST("0.444444", 4444, 8) - TEST("0.555555", 5556, 8) - - assert(!lsx_parsesamples(10000, "x", &samples, 't')); - return 0; -} -#endif - -/* a note is given as an int, - * 0 => 440 Hz = A - * >0 => number of half notes 'up', - * <0 => number of half notes down, - * example 12 => A of next octave, 880Hz - * - * calculated by freq = 440Hz * 2**(note/12) - */ -static double calc_note_freq(double note, int key) -{ - if (key != INT_MAX) { /* Just intonation. */ - static const int n[] = {16, 9, 6, 5, 4, 7}; /* Numerator. */ - static const int d[] = {15, 8, 5, 4, 3, 5}; /* Denominator. */ - static double j[13]; /* Just semitones */ - int i, m = floor(note); - - if (!j[1]) for (i = 1; i <= 12; ++i) - j[i] = i <= 6? log((double)n[i - 1] / d[i - 1]) / log(2.) : 1 - j[12 - i]; - note -= m; - m -= key = m - ((INT_MAX / 2 - ((INT_MAX / 2) % 12) + m - key) % 12); - return 440 * pow(2., key / 12. + j[m] + (j[m + 1] - j[m]) * note); - } - return 440 * pow(2., note / 12); -} - -int lsx_parse_note(char const * text, char * * end_ptr) -{ - int result = INT_MAX; - - if (*text >= 'A' && *text <= 'G') { - result = (int)(5/3. * (*text++ - 'A') + 9.5) % 12 - 9; - if (*text == 'b') {--result; ++text;} - else if (*text == '#') {++result; ++text;} - if (isdigit((unsigned char)*text)) - result += 12 * (*text++ - '4'); - } - *end_ptr = (char *)text; - return result; -} - -/* Read string 'text' and convert to frequency. - * 'text' can be a positive number which is the frequency in Hz. - * If 'text' starts with a '%' and a following number the corresponding - * note is calculated. - * Return -1 on error. - */ -double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key) -{ - double result; - - if (*text == '%') { - result = strtod(text + 1, end_ptr); - if (*end_ptr == text + 1) - return -1; - return calc_note_freq(result, key); - } - if (*text >= 'A' && *text <= 'G') { - int result2 = lsx_parse_note(text, end_ptr); - return result2 == INT_MAX? - 1 : calc_note_freq((double)result2, key); - } - result = strtod(text, end_ptr); - if (end_ptr) { - if (*end_ptr == text) - return -1; - if (**end_ptr == 'k') { - result *= 1000; - ++*end_ptr; - } - } - return result < 0 ? -1 : result; -} - -FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename) -{ - FILE * file; - - if (!filename || !strcmp(filename, "-")) { - if (effp->global_info->global_info->stdin_in_use_by) { - lsx_fail("stdin already in use by `%s'", effp->global_info->global_info->stdin_in_use_by); - return NULL; - } - effp->global_info->global_info->stdin_in_use_by = effp->handler.name; - file = stdin; - } - else if (!(file = fopen(filename, "r"))) { - lsx_fail("couldn't open file %s: %s", filename, strerror(errno)); - return NULL; - } - return file; -} - -int lsx_effects_init(void) -{ - #ifndef __FREEDV__ - init_fft_cache(); - #endif - return SOX_SUCCESS; -} - -int lsx_effects_quit(void) -{ - #ifndef __FREEDV__ - clear_fft_cache(); - #endif - return SOX_SUCCESS; -} diff --git a/freedv/branches/1.2/src/sox/formats_i.c b/freedv/branches/1.2/src/sox/formats_i.c deleted file mode 100644 index 17c40615..00000000 --- a/freedv/branches/1.2/src/sox/formats_i.c +++ /dev/null @@ -1,487 +0,0 @@ -/* Implements a libSoX internal interface for use in implementing file formats. - * All public functions & data are prefixed with lsx_ . - * - * (c) 2005-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include -#include -#include - -void lsx_fail_errno(sox_format_t * ft, int sox_errno, const char *fmt, ...) -{ - va_list args; - - ft->sox_errno = sox_errno; - - va_start(args, fmt); -#ifdef HAVE_VSNPRINTF - vsnprintf(ft->sox_errstr, sizeof(ft->sox_errstr), fmt, args); -#else - vsprintf(ft->sox_errstr, fmt, args); -#endif - va_end(args); - ft->sox_errstr[255] = '\0'; -} - -void lsx_set_signal_defaults(sox_format_t * ft) -{ - if (!ft->signal.rate ) ft->signal.rate = SOX_DEFAULT_RATE; - if (!ft->signal.precision) ft->signal.precision = SOX_DEFAULT_PRECISION; - if (!ft->signal.channels ) ft->signal.channels = SOX_DEFAULT_CHANNELS; - - if (!ft->encoding.bits_per_sample) - ft->encoding.bits_per_sample = ft->signal.precision; - if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN) - ft->encoding.encoding = SOX_ENCODING_SIGN2; -} - -#ifndef __FREEDV__ -int lsx_check_read_params(sox_format_t * ft, unsigned channels, - sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, - uint64_t num_samples, sox_bool check_length) -{ - ft->signal.length = ft->signal.length == SOX_IGNORE_LENGTH? SOX_UNSPEC : num_samples; - - if (ft->seekable) - ft->data_start = lsx_tell(ft); - - if (channels && ft->signal.channels && ft->signal.channels != channels) - lsx_warn("`%s': overriding number of channels", ft->filename); - else ft->signal.channels = channels; - - if (rate && ft->signal.rate && ft->signal.rate != rate) - lsx_warn("`%s': overriding sample rate", ft->filename); - else ft->signal.rate = rate; - - if (encoding && ft->encoding.encoding && ft->encoding.encoding != encoding) - lsx_warn("`%s': overriding encoding type", ft->filename); - else ft->encoding.encoding = encoding; - - if (bits_per_sample && ft->encoding.bits_per_sample && ft->encoding.bits_per_sample != bits_per_sample) - lsx_warn("`%s': overriding encoding size", ft->filename); - ft->encoding.bits_per_sample = bits_per_sample; - - if (check_length && ft->encoding.bits_per_sample && lsx_filelength(ft)) { - uint64_t calculated_length = div_bits(lsx_filelength(ft) - ft->data_start, ft->encoding.bits_per_sample); - if (!ft->signal.length) - ft->signal.length = calculated_length; - else if (num_samples != calculated_length) - lsx_warn("`%s': file header gives the total number of samples as %" PRIu64 " but file length indicates the number is in fact %" PRIu64, ft->filename, num_samples, calculated_length); - } - - if (sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample)) - return SOX_SUCCESS; - lsx_fail_errno(ft, EINVAL, "invalid format for this file type"); - return SOX_EOF; -} -#endif - -/* Read in a buffer of data of length len bytes. - * Returns number of bytes read. - */ -size_t lsx_readbuf(sox_format_t * ft, void *buf, size_t len) -{ - size_t ret = fread(buf, (size_t) 1, len, (FILE*)ft->fp); - if (ret != len && ferror((FILE*)ft->fp)) - lsx_fail_errno(ft, errno, "lsx_readbuf"); - ft->tell_off += ret; - return ret; -} - -/* Skip input without seeking. */ -int lsx_skipbytes(sox_format_t * ft, size_t n) -{ - unsigned char trash; - - while (n--) - if (lsx_readb(ft, &trash) == SOX_EOF) - return (SOX_EOF); - - return (SOX_SUCCESS); -} - -/* Pad output. */ -int lsx_padbytes(sox_format_t * ft, size_t n) -{ - while (n--) - if (lsx_writeb(ft, '\0') == SOX_EOF) - return (SOX_EOF); - - return (SOX_SUCCESS); -} - -/* Write a buffer of data of length bytes. - * Returns number of bytes written. - */ -size_t lsx_writebuf(sox_format_t * ft, void const * buf, size_t len) -{ - size_t ret = fwrite(buf, (size_t) 1, len, (FILE*)ft->fp); - if (ret != len) { - lsx_fail_errno(ft, errno, "error writing output file"); - clearerr((FILE*)ft->fp); /* Allows us to seek back to write header */ - } - ft->tell_off += ret; - return ret; -} - -uint64_t lsx_filelength(sox_format_t * ft) -{ - struct stat st; - int ret = fstat(fileno((FILE*)ft->fp), &st); - - return (!ret && (st.st_mode & S_IFREG))? (uint64_t)st.st_size : 0; -} - -int lsx_flush(sox_format_t * ft) -{ - return fflush((FILE*)ft->fp); -} - -off_t lsx_tell(sox_format_t * ft) -{ - return ft->seekable? (off_t)ftello((FILE*)ft->fp) : (off_t)ft->tell_off; -} - -int lsx_eof(sox_format_t * ft) -{ - return feof((FILE*)ft->fp); -} - -int lsx_error(sox_format_t * ft) -{ - return ferror((FILE*)ft->fp); -} - -void lsx_rewind(sox_format_t * ft) -{ - rewind((FILE*)ft->fp); - ft->tell_off = 0; -} - -void lsx_clearerr(sox_format_t * ft) -{ - clearerr((FILE*)ft->fp); - ft->sox_errno = 0; -} - -int lsx_unreadb(sox_format_t * ft, unsigned b) -{ - return ungetc((int)b, ft->fp); -} - -/* Implements traditional fseek() behavior. Meant to abstract out - * file operations so that they could one day also work on memory - * buffers. - * - * N.B. Can only seek forwards on non-seekable streams! - */ -int lsx_seeki(sox_format_t * ft, off_t offset, int whence) -{ - if (ft->seekable == 0) { - /* If a stream peel off chars else EPERM */ - if (whence == SEEK_CUR) { - while (offset > 0 && !feof((FILE*)ft->fp)) { - getc((FILE*)ft->fp); - offset--; - ++ft->tell_off; - } - if (offset) - lsx_fail_errno(ft,SOX_EOF, "offset past EOF"); - else - ft->sox_errno = SOX_SUCCESS; - } else - lsx_fail_errno(ft,SOX_EPERM, "file not seekable"); - } else { - if (fseeko((FILE*)ft->fp, offset, whence) == -1) - lsx_fail_errno(ft,errno, "%s", strerror(errno)); - else - ft->sox_errno = SOX_SUCCESS; - } - return ft->sox_errno; -} - -int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, off_t to_sample) -{ - double wide_sample = to_sample - (to_sample % ft->signal.channels); - double to_d = wide_sample * ft->encoding.bits_per_sample / 8; - off_t to = to_d; - return (to != to_d)? SOX_EOF : lsx_seeki(ft, (byte_offset + to), SEEK_SET); -} - -/* Read and write known datatypes in "machine format". Swap if indicated. - * They all return SOX_EOF on error and SOX_SUCCESS on success. - */ -/* Read n-char string (and possibly null-terminating). - * Stop reading and null-terminate string if either a 0 or \n is reached. - */ -int lsx_reads(sox_format_t * ft, char *c, size_t len) -{ - char *sc; - char in; - - sc = c; - do - { - if (lsx_readbuf(ft, &in, (size_t)1) != 1) - { - *sc = 0; - return (SOX_EOF); - } - if (in == 0 || in == '\n') - break; - - *sc = in; - sc++; - } while (sc - c < (ptrdiff_t)len); - *sc = 0; - return(SOX_SUCCESS); -} - -/* Write null-terminated string (without \0). */ -int lsx_writes(sox_format_t * ft, char const * c) -{ - if (lsx_writebuf(ft, c, strlen(c)) != strlen(c)) - return(SOX_EOF); - return(SOX_SUCCESS); -} - -/* return swapped 32-bit float */ -static void lsx_swapf(float * f) -{ - union { - uint32_t dw; - float f; - } u; - - u.f= *f; - u.dw= (u.dw>>24) | ((u.dw>>8)&0xff00) | ((u.dw<<8)&0xff0000) | (u.dw<<24); - *f = u.f; -} - -static void swap(void * data, size_t len) -{ - uint8_t * bytes = (uint8_t *)data; - size_t i; - - for (i = 0; i < len / 2; ++i) { - char tmp = bytes[i]; - bytes[i] = bytes[len - 1 - i]; - bytes[len - 1 - i] = tmp; - } -} - -static double lsx_swapdf(double data) -{ - swap(&data, sizeof(data)); - return data; -} - -static uint64_t lsx_swapqw(uint64_t data) -{ - swap(&data, sizeof(data)); - return data; -} - -/* Lookup table to reverse the bit order of a byte. ie MSB become LSB */ -static uint8_t const cswap[256] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, - 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, - 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, - 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, - 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, - 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, - 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, - 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, - 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, - 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, - 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, - 0x3F, 0xBF, 0x7F, 0xFF -}; - -/* Utilities to byte-swap values, use libc optimized macros if possible */ -#define TWIDDLE_BYTE(ub, type) \ - do { \ - if (ft->encoding.reverse_bits) \ - ub = cswap[ub]; \ - if (ft->encoding.reverse_nibbles) \ - ub = ((ub & 15) << 4) | (ub >> 4); \ - } while (0); - -#define TWIDDLE_WORD(uw, type) \ - if (ft->encoding.reverse_bytes) \ - uw = lsx_swap ## type(uw); - -#define TWIDDLE_FLOAT(f, type) \ - if (ft->encoding.reverse_bytes) \ - lsx_swapf(&f); - -/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte - types). */ -#define READ_FUNC(type, size, ctype, twiddle) \ - size_t lsx_read_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nread; \ - nread = lsx_readbuf(ft, buf, len * size) / size; \ - for (n = 0; n < nread; n++) \ - twiddle(buf[n], type); \ - return nread; \ - } - -/* Unpack a 3-byte value from a uint8_t * */ -#define sox_unpack3(p) (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN? \ - ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16)) : \ - ((p)[2] | ((p)[1] << 8) | ((p)[0] << 16))) - -/* This (slower) macro works for unaligned types (e.g. 3-byte types) - that need to be unpacked. */ -#define READ_FUNC_UNPACK(type, size, ctype, twiddle) \ - size_t lsx_read_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nread; \ - uint8_t *data = lsx_malloc(size * len); \ - nread = lsx_readbuf(ft, data, len * size) / size; \ - for (n = 0; n < nread; n++) \ - buf[n] = sox_unpack ## size(data + n * size); \ - free(data); \ - return n; \ - } - -READ_FUNC(b, 1, uint8_t, TWIDDLE_BYTE) -READ_FUNC(w, 2, uint16_t, TWIDDLE_WORD) -READ_FUNC_UNPACK(3, 3, sox_uint24_t, TWIDDLE_WORD) -READ_FUNC(dw, 4, uint32_t, TWIDDLE_WORD) -READ_FUNC(qw, 8, uint64_t, TWIDDLE_WORD) -READ_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT) -READ_FUNC(df, sizeof(double), double, TWIDDLE_WORD) - -#define READ1_FUNC(type, ctype) \ -int lsx_read ## type(sox_format_t * ft, ctype * datum) { \ - if (lsx_read_ ## type ## _buf(ft, datum, (size_t)1) == 1) \ - return SOX_SUCCESS; \ - if (!lsx_error(ft)) \ - lsx_fail_errno(ft, errno, premature_eof); \ - return SOX_EOF; \ -} - -static char const premature_eof[] = "premature EOF"; - -READ1_FUNC(b, uint8_t) -READ1_FUNC(w, uint16_t) -READ1_FUNC(3, sox_uint24_t) -READ1_FUNC(dw, uint32_t) -READ1_FUNC(qw, uint64_t) -READ1_FUNC(f, float) -READ1_FUNC(df, double) - -int lsx_readchars(sox_format_t * ft, char * chars, size_t len) -{ - size_t ret = lsx_readbuf(ft, chars, len); - if (ret == len) - return SOX_SUCCESS; - if (!lsx_error(ft)) - lsx_fail_errno(ft, errno, premature_eof); - return SOX_EOF; -} - -/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte - types). */ -#define WRITE_FUNC(type, size, ctype, twiddle) \ - size_t lsx_write_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nwritten; \ - for (n = 0; n < len; n++) \ - twiddle(buf[n], type); \ - nwritten = lsx_writebuf(ft, buf, len * size); \ - return nwritten / size; \ - } - -/* Pack a 3-byte value to a uint8_t * */ -#define sox_pack3(p, v) do {if (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN)\ -{(p)[0] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[2] = (v >> 16) & 0xff;} else \ -{(p)[2] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[0] = (v >> 16) & 0xff;} \ -} while (0) - -/* This (slower) macro works for unaligned types (e.g. 3-byte types) - that need to be packed. */ -#define WRITE_FUNC_PACK(type, size, ctype, twiddle) \ - size_t lsx_write_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nwritten; \ - uint8_t *data = lsx_malloc(size * len); \ - for (n = 0; n < len; n++) \ - sox_pack ## size(data + n * size, buf[n]); \ - nwritten = lsx_writebuf(ft, data, len * size); \ - free(data); \ - return nwritten / size; \ - } - -WRITE_FUNC(b, 1, uint8_t, TWIDDLE_BYTE) -WRITE_FUNC(w, 2, uint16_t, TWIDDLE_WORD) -WRITE_FUNC_PACK(3, 3, sox_uint24_t, TWIDDLE_WORD) -WRITE_FUNC(dw, 4, uint32_t, TWIDDLE_WORD) -WRITE_FUNC(qw, 8, uint64_t, TWIDDLE_WORD) -WRITE_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT) -WRITE_FUNC(df, sizeof(double), double, TWIDDLE_WORD) - -#define WRITE1U_FUNC(type, ctype) \ - int lsx_write ## type(sox_format_t * ft, unsigned d) \ - { ctype datum = (ctype)d; \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -#define WRITE1S_FUNC(type, ctype) \ - int lsx_writes ## type(sox_format_t * ft, signed d) \ - { ctype datum = (ctype)d; \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -#define WRITE1_FUNC(type, ctype) \ - int lsx_write ## type(sox_format_t * ft, ctype datum) \ - { \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -WRITE1U_FUNC(b, uint8_t) -WRITE1U_FUNC(w, uint16_t) -WRITE1U_FUNC(3, sox_uint24_t) -WRITE1U_FUNC(dw, uint32_t) -WRITE1_FUNC(qw, uint64_t) -WRITE1S_FUNC(b, uint8_t) -WRITE1S_FUNC(w, uint16_t) -WRITE1_FUNC(df, double) - -int lsx_writef(sox_format_t * ft, double datum) -{ - float f = datum; - return lsx_write_f_buf(ft, &f, (size_t) 1) == 1 ? SOX_SUCCESS : SOX_EOF; -} diff --git a/freedv/branches/1.2/src/sox/libsox.c b/freedv/branches/1.2/src/sox/libsox.c deleted file mode 100644 index 43620250..00000000 --- a/freedv/branches/1.2/src/sox/libsox.c +++ /dev/null @@ -1,225 +0,0 @@ -/* Implements the public API for libSoX general functions - * All public functions & data are prefixed with sox_ . - * - * (c) 2006-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include - -const char *sox_version(void) -{ - static char versionstr[20]; - - sprintf(versionstr, "%d.%d.%d", - (SOX_LIB_VERSION_CODE & 0xff0000) >> 16, - (SOX_LIB_VERSION_CODE & 0x00ff00) >> 8, - (SOX_LIB_VERSION_CODE & 0x0000ff)); - return(versionstr); -} - -sox_version_info_t const * sox_version_info(void) -{ -#define STRINGIZE1(x) #x -#define STRINGIZE(x) STRINGIZE1(x) - static char arch[30]; - static sox_version_info_t info = { - /* size */ - sizeof(sox_version_info_t), - /* flags */ - (sox_version_flags_t)( -#if HAVE_POPEN - sox_version_have_popen + -#endif -#if HAVE_MAGIC - sox_version_have_magic + -#endif -#if HAVE_OPENMP - sox_version_have_threads + -#endif -#ifdef HAVE_FMEMOPEN - sox_version_have_memopen + -#endif - sox_version_none), - /* version_code */ - SOX_LIB_VERSION_CODE, - /* version */ - NULL, - /* sox_version_extra */ -#ifdef PACKAGE_EXTRA - PACKAGE_EXTRA, -#else - NULL, -#endif - /* sox_time */ - __DATE__ " " __TIME__, - /* sox_distro */ -#ifdef DISTRO - DISTRO, -#else - NULL, -#endif - /* sox_compiler */ -#if defined __GNUC__ - "gcc " __VERSION__, -#elif defined _MSC_VER - "msvc " STRINGIZE(_MSC_FULL_VER), -#elif defined __SUNPRO_C - fprintf(file, "sun c " STRINGIZE(__SUNPRO_C), -#else - NULL, -#endif - /* sox_arch */ - NULL - }; - - if (!info.version) - { - info.version = sox_version(); - } - - if (!info.arch) - { - snprintf(arch, sizeof(arch), - "%" PRIuPTR "%" PRIuPTR "%" PRIuPTR "%" PRIuPTR - " %" PRIuPTR "%" PRIuPTR " %" PRIuPTR "%" PRIuPTR " %c %s", - sizeof(char), sizeof(short), sizeof(long), sizeof(off_t), - sizeof(float), sizeof(double), sizeof(int *), sizeof(int (*)(void)), - MACHINE_IS_BIGENDIAN ? 'B' : 'L', - (info.flags & sox_version_have_threads) ? "OMP" : ""); - arch[sizeof(arch) - 1] = 0; - info.arch = arch; - } - - return &info; -} - -/* Default routine to output messages; can be overridden */ -static void output_message( - unsigned level, const char *filename, const char *fmt, va_list ap) -{ - if (sox_globals.verbosity >= level) { - char base_name[128]; - sox_basename(base_name, sizeof(base_name), filename); - fprintf(stderr, "%s: ", base_name); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - } -} - -static sox_globals_t s_sox_globals = { - 2, /* unsigned verbosity */ - output_message, /* sox_output_message_handler */ - sox_false, /* sox_bool repeatable */ - 8192, /* size_t bufsiz */ - 0, /* size_t input_bufsiz */ - 0, /* int32_t ranqd1 */ - NULL, /* char const * stdin_in_use_by */ - NULL, /* char const * stdout_in_use_by */ - NULL, /* char const * subsystem */ - NULL, /* char * tmp_path */ - sox_false, /* sox_bool use_magic */ - sox_false /* sox_bool use_threads */ -}; - -sox_globals_t * sox_get_globals(void) -{ - return &s_sox_globals; -} - -/* FIXME: Not thread safe using globals */ -static sox_effects_globals_t s_sox_effects_globals = - {sox_plot_off, &s_sox_globals}; - -sox_effects_globals_t * -sox_get_effects_globals(void) -{ - return &s_sox_effects_globals; -} - -char const * sox_strerror(int sox_errno) -{ - static char const * const errors[] = { - "Invalid Audio Header", - "Unsupported data format", - "Can't allocate memory", - "Operation not permitted", - "Operation not supported", - "Invalid argument", - }; - if (sox_errno < SOX_EHDR) - return strerror(sox_errno); - sox_errno -= SOX_EHDR; - if (sox_errno < 0 || (size_t)sox_errno >= array_length(errors)) - return "Unknown error"; - return errors[sox_errno]; -} - -size_t sox_basename(char * base_buffer, size_t base_buffer_len, const char * filename) -{ - if (!base_buffer || !base_buffer_len) - { - return 0; - } - else - { - char const * slash_pos = LAST_SLASH(filename); - char const * base_name = slash_pos ? slash_pos + 1 : filename; - char const * dot_pos = strrchr(base_name, '.'); - size_t i, len; - dot_pos = dot_pos ? dot_pos : base_name + strlen(base_name); - len = dot_pos - base_name; - len = min(len, base_buffer_len - 1); - for (i = 0; i < len; i++) - { - base_buffer[i] = base_name[i]; - } - base_buffer[i] = 0; - return i; - } -} - -#define SOX_MESSAGE_FUNCTION(name,level) \ -void name(char const * fmt, ...) { \ - va_list ap; \ - va_start(ap, fmt); \ - if (sox_globals.output_message_handler) \ - (*sox_globals.output_message_handler)(level,sox_globals.subsystem,fmt,ap); \ - va_end(ap); \ -} - -SOX_MESSAGE_FUNCTION(lsx_fail_impl , 1) -SOX_MESSAGE_FUNCTION(lsx_warn_impl , 2) -SOX_MESSAGE_FUNCTION(lsx_report_impl, 3) -SOX_MESSAGE_FUNCTION(lsx_debug_impl , 4) -SOX_MESSAGE_FUNCTION(lsx_debug_more_impl , 5) -SOX_MESSAGE_FUNCTION(lsx_debug_most_impl , 6) - -#undef SOX_MESSAGE_FUNCTION - -int sox_init(void) -{ - return lsx_effects_init(); -} - -int sox_quit(void) -{ - #ifndef __FREEDV__ - sox_format_quit(); - #endif - return lsx_effects_quit(); -} diff --git a/freedv/branches/1.2/src/sox/sox.h b/freedv/branches/1.2/src/sox/sox.h deleted file mode 100644 index 05372558..00000000 --- a/freedv/branches/1.2/src/sox/sox.h +++ /dev/null @@ -1,2608 +0,0 @@ -/* libSoX Library Public Interface - * - * Copyright 1999-2011 Chris Bagwell and SoX Contributors. - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Chris Bagwell And SoX Contributors are not responsible for - * the consequences of using this software. - */ - -/** @file -Contains the interface exposed to clients of the libSoX library. -Symbols starting with "sox_" or "SOX_" are part of the public interface for -libSoX clients (applications that consume libSoX). Symbols starting with -"lsx_" or "LSX_" are internal use by libSoX and plugins. -LSX_ and lsx_ symbols should not be used by libSoX-based applications. -*/ - -#ifndef SOX_H -#define SOX_H /**< Client API: This macro is defined if sox.h has been included. */ - -#include -#include -#include - -#if defined(__cplusplus) -extern "C" { -#endif - -/* Suppress warnings from use of type long long. */ -#if defined __GNUC__ -#pragma GCC system_header -#endif - -/***************************************************************************** -API decoration macros: -Mostly for documentation purposes. For some compilers, decorations also affect -code generation, influence compiler warnings or activate compiler -optimizations. -*****************************************************************************/ - -/** -Plugins API: -Attribute required on all functions exported by libSoX and on all function -pointer types used by the libSoX API. -*/ -#ifdef __GNUC__ -#define LSX_API __attribute__ ((cdecl)) /* libSoX function */ -#elif _MSC_VER -#define LSX_API __cdecl /* libSoX function */ -#else -#define LSX_API /* libSoX function */ -#endif - -/** -Plugins API: -Attribute applied to a parameter or local variable to suppress warnings about -the variable being unused (especially in macro-generated code). -*/ -#ifdef __GNUC__ -#define LSX_UNUSED __attribute__ ((unused)) /* Parameter or local variable is intentionally unused. */ -#else -#define LSX_UNUSED /* Parameter or local variable is intentionally unused. */ -#endif - -/** -Plugins API: -LSX_PRINTF12: Attribute applied to a function to indicate that it requires -a printf-style format string for arg1 and that printf parameters start at -arg2. -*/ -#ifdef __GNUC__ -#define LSX_PRINTF12 __attribute__ ((format (printf, 1, 2))) /* Function has printf-style arguments. */ -#else -#define LSX_PRINTF12 /* Function has printf-style arguments. */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that it has no side effects and -depends only its input parameters and global memory. If called repeatedly, it -returns the same result each time. -*/ -#ifdef __GNUC__ -#define LSX_RETURN_PURE __attribute__ ((pure)) /* Function is pure. */ -#else -#define LSX_RETURN_PURE /* Function is pure. */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the -return value is always a pointer to a valid object (never NULL). -*/ -#ifdef _Ret_ -#define LSX_RETURN_VALID _Ret_ /* Function always returns a valid object (never NULL). */ -#else -#define LSX_RETURN_VALID /* Function always returns a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the return value is always a -pointer to a valid array (never NULL). -*/ -#ifdef _Ret_valid_ -#define LSX_RETURN_ARRAY _Ret_valid_ /* Function always returns a valid array (never NULL). */ -#else -#define LSX_RETURN_ARRAY /* Function always returns a valid array (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the return value is always a -pointer to a valid 0-terminated array (never NULL). -*/ -#ifdef _Ret_z_ -#define LSX_RETURN_VALID_Z _Ret_z_ /* Function always returns a 0-terminated array (never NULL). */ -#else -#define LSX_RETURN_VALID_Z /* Function always returns a 0-terminated array (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the returned pointer may be -null. -*/ -#ifdef _Ret_opt_ -#define LSX_RETURN_OPT _Ret_opt_ /* Function may return NULL. */ -#else -#define LSX_RETURN_OPT /* Function may return NULL. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to one const element of the pointed-to type (never NULL). -*/ -#ifdef _In_ -#define LSX_PARAM_IN _In_ /* Required const pointer to a valid object (never NULL). */ -#else -#define LSX_PARAM_IN /* Required const pointer to a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to a const 0-terminated string (never NULL). -*/ -#ifdef _In_z_ -#define LSX_PARAM_IN_Z _In_z_ /* Required const pointer to 0-terminated string (never NULL). */ -#else -#define LSX_PARAM_IN_Z /* Required const pointer to 0-terminated string (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a const -pointer to a 0-terminated printf format string. -*/ -#ifdef _Printf_format_string_ -#define LSX_PARAM_IN_PRINTF _Printf_format_string_ /* Required const pointer to 0-terminated printf format string (never NULL). */ -#else -#define LSX_PARAM_IN_PRINTF /* Required const pointer to 0-terminated printf format string (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) const initialized elements of the pointed-to type, where -(len) is the name of another parameter. -@param len The parameter that contains the number of elements in the array. -*/ -#ifdef _In_count_ -#define LSX_PARAM_IN_COUNT(len) _In_count_(len) /* Required const pointer to (len) valid objects (never NULL). */ -#else -#define LSX_PARAM_IN_COUNT(len) /* Required const pointer to (len) valid objects (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) const bytes of initialized data, where (len) is the name of -another parameter. -@param len The parameter that contains the number of bytes in the array. -*/ -#ifdef _In_bytecount_ -#define LSX_PARAM_IN_BYTECOUNT(len) _In_bytecount_(len) /* Required const pointer to (len) bytes of data (never NULL). */ -#else -#define LSX_PARAM_IN_BYTECOUNT(len) /* Required const pointer to (len) bytes of data (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to one const element of the pointed-to type. -*/ -#ifdef _In_opt_ -#define LSX_PARAM_IN_OPT _In_opt_ /* Optional const pointer to a valid object (may be NULL). */ -#else -#define LSX_PARAM_IN_OPT /* Optional const pointer to a valid object (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to a const 0-terminated string. -*/ -#ifdef _In_opt_z_ -#define LSX_PARAM_IN_OPT_Z _In_opt_z_ /* Optional const pointer to 0-terminated string (may be NULL). */ -#else -#define LSX_PARAM_IN_OPT_Z /* Optional const pointer to 0-terminated string (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to one initialized element of the pointed-to type (never NULL). The -function may modify the element. -*/ -#ifdef _Inout_ -#define LSX_PARAM_INOUT _Inout_ /* Required pointer to a valid object (never NULL). */ -#else -#define LSX_PARAM_INOUT /* Required pointer to a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) initialized elements of the pointed-to type (never NULL). The -function may modify the elements. -@param len The parameter that contains the number of elements in the array. -*/ -#ifdef _Inout_count_x_ -#define LSX_PARAM_INOUT_COUNT(len) _Inout_count_x_(len) /* Required pointer to (len) valid objects (never NULL). */ -#else -#define LSX_PARAM_INOUT_COUNT(len) /* Required pointer to (len) valid objects (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for one element of the pointed-to type (never -NULL). The function will initialize the element. -*/ -#ifdef _Out_ -#define LSX_PARAM_OUT _Out_ /* Required pointer to an object to be initialized (never NULL). */ -#else -#define LSX_PARAM_OUT /* Required pointer to an object to be initialized (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) bytes of data (never NULL), where (len) -is the name of another parameter. The function may write up to len bytes of -data to this memory. -@param len The parameter that contains the number of bytes in the array. -*/ -#ifdef _Out_bytecap_ -#define LSX_PARAM_OUT_BYTECAP(len) _Out_bytecap_(len) /* Required pointer to writable buffer with room for len bytes. */ -#else -#define LSX_PARAM_OUT_BYTECAP(len) /* Required pointer to writable buffer with room for len bytes. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) elements of the pointed-to type (never -NULL), where (len) is the name of another parameter. On return, (filled) -elements will have been initialized, where (filled) is either the dereference -of another pointer parameter (for example "*written") or the "return" -parameter (indicating that the function returns the number of elements -written). -@param len The parameter that contains the number of elements in the array. -@param filled The dereference of the parameter that receives the number of elements written to the array, or "return" if the value is returned. -*/ -#ifdef _Out_cap_post_count_ -#define LSX_PARAM_OUT_CAP_POST_COUNT(len,filled) _Out_cap_post_count_(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled) elements will have been initialized. */ -#else -#define LSX_PARAM_OUT_CAP_POST_COUNT(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled) elements will have been initialized. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) elements of the pointed-to type (never -NULL), where (len) is the name of another parameter. On return, (filled+1) -elements will have been initialized, with the last element having been -initialized to 0, where (filled) is either the dereference of another pointer -parameter (for example, "*written") or the "return" parameter (indicating that -the function returns the number of elements written). -@param len The parameter that contains the number of elements in the array. -@param filled The dereference of the parameter that receives the number of elements written to the array (not counting the terminating null), or "return" if the value is returned. -*/ -#ifdef _Out_z_cap_post_count_ -#define LSX_PARAM_OUT_Z_CAP_POST_COUNT(len,filled) _Out_z_cap_post_count_(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled+1) elements will have been initialized, and the array will be 0-terminated. */ -#else -#define LSX_PARAM_OUT_Z_CAP_POST_COUNT(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled+1) elements will have been initialized, and the array will be 0-terminated. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to memory sufficient for one element of the pointed-to -type. The function will initialize the element. -*/ -#ifdef _Out_opt_ -#define LSX_PARAM_OUT_OPT _Out_opt_ /* Optional pointer to an object to be initialized (may be NULL). */ -#else -#define LSX_PARAM_OUT_OPT /* Optional pointer to an object to be initialized (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which may be NULL when the function is -invoked. -*/ -#ifdef _Deref_pre_maybenull_ -#define LSX_PARAM_DEREF_PRE_MAYBENULL _Deref_pre_maybenull_ /* Required pointer (never NULL) to another pointer (may be NULL). */ -#else -#define LSX_PARAM_DEREF_PRE_MAYBENULL /* Required pointer (never NULL) to another pointer (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which will be NULL when the function -returns. -*/ -#ifdef _Deref_post_null_ -#define LSX_PARAM_DEREF_POST_NULL _Deref_post_null_ /* Required pointer (never NULL) to another pointer, which will be NULL on exit. */ -#else -#define LSX_PARAM_DEREF_POST_NULL /* Required pointer (never NULL) to another pointer, which will be NULL on exit. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which will be non-NULL when the -function returns. -*/ -#ifdef _Deref_post_notnull_ -#define LSX_PARAM_DEREF_POST_NOTNULL _Deref_post_notnull_ /* Required pointer (never NULL) to another pointer, which will be valid (not NULL) on exit. */ -#else -#define LSX_PARAM_DEREF_POST_NOTNULL /* Required pointer (never NULL) to another pointer, which will be valid (not NULL) on exit. */ -#endif - -/** -Plugins API: -Expression that "uses" a potentially-unused variable to avoid compiler -warnings (especially in macro-generated code). -*/ -#ifdef _PREFAST_ -#define LSX_USE_VAR(x) ((void)(x=0)) /* During static analysis, initialize unused variables to 0. */ -#else -#define LSX_USE_VAR(x) ((void)(x)) /* Parameter or variable is intentionally unused. */ -#endif - -/** -Plugins API: -Compile-time assertion. Causes a compile error if the expression is false. -@param e The expression to test. If expression is false, compilation will fail. -@param f A unique identifier for the test, for example foo_must_not_be_zero. -*/ -#define lsx_static_assert(e,f) enum {lsx_static_assert_##f = 1/((e) ? 1 : 0)} - -/***************************************************************************** -Basic typedefs: -*****************************************************************************/ - -/** -Client API: -Signed twos-complement 8-bit type. Typically defined as signed char. -*/ -#if SCHAR_MAX==127 && SCHAR_MIN==(-128) -typedef signed char sox_int8_t; -#elif CHAR_MAX==127 && CHAR_MIN==(-128) -typedef char sox_int8_t; -#else -#error Unable to determine an appropriate definition for sox_int8_t. -#endif - -/** -Client API: -Unsigned 8-bit type. Typically defined as unsigned char. -*/ -#if UCHAR_MAX==0xff -typedef unsigned char sox_uint8_t; -#elif CHAR_MAX==0xff && CHAR_MIN==0 -typedef char sox_uint8_t; -#else -#error Unable to determine an appropriate definition for sox_uint8_t. -#endif - -/** -Client API: -Signed twos-complement 16-bit type. Typically defined as short. -*/ -#if SHRT_MAX==32767 && SHRT_MIN==(-32768) -typedef short sox_int16_t; -#elif INT_MAX==32767 && INT_MIN==(-32768) -typedef int sox_int16_t; -#else -#error Unable to determine an appropriate definition for sox_int16_t. -#endif - -/** -Client API: -Unsigned 16-bit type. Typically defined as unsigned short. -*/ -#if USHRT_MAX==0xffff -typedef unsigned short sox_uint16_t; -#elif UINT_MAX==0xffff -typedef unsigned int sox_uint16_t; -#else -#error Unable to determine an appropriate definition for sox_uint16_t. -#endif - -/** -Client API: -Signed twos-complement 32-bit type. Typically defined as int. -*/ -#if INT_MAX==2147483647 && INT_MIN==(-2147483647-1) -typedef int sox_int32_t; -#elif LONG_MAX==2147483647 && LONG_MIN==(-2147483647-1) -typedef long sox_int32_t; -#else -#error Unable to determine an appropriate definition for sox_int32_t. -#endif - -/** -Client API: -Unsigned 32-bit type. Typically defined as unsigned int. -*/ -#if UINT_MAX==0xffffffff -typedef unsigned int sox_uint32_t; -#elif ULONG_MAX==0xffffffff -typedef unsigned long sox_uint32_t; -#else -#error Unable to determine an appropriate definition for sox_uint32_t. -#endif - -/** -Client API: -Signed twos-complement 64-bit type. Typically defined as long or long long. -*/ -#if LONG_MAX==9223372036854775807 && LONG_MIN==(-9223372036854775807-1) -typedef long sox_int64_t; -#elif defined(_MSC_VER) -typedef __int64 sox_int64_t; -#else -typedef long long sox_int64_t; -#endif - -/** -Client API: -Unsigned 64-bit type. Typically defined as unsigned long or unsigned long long. -*/ -#if ULONG_MAX==0xffffffffffffffff -typedef unsigned long sox_uint64_t; -#elif defined(_MSC_VER) -typedef unsigned __int64 sox_uint64_t; -#else -typedef unsigned long long sox_uint64_t; -#endif - -#ifndef _DOXYGEN_ -lsx_static_assert(sizeof(sox_int8_t)==1, sox_int8_size); -lsx_static_assert(sizeof(sox_uint8_t)==1, sox_uint8_size); -lsx_static_assert(sizeof(sox_int16_t)==2, sox_int16_size); -lsx_static_assert(sizeof(sox_uint16_t)==2, sox_uint16_size); -lsx_static_assert(sizeof(sox_int32_t)==4, sox_int32_size); -lsx_static_assert(sizeof(sox_uint32_t)==4, sox_uint32_size); -lsx_static_assert(sizeof(sox_int64_t)==8, sox_int64_size); -lsx_static_assert(sizeof(sox_uint64_t)==8, sox_uint64_size); -#endif - -/** -Client API: -Alias for sox_int32_t (beware of the extra byte). -*/ -typedef sox_int32_t sox_int24_t; - -/** -Client API: -Alias for sox_uint32_t (beware of the extra byte). -*/ -typedef sox_uint32_t sox_uint24_t; - -/** -Client API: -Native SoX audio sample type (alias for sox_int32_t). -*/ -typedef sox_int32_t sox_sample_t; - -/** -Client API: -Samples per second is stored as a double. -*/ -typedef double sox_rate_t; - -/** -Client API: -File's metadata, access via sox_*_comments functions. -*/ -typedef char * * sox_comments_t; - -/***************************************************************************** -Enumerations: -*****************************************************************************/ - -/** -Client API: -Boolean type, assignment (but not necessarily binary) compatible with C++ bool. -*/ -typedef enum sox_bool { - sox_false, /**< False = 0. */ - sox_true /**< True = 1. */ -} sox_bool; - -/** -Client API: -no, yes, or default (default usually implies some kind of auto-detect logic). -*/ -typedef enum sox_option_t { - sox_option_no, /**< Option specified as no = 0. */ - sox_option_yes, /**< Option specified as yes = 1. */ - sox_option_default /**< Option unspecified = 2. */ -} sox_option_t; - -/** -Client API: -The libSoX-specific error codes. -libSoX functions may return these codes or others that map from errno codes. -*/ -enum sox_error_t { - SOX_SUCCESS = 0, /**< Function succeeded = 0 */ - SOX_EOF = -1, /**< End Of File or other error = -1 */ - SOX_EHDR = 2000, /**< Invalid Audio Header = 2000 */ - SOX_EFMT, /**< Unsupported data format = 2001 */ - SOX_ENOMEM, /**< Can't alloc memory = 2002 */ - SOX_EPERM, /**< Operation not permitted = 2003 */ - SOX_ENOTSUP, /**< Operation not supported = 2004 */ - SOX_EINVAL /**< Invalid argument = 2005 */ -}; - -/** -Client API: -Flags indicating whether optional features are present in this build of libSoX. -*/ -typedef enum sox_version_flags_t { - sox_version_none = 0, /**< No special features = 0. */ - sox_version_have_popen = 1, /**< popen = 1. */ - sox_version_have_magic = 2, /**< magic = 2. */ - sox_version_have_threads = 4, /**< threads = 4. */ - sox_version_have_memopen = 8 /**< memopen = 8. */ -} sox_version_flags_t; - -/** -Client API: -Format of sample data. -*/ -typedef enum sox_encoding_t { - SOX_ENCODING_UNKNOWN , /**< encoding has not yet been determined */ - - SOX_ENCODING_SIGN2 , /**< signed linear 2's comp: Mac */ - SOX_ENCODING_UNSIGNED , /**< unsigned linear: Sound Blaster */ - SOX_ENCODING_FLOAT , /**< floating point (binary format) */ - SOX_ENCODING_FLOAT_TEXT, /**< floating point (text format) */ - SOX_ENCODING_FLAC , /**< FLAC compression */ - SOX_ENCODING_HCOM , /**< Mac FSSD files with Huffman compression */ - SOX_ENCODING_WAVPACK , /**< WavPack with integer samples */ - SOX_ENCODING_WAVPACKF , /**< WavPack with float samples */ - SOX_ENCODING_ULAW , /**< u-law signed logs: US telephony, SPARC */ - SOX_ENCODING_ALAW , /**< A-law signed logs: non-US telephony, Psion */ - SOX_ENCODING_G721 , /**< G.721 4-bit ADPCM */ - SOX_ENCODING_G723 , /**< G.723 3 or 5 bit ADPCM */ - SOX_ENCODING_CL_ADPCM , /**< Creative Labs 8 --> 2,3,4 bit Compressed PCM */ - SOX_ENCODING_CL_ADPCM16, /**< Creative Labs 16 --> 4 bit Compressed PCM */ - SOX_ENCODING_MS_ADPCM , /**< Microsoft Compressed PCM */ - SOX_ENCODING_IMA_ADPCM , /**< IMA Compressed PCM */ - SOX_ENCODING_OKI_ADPCM , /**< Dialogic/OKI Compressed PCM */ - SOX_ENCODING_DPCM , /**< Differential PCM: Fasttracker 2 (xi) */ - SOX_ENCODING_DWVW , /**< Delta Width Variable Word */ - SOX_ENCODING_DWVWN , /**< Delta Width Variable Word N-bit */ - SOX_ENCODING_GSM , /**< GSM 6.10 33byte frame lossy compression */ - SOX_ENCODING_MP3 , /**< MP3 compression */ - SOX_ENCODING_VORBIS , /**< Vorbis compression */ - SOX_ENCODING_AMR_WB , /**< AMR-WB compression */ - SOX_ENCODING_AMR_NB , /**< AMR-NB compression */ - SOX_ENCODING_CVSD , /**< Continuously Variable Slope Delta modulation */ - SOX_ENCODING_LPC10 , /**< Linear Predictive Coding */ - - SOX_ENCODINGS /**< End of list marker */ -} sox_encoding_t; - -/** -Client API: -Flags for sox_encodings_info_t: lossless/lossy1/lossy2. -*/ -typedef enum sox_encodings_flags_t { - sox_encodings_none = 0, /**< no flags specified (implies lossless encoding) = 0. */ - sox_encodings_lossy1 = 1, /**< encode, decode: lossy once = 1. */ - sox_encodings_lossy2 = 2 /**< encode, decode, encode, decode: lossy twice = 2. */ -} sox_encodings_flags_t; - -/** -Client API: -Type of plot. -*/ -typedef enum sox_plot_t { - sox_plot_off, /**< No plot = 0. */ - sox_plot_octave, /**< Octave plot = 1. */ - sox_plot_gnuplot, /**< Gnuplot plot = 2. */ - sox_plot_data /**< Plot data = 3. */ -} sox_plot_t; - -/** -Client API: -Loop modes: upper 4 bits mask the loop blass, lower 4 bits describe -the loop behaviour, for example single shot, bidirectional etc. -*/ -enum sox_loop_flags_t { - sox_loop_none = 0, /**< single-shot = 0 */ - sox_loop_forward = 1, /**< forward loop = 1 */ - sox_loop_forward_back = 2, /**< forward/back loop = 2 */ - sox_loop_8 = 32, /**< 8 loops (??) = 32 */ - sox_loop_sustain_decay = 64 /**< AIFF style, one sustain & one decay loop = 64 */ -}; - -/** -Plugins API: -Is file a real file, a pipe, or a url? -*/ -typedef enum lsx_io_type -{ - lsx_io_file, /**< File is a real file = 0. */ - lsx_io_pipe, /**< File is a pipe (no seeking) = 1. */ - lsx_io_url /**< File is a URL (no seeking) = 2. */ -} lsx_io_type; - -/***************************************************************************** -Macros: -*****************************************************************************/ - -/** -Client API: -Compute a 32-bit integer API version from three 8-bit parts. -@param a Major version. -@param b Minor version. -@param c Revision or build number. -@returns 32-bit integer API version 0x000a0b0c. -*/ -#define SOX_LIB_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) - -/** -Client API: -The API version of the sox.h file. It is not meant to follow the version -number of SoX but it has historically. Please do not count on -SOX_LIB_VERSION_CODE staying in sync with the libSoX version. -*/ -#define SOX_LIB_VERSION_CODE SOX_LIB_VERSION(14, 4, 1) - -/** -Client API: -Returns the smallest (negative) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer; -for example, SOX_INT_MIN(8) = 0x80, SOX_INT_MIN(16) = 0x8000, etc. -@param bits Size of value for which to calculate minimum. -@returns the smallest (negative) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer. -*/ -#define SOX_INT_MIN(bits) (1 <<((bits)-1)) - -/** -Client API: -Returns the largest (positive) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer; -for example, SOX_INT_MAX(8) = 0x7F, SOX_INT_MAX(16) = 0x7FFF, etc. -@param bits Size of value for which to calculate maximum. -@returns the largest (positive) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer. -*/ -#define SOX_INT_MAX(bits) (((unsigned)-1)>>(33-(bits))) - -/** -Client API: -Returns the largest value storable in an unsigned integer with the specified -number of bits; for example, SOX_UINT_MAX(8) = 0xFF, -SOX_UINT_MAX(16) = 0xFFFF, etc. -@param bits Size of value for which to calculate maximum. -@returns the largest value storable in an unsigned integer with the specified -number of bits. -*/ -#define SOX_UINT_MAX(bits) (SOX_INT_MIN(bits)|SOX_INT_MAX(bits)) - -/** -Client API: -Returns 0x7F. -*/ -#define SOX_INT8_MAX SOX_INT_MAX(8) - -/** -Client API: -Returns 0x7FFF. -*/ -#define SOX_INT16_MAX SOX_INT_MAX(16) - -/** -Client API: -Returns 0x7FFFFF. -*/ -#define SOX_INT24_MAX SOX_INT_MAX(24) - -/** -Client API: -Returns 0x7FFFFFFF. -*/ -#define SOX_INT32_MAX SOX_INT_MAX(32) - -/** -Client API: -Bits in a sox_sample_t = 32. -*/ -#define SOX_SAMPLE_PRECISION 32 - -/** -Client API: -Max value for sox_sample_t = 0x7FFFFFFF. -*/ -#define SOX_SAMPLE_MAX (sox_sample_t)SOX_INT_MAX(32) - -/** -Client API: -Min value for sox_sample_t = 0x80000000. -*/ -#define SOX_SAMPLE_MIN (sox_sample_t)SOX_INT_MIN(32) - - -/* Conversions: Linear PCM <--> sox_sample_t - * - * I/O Input sox_sample_t Clips? Input sox_sample_t Clips? - * Format Minimum Minimum I O Maximum Maximum I O - * ------ --------- ------------ -- -- -------- ------------ -- -- - * Float -inf -1 y n inf 1 - 5e-10 y n - * Int8 -128 -128 n n 127 127.9999999 n y - * Int16 -32768 -32768 n n 32767 32767.99998 n y - * Int24 -8388608 -8388608 n n 8388607 8388607.996 n y - * Int32 -2147483648 -2147483648 n n 2147483647 2147483647 n n - * - * Conversions are as accurate as possible (with rounding). - * - * Rounding: halves toward +inf, all others to nearest integer. - * - * Clips? shows whether on not there is the possibility of a conversion - * clipping to the minimum or maximum value when inputing from or outputing - * to a given type. - * - * Unsigned integers are converted to and from signed integers by flipping - * the upper-most bit then treating them as signed integers. - */ - -/** -Client API: -Declares the temporary local variables that are required when using SOX -conversion macros. -*/ -#define SOX_SAMPLE_LOCALS sox_sample_t sox_macro_temp_sample LSX_UNUSED; \ - double sox_macro_temp_double LSX_UNUSED - -/** -Client API: -Sign bit for sox_sample_t = 0x80000000. -*/ -#define SOX_SAMPLE_NEG SOX_INT_MIN(32) - -/** -Client API: -Converts sox_sample_t to an unsigned integer of width (bits). -@param bits Width of resulting sample (1 through 32). -@param d Input sample to be converted. -@param clips Variable that is incremented if the result is too big. -@returns Unsigned integer of width (bits). -*/ -#define SOX_SAMPLE_TO_UNSIGNED(bits,d,clips) \ - (sox_uint##bits##_t)(SOX_SAMPLE_TO_SIGNED(bits,d,clips)^SOX_INT_MIN(bits)) - -/** -Client API: -Converts sox_sample_t to a signed integer of width (bits). -@param bits Width of resulting sample (1 through 32). -@param d Input sample to be converted. -@param clips Variable that is incremented if the result is too big. -@returns Signed integer of width (bits). -*/ -#define SOX_SAMPLE_TO_SIGNED(bits,d,clips) \ - (sox_int##bits##_t)(LSX_USE_VAR(sox_macro_temp_double),sox_macro_temp_sample=(d),sox_macro_temp_sample>SOX_SAMPLE_MAX-(1<<(31-bits))?++(clips),SOX_INT_MAX(bits):((sox_uint32_t)(sox_macro_temp_sample+(1<<(31-bits))))>>(32-bits)) - -/** -Client API: -Converts signed integer of width (bits) to sox_sample_t. -@param bits Width of input sample (1 through 32). -@param d Input sample to be converted. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_TO_SAMPLE(bits,d)((sox_sample_t)(d)<<(32-bits)) - -/** -Client API: -Converts unsigned integer of width (bits) to sox_sample_t. -@param bits Width of input sample (1 through 32). -@param d Input sample to be converted. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_TO_SAMPLE(bits,d)(SOX_SIGNED_TO_SAMPLE(bits,d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts unsigned 8-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_8BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(8,d) - -/** -Client API: -Converts signed 8-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_8BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(8,d) - -/** -Client API: -Converts unsigned 16-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_16BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(16,d) - -/** -Client API: -Converts signed 16-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_16BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(16,d) - -/** -Client API: -Converts unsigned 24-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_24BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(24,d) - -/** -Client API: -Converts signed 24-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_24BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(24,d) - -/** -Client API: -Converts unsigned 32-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_32BIT_TO_SAMPLE(d,clips) ((sox_sample_t)(d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts signed 32-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_32BIT_TO_SAMPLE(d,clips) (sox_sample_t)(d) - -/** -Client API: -Converts 32-bit float to sox_sample_t. -@param d Input sample to be converted, range [-1, 1). -@param clips Variable to increment if the input sample is too large or too small. -@returns SoX native sample value. -*/ -#define SOX_FLOAT_32BIT_TO_SAMPLE(d,clips) (sox_sample_t)(LSX_USE_VAR(sox_macro_temp_sample),sox_macro_temp_double=(d)*(SOX_SAMPLE_MAX+1.),sox_macro_temp_double=SOX_SAMPLE_MAX+1.?sox_macro_temp_double>SOX_SAMPLE_MAX+1.?++(clips),SOX_SAMPLE_MAX:SOX_SAMPLE_MAX:sox_macro_temp_double) - -/** -Client API: -Converts 64-bit float to sox_sample_t. -@param d Input sample to be converted, range [-1, 1). -@param clips Variable to increment if the input sample is too large or too small. -@returns SoX native sample value. -*/ -#define SOX_FLOAT_64BIT_TO_SAMPLE(d,clips) (sox_sample_t)(LSX_USE_VAR(sox_macro_temp_sample),sox_macro_temp_double=(d)*(SOX_SAMPLE_MAX+1.),sox_macro_temp_double<0?sox_macro_temp_double<=SOX_SAMPLE_MIN-.5?++(clips),SOX_SAMPLE_MIN:sox_macro_temp_double-.5:sox_macro_temp_double>=SOX_SAMPLE_MAX+.5?sox_macro_temp_double>SOX_SAMPLE_MAX+1.?++(clips),SOX_SAMPLE_MAX:SOX_SAMPLE_MAX:sox_macro_temp_double+.5) - -/** -Client API: -Converts SoX native sample to an unsigned 8-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_8BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(8,d,clips) - -/** -Client API: -Converts SoX native sample to an signed 8-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_8BIT(d,clips) SOX_SAMPLE_TO_SIGNED(8,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 16-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_16BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(16,d,clips) - -/** -Client API: -Converts SoX native sample to a signed 16-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_16BIT(d,clips) SOX_SAMPLE_TO_SIGNED(16,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 24-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_24BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(24,d,clips) - -/** -Client API: -Converts SoX native sample to a signed 24-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_24BIT(d,clips) SOX_SAMPLE_TO_SIGNED(24,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 32-bit integer. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_32BIT(d,clips) (sox_uint32_t)((d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts SoX native sample to a signed 32-bit integer. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_SIGNED_32BIT(d,clips) (sox_int32_t)(d) - -/** -Client API: -Converts SoX native sample to a 32-bit float. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_FLOAT_32BIT(d,clips) (LSX_USE_VAR(sox_macro_temp_double),sox_macro_temp_sample=(d),sox_macro_temp_sample>SOX_SAMPLE_MAX-128?++(clips),1:(((sox_macro_temp_sample+128)&~255)*(1./(SOX_SAMPLE_MAX+1.)))) - -/** -Client API: -Converts SoX native sample to a 64-bit float. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_FLOAT_64BIT(d,clips) ((d)*(1./(SOX_SAMPLE_MAX+1.))) - -/** -Client API: -Clips a value of a type that is larger then sox_sample_t (for example, int64) -to sox_sample_t's limits and increment a counter if clipping occurs. -@param samp Value (lvalue) to be clipped, updated as necessary. -@param clips Value (lvalue) that is incremented if clipping is needed. -*/ -#define SOX_SAMPLE_CLIP_COUNT(samp, clips) \ - do { \ - if (samp > SOX_SAMPLE_MAX) \ - { samp = SOX_SAMPLE_MAX; clips++; } \ - else if (samp < SOX_SAMPLE_MIN) \ - { samp = SOX_SAMPLE_MIN; clips++; } \ - } while (0) - -/** -Client API: -Clips a value of a type that is larger then sox_sample_t (for example, int64) -to sox_sample_t's limits and increment a counter if clipping occurs. -@param d Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_ROUND_CLIP_COUNT(d, clips) \ - ((d) < 0? (d) <= SOX_SAMPLE_MIN - 0.5? ++(clips), SOX_SAMPLE_MIN: (d) - 0.5 \ - : (d) >= SOX_SAMPLE_MAX + 0.5? ++(clips), SOX_SAMPLE_MAX: (d) + 0.5) - -/** -Client API: -Clips a value to the limits of a signed integer of the specified width -and increment a counter if clipping occurs. -@param bits Width (in bits) of target integer type. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_INTEGER_CLIP_COUNT(bits,i,clips) ( \ - (i) >(1 << ((bits)-1))- 1? ++(clips),(1 << ((bits)-1))- 1 : \ - (i) <-1 << ((bits)-1) ? ++(clips),-1 << ((bits)-1) : (i)) - -/** -Client API: -Clips a value to the limits of a 16-bit signed integer and increment a counter -if clipping occurs. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_16BIT_CLIP_COUNT(i,clips) SOX_INTEGER_CLIP_COUNT(16,i,clips) - -/** -Client API: -Clips a value to the limits of a 24-bit signed integer and increment a counter -if clipping occurs. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_24BIT_CLIP_COUNT(i,clips) SOX_INTEGER_CLIP_COUNT(24,i,clips) - -#define SOX_SIZE_MAX ((size_t)(-1)) /**< Client API: Maximum value of size_t. */ - -#define SOX_UNSPEC 0 /**< Client API: Members of sox_signalinfo_t are set to SOX_UNSPEC (= 0) if the actual value is not yet known. */ -#define SOX_UNKNOWN_LEN (sox_uint64_t)(-1) /**< Client API: sox_signalinfo_t.length is set to SOX_UNKNOWN_LEN (= -1) within the effects chain if the actual length is not known. Format handlers currently use SOX_UNSPEC instead. */ -#define SOX_IGNORE_LENGTH (sox_uint64_t)(-2) /**< Client API: sox_signalinfo_t.length is set to SOX_IGNORE_LENGTH (= -2) to indicate that a format handler should ignore length information in file headers. */ - -#define SOX_DEFAULT_CHANNELS 2 /**< Client API: Default channel count is 2 (stereo). */ -#define SOX_DEFAULT_RATE 48000 /**< Client API: Default rate is 48000Hz. */ -#define SOX_DEFAULT_PRECISION 16 /**< Client API: Default precision is 16 bits per sample. */ -#define SOX_DEFAULT_ENCODING SOX_ENCODING_SIGN2 /**< Client API: Default encoding is SIGN2 (linear 2's complement PCM). */ - -#define SOX_LOOP_NONE ((unsigned char)sox_loop_none) /**< Client API: single-shot = 0 */ -#define SOX_LOOP_8 ((unsigned char)sox_loop_8) /**< Client API: 8 loops = 32 */ -#define SOX_LOOP_SUSTAIN_DECAY ((unsigned char)sox_loop_sustain_decay) /**< Client API: AIFF style, one sustain & one decay loop = 64 */ - -#define SOX_MAX_NLOOPS 8 /**< Client API: Maximum number of loops supported by sox_oob_t = 8. */ - -#define SOX_FILE_NOSTDIO 0x0001 /**< Client API: Does not use stdio routines */ -#define SOX_FILE_DEVICE 0x0002 /**< Client API: File is an audio device */ -#define SOX_FILE_PHONY 0x0004 /**< Client API: Phony file/device (for example /dev/null) */ -#define SOX_FILE_REWIND 0x0008 /**< Client API: File should be rewound to write header */ -#define SOX_FILE_BIT_REV 0x0010 /**< Client API: Is file bit-reversed? */ -#define SOX_FILE_NIB_REV 0x0020 /**< Client API: Is file nibble-reversed? */ -#define SOX_FILE_ENDIAN 0x0040 /**< Client API: Is file format endian? */ -#define SOX_FILE_ENDBIG 0x0080 /**< Client API: For endian file format, is it big endian? */ -#define SOX_FILE_MONO 0x0100 /**< Client API: Do channel restrictions allow mono? */ -#define SOX_FILE_STEREO 0x0200 /**< Client API: Do channel restrictions allow stereo? */ -#define SOX_FILE_QUAD 0x0400 /**< Client API: Do channel restrictions allow quad? */ - -#define SOX_FILE_CHANS (SOX_FILE_MONO | SOX_FILE_STEREO | SOX_FILE_QUAD) /**< Client API: No channel restrictions */ -#define SOX_FILE_LIT_END (SOX_FILE_ENDIAN | 0) /**< Client API: File is little-endian */ -#define SOX_FILE_BIG_END (SOX_FILE_ENDIAN | SOX_FILE_ENDBIG) /**< Client API: File is big-endian */ - -#define SOX_EFF_CHAN 1 /**< Client API: Effect might alter the number of channels */ -#define SOX_EFF_RATE 2 /**< Client API: Effect might alter sample rate */ -#define SOX_EFF_PREC 4 /**< Client API: Effect does its own calculation of output sample precision (otherwise a default value is taken, depending on the presence of SOX_EFF_MODIFY) */ -#define SOX_EFF_LENGTH 8 /**< Client API: Effect might alter audio length (as measured in time units, not necessarily in samples) */ -#define SOX_EFF_MCHAN 16 /**< Client API: Effect handles multiple channels internally */ -#define SOX_EFF_NULL 32 /**< Client API: Effect does nothing (can be optimized out of chain) */ -#define SOX_EFF_DEPRECATED 64 /**< Client API: Effect will soon be removed from SoX */ -#define SOX_EFF_GAIN 128 /**< Client API: Effect does not support gain -r */ -#define SOX_EFF_MODIFY 256 /**< Client API: Effect does not modify sample values (but might remove or duplicate samples or insert zeros) */ -#define SOX_EFF_ALPHA 512 /**< Client API: Effect is experimental/incomplete */ -#define SOX_EFF_INTERNAL 1024 /**< Client API: Effect present in libSoX but not valid for use by SoX command-line tools */ - -/** -Client API: -When used as the "whence" parameter of sox_seek, indicates that the specified -offset is relative to the beginning of the file. -*/ -#define SOX_SEEK_SET 0 - -/***************************************************************************** -Forward declarations: -*****************************************************************************/ - -typedef struct sox_format_t sox_format_t; -typedef struct sox_effect_t sox_effect_t; -typedef struct sox_effect_handler_t sox_effect_handler_t; -typedef struct sox_format_handler_t sox_format_handler_t; - -/***************************************************************************** -Function pointers: -*****************************************************************************/ - -/** -Client API: -Callback to write a message to an output device (console or log file), -used by sox_globals_t.output_message_handler. -*/ -typedef void (LSX_API * sox_output_message_handler_t)( - unsigned level, /* 1 = FAIL, 2 = WARN, 3 = INFO, 4 = DEBUG, 5 = DEBUG_MORE, 6 = DEBUG_MOST. */ - LSX_PARAM_IN_Z char const * filename, /* Source code __FILENAME__ from which message originates. */ - LSX_PARAM_IN_PRINTF char const * fmt, /* Message format string. */ - LSX_PARAM_IN va_list ap /* Message format parameters. */ - ); - -/** -Client API: -Callback to retrieve information about a format handler, -used by sox_format_tab_t.fn. -@returns format handler information. -*/ -typedef sox_format_handler_t const * (LSX_API * sox_format_fn_t)(void); - -/** -Client API: -Callback to get information about an effect handler, -used by the table returned from sox_get_effect_fns(void). -@returns Pointer to information about an effect handler. -*/ -typedef sox_effect_handler_t const * (LSX_API *sox_effect_fn_t)(void); - -/** -Client API: -Callback to initialize reader (decoder), used by -sox_format_handler.startread. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_startread)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to read (decode) a block of samples, -used by sox_format_handler.read. -@returns number of samples read, or 0 if unsuccessful. -*/ -typedef size_t (LSX_API * sox_format_handler_read)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(len,return) sox_sample_t *buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Callback to close reader (decoder), -used by sox_format_handler.stopread. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_stopread)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to initialize writer (encoder), -used by sox_format_handler.startwrite. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_startwrite)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to write (encode) a block of samples, -used by sox_format_handler.write. -@returns number of samples written, or 0 if unsuccessful. -*/ -typedef size_t (LSX_API * sox_format_handler_write)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_IN_COUNT(len) sox_sample_t const * buf, /**< Buffer to which samples are written. */ - size_t len /**< Capacity of buf, measured in samples. */ - ); - -/** -Client API: -Callback to close writer (decoder), -used by sox_format_handler.stopwrite. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_stopwrite)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to reposition reader, -used by sox_format_handler.seek. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_seek)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - sox_uint64_t offset /**< Sample offset to which reader should be positioned. */ - ); - -/** -Client API: -Callback to parse command-line arguments (called once per effect), -used by sox_effect_handler.getopts. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_getopts)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - int argc, /**< Number of arguments in argv. */ - LSX_PARAM_IN_COUNT(argc) char *argv[] /**< Array of command-line arguments. */ - ); - -/** -Client API: -Callback to initialize effect (called once per flow), -used by sox_effect_handler.start. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_start)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback to process samples, -used by sox_effect_handler.flow. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_flow)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - LSX_PARAM_IN_COUNT(*isamp) sox_sample_t const * ibuf, /**< Buffer from which to read samples. */ - LSX_PARAM_OUT_CAP_POST_COUNT(*osamp,*osamp) sox_sample_t * obuf, /**< Buffer to which samples are written. */ - LSX_PARAM_INOUT size_t *isamp, /**< On entry, contains capacity of ibuf; on exit, contains number of samples consumed. */ - LSX_PARAM_INOUT size_t *osamp /**< On entry, contains capacity of obuf; on exit, contains number of samples written. */ - ); - -/** -Client API: -Callback to finish getting output after input is complete, -used by sox_effect_handler.drain. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_drain)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(*osamp,*osamp) sox_sample_t *obuf, /**< Buffer to which samples are written. */ - LSX_PARAM_INOUT size_t *osamp /**< On entry, contains capacity of obuf; on exit, contains number of samples written. */ - ); - -/** -Client API: -Callback to shut down effect (called once per flow), -used by sox_effect_handler.stop. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_stop)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback to shut down effect (called once per effect), -used by sox_effect_handler.kill. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_kill)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback called while flow is running (called once per buffer), -used by sox_flow_effects.callback. -@returns SOX_SUCCESS to continue, other value to abort flow. -*/ -typedef int (LSX_API * sox_flow_effects_callback)( - sox_bool all_done, - void * client_data - ); - -/** -Client API: -Callback for enumerating the contents of a playlist, -used by the sox_parse_playlist function. -@returns SOX_SUCCESS if successful, any other value to abort playlist enumeration. -*/ -typedef int (LSX_API * sox_playlist_callback_t)( - void * callback_data, - LSX_PARAM_IN_Z char const * filename - ); - -/***************************************************************************** -Structures: -*****************************************************************************/ - -/** -Client API: -Information about a build of libSoX, returned from the sox_version_info -function. -*/ -typedef struct sox_version_info_t { - size_t size; /**< structure size = sizeof(sox_version_info_t) */ - sox_version_flags_t flags; /**< feature flags = popen | magic | threads | memopen */ - sox_uint32_t version_code; /**< version number = 0x140400 */ - char const * version; /**< version string = sox_version(), for example, "14.4.0" */ - char const * version_extra;/**< version extra info or null = "PACKAGE_EXTRA", for example, "beta" */ - char const * time; /**< build time = "__DATE__ __TIME__", for example, "Jan 7 2010 03:31:50" */ - char const * distro; /**< distro or null = "DISTRO", for example, "Debian" */ - char const * compiler; /**< compiler info or null, for example, "msvc 160040219" */ - char const * arch; /**< arch, for example, "1248 48 44 L OMP" */ - /* new info should be added at the end for version backwards-compatibility. */ -} sox_version_info_t; - -/** -Client API: -Global parameters (for effects & formats), returned from the sox_get_globals -function. -*/ -typedef struct sox_globals_t { -/* public: */ - unsigned verbosity; /**< messages are only written if globals.verbosity >= message.level */ - sox_output_message_handler_t output_message_handler; /**< client-specified message output callback */ - sox_bool repeatable; /**< true to use pre-determined timestamps and PRNG seed */ - - /** - Default size (in bytes) used by libSoX for blocks of sample data. - Plugins should use similarly-sized buffers to get best performance. - */ - size_t bufsiz; - - /** - Default size (in bytes) used by libSoX for blocks of input sample data. - Plugins should use similarly-sized buffers to get best performance. - */ - size_t input_bufsiz; - - sox_int32_t ranqd1; /**< Can be used to re-seed libSoX's PRNG */ - - char const * stdin_in_use_by; /**< Private: tracks the name of the handler currently using stdin */ - char const * stdout_in_use_by; /**< Private: tracks the name of the handler currently using stdout */ - char const * subsystem; /**< Private: tracks the name of the handler currently writing an output message */ - char * tmp_path; /**< Private: client-configured path to use for temporary files */ - sox_bool use_magic; /**< Private: true if client has requested use of 'magic' file-type detection */ - sox_bool use_threads; /**< Private: true if client has requested parallel effects processing */ -} sox_globals_t; - -/** -Client API: -Signal parameters; members should be set to SOX_UNSPEC (= 0) if unknown. -*/ -typedef struct sox_signalinfo_t { - sox_rate_t rate; /**< samples per second, 0 if unknown */ - unsigned channels; /**< number of sound channels, 0 if unknown */ - unsigned precision; /**< bits per sample, 0 if unknown */ - sox_uint64_t length; /**< samples * chans in file, 0 if unknown, -1 if unspecified */ - double * mult; /**< Effects headroom multiplier; may be null */ -} sox_signalinfo_t; - -/** -Client API: -Basic information about an encoding. -*/ -typedef struct sox_encodings_info_t { - sox_encodings_flags_t flags; /**< lossy once (lossy1), lossy twice (lossy2), or lossless (none). */ - char const * name; /**< encoding name. */ - char const * desc; /**< encoding description. */ -} sox_encodings_info_t; - -/** -Client API: -Encoding parameters. -*/ -typedef struct sox_encodinginfo_t { - sox_encoding_t encoding; /**< format of sample numbers */ - unsigned bits_per_sample;/**< 0 if unknown or variable; uncompressed value if lossless; compressed value if lossy */ - double compression; /**< compression factor (where applicable) */ - - /** - Should bytes be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_bytes; - - /** - Should nibbles be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_nibbles; - - /** - Should bits be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_bits; - - /** - If set to true, the format should reverse its default endianness. - */ - sox_bool opposite_endian; -} sox_encodinginfo_t; - -/** -Client API: -Looping parameters (out-of-band data). -*/ -typedef struct sox_loopinfo_t { - sox_uint64_t start; /**< first sample */ - sox_uint64_t length; /**< length */ - unsigned count; /**< number of repeats, 0=forever */ - unsigned char type; /**< 0=no, 1=forward, 2=forward/back (see sox_loop_* for valid values). */ -} sox_loopinfo_t; - -/** -Client API: -Instrument information. -*/ -typedef struct sox_instrinfo_t{ - signed char MIDInote; /**< for unity pitch playback */ - signed char MIDIlow; /**< MIDI pitch-bend low range */ - signed char MIDIhi; /**< MIDI pitch-bend high range */ - unsigned char loopmode; /**< 0=no, 1=forward, 2=forward/back (see sox_loop_* values) */ - unsigned nloops; /**< number of active loops (max SOX_MAX_NLOOPS). */ -} sox_instrinfo_t; - -/** -Client API: -File buffer info. Holds info so that data can be read in blocks. -*/ -typedef struct sox_fileinfo_t { - char *buf; /**< Pointer to data buffer */ - size_t size; /**< Size of buffer in bytes */ - size_t count; /**< Count read into buffer */ - size_t pos; /**< Position in buffer */ -} sox_fileinfo_t; - -/** -Client API: -Handler structure defined by each format. -*/ -struct sox_format_handler_t { - unsigned sox_lib_version_code; /**< Checked on load; must be 1st in struct*/ - char const * description; /**< short description of format */ - char const * const * names; /**< null-terminated array of filename extensions that are handled by this format */ - unsigned int flags; /**< File flags (SOX_FILE_* values). */ - sox_format_handler_startread startread; /**< called to initialize reader (decoder) */ - sox_format_handler_read read; /**< called to read (decode) a block of samples */ - sox_format_handler_stopread stopread; /**< called to close reader (decoder); may be null if no closing necessary */ - sox_format_handler_startwrite startwrite; /**< called to initialize writer (encoder) */ - sox_format_handler_write write; /**< called to write (encode) a block of samples */ - sox_format_handler_stopwrite stopwrite; /**< called to close writer (decoder); may be null if no closing necessary */ - sox_format_handler_seek seek; /**< called to reposition reader; may be null if not supported */ - - /** - Array of values indicating the encodings and precisions supported for - writing (encoding). Precisions specified with default precision first. - Encoding, precision, precision, ..., 0, repeat. End with one more 0. - Example: - unsigned const * formats = { - SOX_ENCODING_SIGN2, 16, 24, 0, // Support SIGN2 at 16 and 24 bits, default to 16 bits. - SOX_ENCODING_UNSIGNED, 8, 0, // Support UNSIGNED at 8 bits, default to 8 bits. - 0 // No more supported encodings. - }; - */ - unsigned const * write_formats; - - /** - Array of sample rates (samples per second) supported for writing (encoding). - NULL if all (or almost all) rates are supported. End with 0. - */ - sox_rate_t const * write_rates; - - /** - SoX will automatically allocate a buffer in which the handler can store data. - Specify the size of the buffer needed here. Usually this will be sizeof(your_struct). - The buffer will be allocated and zeroed before the call to startread/startwrite. - The buffer will be freed after the call to stopread/stopwrite. - The buffer will be provided via format.priv in each call to the handler. - */ - size_t priv_size; -}; - -/** -Client API: -Comments, instrument info, loop info (out-of-band data). -*/ -typedef struct sox_oob_t{ - /* Decoded: */ - sox_comments_t comments; /**< Comment strings in id=value format. */ - sox_instrinfo_t instr; /**< Instrument specification */ - sox_loopinfo_t loops[SOX_MAX_NLOOPS]; /**< Looping specification */ - - /* TBD: Non-decoded chunks, etc: */ -} sox_oob_t; - -/** -Client API: -Data passed to/from the format handler -*/ -struct sox_format_t { - char * filename; /**< File name */ - - /** - Signal specifications for reader (decoder) or writer (encoder): - sample rate, number of channels, precision, length, headroom multiplier. - Any info specified by the user is here on entry to startread or - startwrite. Info will be SOX_UNSPEC if the user provided no info. - At exit from startread, should be completely filled in, using - either data from the file's headers (if available) or whatever - the format is guessing/assuming (if header data is not available). - At exit from startwrite, should be completely filled in, using - either the data that was specified, or values chosen by the format - based on the format's defaults or capabilities. - */ - sox_signalinfo_t signal; - - /** - Encoding specifications for reader (decoder) or writer (encoder): - encoding (sample format), bits per sample, compression rate, endianness. - Should be filled in by startread. Values specified should be used - by startwrite when it is configuring the encoding parameters. - */ - sox_encodinginfo_t encoding; - - char * filetype; /**< Type of file, as determined by header inspection or libmagic. */ - sox_oob_t oob; /**< comments, instrument info, loop info (out-of-band data) */ - sox_bool seekable; /**< Can seek on this file */ - char mode; /**< Read or write mode ('r' or 'w') */ - sox_uint64_t olength; /**< Samples * chans written to file */ - sox_uint64_t clips; /**< Incremented if clipping occurs */ - int sox_errno; /**< Failure error code */ - char sox_errstr[256]; /**< Failure error text */ - void * fp; /**< File stream pointer */ - lsx_io_type io_type; /**< Stores whether this is a file, pipe or URL */ - sox_uint64_t tell_off; /**< Current offset within file */ - sox_uint64_t data_start; /**< Offset at which headers end and sound data begins (set by lsx_check_read_params) */ - sox_format_handler_t handler; /**< Format handler for this file */ - void * priv; /**< Format handler's private data area */ -}; - -/** -Client API: -Information about a loaded format handler, including the format name and a -function pointer that can be invoked to get additional information about the -format. -*/ -typedef struct sox_format_tab_t { - char *name; /**< Name of format handler */ - sox_format_fn_t fn; /**< Function to call to get format handler's information */ -} sox_format_tab_t; - -/** -Client API: -Global parameters for effects. -*/ -typedef struct sox_effects_globals_t { - sox_plot_t plot; /**< To help the user choose effect & options */ - sox_globals_t * global_info; /**< Pointer to associated SoX globals */ -} sox_effects_globals_t; - -/** -Client API: -Effect handler information. -*/ -struct sox_effect_handler_t { - char const * name; /**< Effect name */ - char const * usage; /**< Short explanation of parameters accepted by effect */ - unsigned int flags; /**< Combination of SOX_EFF_* flags */ - sox_effect_handler_getopts getopts; /**< Called to parse command-line arguments (called once per effect). */ - sox_effect_handler_start start; /**< Called to initialize effect (called once per flow). */ - sox_effect_handler_flow flow; /**< Called to process samples. */ - sox_effect_handler_drain drain; /**< Called to finish getting output after input is complete. */ - sox_effect_handler_stop stop; /**< Called to shut down effect (called once per flow). */ - sox_effect_handler_kill kill; /**< Called to shut down effect (called once per effect). */ - size_t priv_size; /**< Size of private data SoX should pre-allocate for effect */ -}; - -/** -Client API: -Effect information. -*/ -struct sox_effect_t { - sox_effects_globals_t * global_info; /**< global effect parameters */ - sox_signalinfo_t in_signal; /**< Information about the incoming data stream */ - sox_signalinfo_t out_signal; /**< Information about the outgoing data stream */ - sox_encodinginfo_t const * in_encoding; /**< Information about the incoming data encoding */ - sox_encodinginfo_t const * out_encoding; /**< Information about the outgoing data encoding */ - sox_effect_handler_t handler; /**< The handler for this effect */ - sox_sample_t * obuf; /**< output buffer */ - size_t obeg; /**< output buffer: start of valid data section */ - size_t oend; /**< output buffer: one past valid data section (oend-obeg is length of current content) */ - size_t imin; /**< minimum input buffer content required for calling this effect's flow function; set via lsx_effect_set_imin() */ - sox_uint64_t clips; /**< increment if clipping occurs */ - size_t flows; /**< 1 if MCHAN, number of chans otherwise */ - size_t flow; /**< flow number */ - void * priv; /**< Effect's private data area (each flow has a separate copy) */ -}; - -/** -Client API: -Chain of effects to be applied to a stream. -*/ -typedef struct sox_effects_chain_t { - sox_effect_t **effects; /**< Table of effects to be applied to a stream */ - unsigned table_size; /**< Number of entries in effects table */ - unsigned length; /**< Number of effects to be applied */ - sox_sample_t **ibufc; /**< Channel interleave buffer */ - sox_sample_t **obufc; /**< Channel interleave buffer */ - sox_effects_globals_t global_info; /**< Copy of global effects settings */ - sox_encodinginfo_t const * in_enc; /**< Input encoding */ - sox_encodinginfo_t const * out_enc; /**< Output encoding */ -} sox_effects_chain_t; - -/***************************************************************************** -Functions: -*****************************************************************************/ - -/** -Client API: -Returns version number string of libSoX, for example, "14.4.0". -@returns The version number string of libSoX, for example, "14.4.0". -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -sox_version(void); - -/** -Client API: -Returns information about this build of libsox. -@returns Pointer to a version information structure. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_version_info_t const * -LSX_API -sox_version_info(void); - -/** -Client API: -Returns a pointer to the structure with libSoX's global settings. -@returns a pointer to the structure with libSoX's global settings. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_globals_t * -LSX_API -sox_get_globals(void); - -/** -Client API: -Deprecated macro that returns the structure with libSoX's global settings -as an lvalue. -*/ -#define sox_globals (*sox_get_globals()) - -/** -Client API: -Returns a pointer to the list of available encodings. -End of list indicated by name == NULL. -@returns pointer to the list of available encodings. -*/ -LSX_RETURN_ARRAY LSX_RETURN_PURE -sox_encodings_info_t const * -LSX_API -sox_get_encodings_info(void); - -/** -Client API: -Deprecated macro that returns the list of available encodings. -End of list indicated by name == NULL. -*/ -#define sox_encodings_info (sox_get_encodings_info()) - -/** -Client API: -Fills in an encodinginfo with default values. -*/ -void -LSX_API -sox_init_encodinginfo( - LSX_PARAM_OUT sox_encodinginfo_t * e /**< Pointer to uninitialized encoding info structure to be initialized. */ - ); - -/** -Client API: -Given an encoding (for example, SIGN2) and the encoded bits_per_sample (for -example, 16), returns the number of useful bits per sample in the decoded data -(for example, 16), or returns 0 to indicate that the value returned by the -format handler should be used instead of a pre-determined precision. -@returns the number of useful bits per sample in the decoded data (for example -16), or returns 0 to indicate that the value returned by the format handler -should be used instead of a pre-determined precision. -*/ -LSX_RETURN_PURE -unsigned -LSX_API -sox_precision( - sox_encoding_t encoding, /**< Encoding for which to lookup precision information. */ - unsigned bits_per_sample /**< The number of encoded bits per sample. */ - ); - -/** -Client API: -Returns the number of items in the metadata block. -@returns the number of items in the metadata block. -*/ -size_t -LSX_API -sox_num_comments( - LSX_PARAM_IN_OPT sox_comments_t comments /**< Metadata block. */ - ); - -/** -Client API: -Adds an "id=value" item to the metadata block. -*/ -void -LSX_API -sox_append_comment( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NOTNULL sox_comments_t * comments, /**< Metadata block. */ - LSX_PARAM_IN_Z char const * item /**< Item to be added in "id=value" format. */ - ); - -/** -Client API: -Adds a newline-delimited list of "id=value" items to the metadata block. -*/ -void -LSX_API -sox_append_comments( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NOTNULL sox_comments_t * comments, /**< Metadata block. */ - LSX_PARAM_IN_Z char const * items /**< Newline-separated list of items to be added, for example "id1=value1\\nid2=value2". */ - ); - -/** -Client API: -Duplicates the metadata block. -@returns the copied metadata block. -*/ -LSX_RETURN_OPT -sox_comments_t -LSX_API -sox_copy_comments( - LSX_PARAM_IN_OPT sox_comments_t comments /**< Metadata block to copy. */ - ); - -/** -Client API: -Frees the metadata block. -*/ -void -LSX_API -sox_delete_comments( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NULL sox_comments_t * comments /**< Metadata block. */ - ); - -/** -Client API: -If "id=value" is found, return value, else return null. -@returns value, or null if value not found. -*/ -LSX_RETURN_OPT -char const * -LSX_API -sox_find_comment( - LSX_PARAM_IN_OPT sox_comments_t comments, /**< Metadata block in which to search. */ - LSX_PARAM_IN_Z char const * id /**< Id for which to search */ - ); - -/** -Client API: -Find and load format handler plugins. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_format_init(void); - -/** -Client API: -Unload format handler plugins. -*/ -void -LSX_API -sox_format_quit(void); - -/** -Client API: -Initialize effects library. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_init(void); - -/** -Client API: -Close effects library and unload format handler plugins. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_quit(void); - -/** -Client API: -Returns the table of format handler names and functions. -@returns the table of format handler names and functions. -*/ -LSX_RETURN_ARRAY LSX_RETURN_PURE -sox_format_tab_t const * -LSX_API -sox_get_format_fns(void); - -/** -Client API: -Deprecated macro that returns the table of format handler names and functions. -*/ -#define sox_format_fns (sox_get_format_fns()) - -/** -Client API: -Opens a decoding session for a file. Returned handle must be closed with sox_close(). -@returns The handle for the new session, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_read( - LSX_PARAM_IN_Z char const * path, /**< Path to file to be opened (required). */ - LSX_PARAM_IN_OPT sox_signalinfo_t const * signal, /**< Information already known about audio stream, or NULL if none. */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information already known about sample encoding, or NULL if none. */ - LSX_PARAM_IN_OPT_Z char const * filetype /**< Previously-determined file type, or NULL to auto-detect. */ - ); - -/** -Client API: -Opens a decoding session for a memory buffer. Returned handle must be closed with sox_close(). -@returns The handle for the new session, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_mem_read( - LSX_PARAM_IN_BYTECOUNT(buffer_size) void * buffer, /**< Pointer to audio data buffer (required). */ - size_t buffer_size,/**< Number of bytes to read from audio data buffer. */ - LSX_PARAM_IN_OPT sox_signalinfo_t const * signal, /**< Information already known about audio stream, or NULL if none. */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information already known about sample encoding, or NULL if none. */ - LSX_PARAM_IN_OPT_Z char const * filetype /**< Previously-determined file type, or NULL to auto-detect. */ - ); - -/** -Client API: -Returns true if the format handler for the specified file type supports the specified encoding. -@returns true if the format handler for the specified file type supports the specified encoding. -*/ -sox_bool -LSX_API -sox_format_supports_encoding( - LSX_PARAM_IN_OPT_Z char const * path, /**< Path to file to be examined (required if filetype is NULL). */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to use extension from path. */ - LSX_PARAM_IN sox_encodinginfo_t const * encoding /**< Encoding for which format handler should be queried. */ - ); - -/** -Client API: -Gets the format handler for a specified file type. -@returns The found format handler, or null if not found. -*/ -LSX_RETURN_OPT -sox_format_handler_t const * -LSX_API -sox_write_handler( - LSX_PARAM_IN_OPT_Z char const * path, /**< Path to file (required if filetype is NULL). */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Filetype for which handler is needed, or NULL to use extension from path. */ - LSX_PARAM_OUT_OPT char const * * filetype1 /**< Receives the filetype that was detected. Pass NULL if not needed. */ - ); - -/** -Client API: -Opens an encoding session for a file. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_write( - LSX_PARAM_IN_Z char const * path, /**< Path to file to be written (required). */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob, /**< Out-of-band data to add to file, or NULL if none. */ - LSX_PARAM_IN_OPT sox_bool (LSX_API * overwrite_permitted)(LSX_PARAM_IN_Z char const * filename) /**< Called if file exists to determine whether overwrite is ok. */ - ); - -/** -Client API: -Opens an encoding session for a memory buffer. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_mem_write( - LSX_PARAM_OUT_BYTECAP(buffer_size) void * buffer, /**< Pointer to audio data buffer that receives data (required). */ - LSX_PARAM_IN size_t buffer_size, /**< Maximum number of bytes to write to audio data buffer. */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob /**< Out-of-band data to add to file, or NULL if none. */ - ); - -/** -Client API: -Opens an encoding session for a memstream buffer. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_memstream_write( - LSX_PARAM_OUT char * * buffer_ptr, /**< Receives pointer to audio data buffer that receives data (required). */ - LSX_PARAM_OUT size_t * buffer_size_ptr, /**< Receives size of data written to audio data buffer (required). */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob /**< Out-of-band data to add to file, or NULL if none. */ - ); - -/** -Client API: -Reads samples from a decoding session into a sample buffer. -@returns Number of samples decoded, or 0 for EOF. -*/ -size_t -LSX_API -sox_read( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(len,return) sox_sample_t *buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Writes samples to an encoding session from a sample buffer. -@returns Number of samples encoded. -*/ -size_t -LSX_API -sox_write( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_IN_COUNT(len) sox_sample_t const * buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Closes an encoding or decoding session. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_close( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Sets the location at which next samples will be decoded. Returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_seek( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - sox_uint64_t offset, /**< Sample offset at which to position reader. */ - int whence /**< Set to SOX_SEEK_SET. */ - ); - -/** -Client API: -Finds a format handler by name. -@returns Format handler data, or null if not found. -*/ -LSX_RETURN_OPT -sox_format_handler_t const * -LSX_API -sox_find_format( - LSX_PARAM_IN_Z char const * name, /**< Name of format handler to find. */ - sox_bool ignore_devices /**< Set to true to ignore device names. */ - ); - -/** -Client API: -Returns global parameters for effects -@returns global parameters for effects. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_effects_globals_t * -LSX_API -sox_get_effects_globals(void); - -/** -Client API: -Deprecated macro that returns global parameters for effects. -*/ -#define sox_effects_globals (*sox_get_effects_globals()) - -/** -Client API: -Finds the effect handler with the given name. -@returns Effect pointer, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -sox_effect_handler_t const * -LSX_API -sox_find_effect( - LSX_PARAM_IN_Z char const * name /**< Name of effect to find. */ - ); - -/** -Client API: -Creates an effect using the given handler. -@returns The new effect, or null if not found. -*/ -LSX_RETURN_OPT -sox_effect_t * -LSX_API -sox_create_effect( - LSX_PARAM_IN sox_effect_handler_t const * eh /**< Handler to use for effect. */ - ); - -/** -Client API: -Applies the command-line options to the effect. -@returns the number of arguments consumed. -*/ -int -LSX_API -sox_effect_options( - LSX_PARAM_IN sox_effect_t *effp, /**< Effect pointer on which to set options. */ - int argc, /**< Number of arguments in argv. */ - LSX_PARAM_IN_COUNT(argc) char * const argv[] /**< Array of command-line options. */ - ); - -/** -Client API: -Returns an array containing the known effect handlers. -@returns An array containing the known effect handlers. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -sox_effect_fn_t const * -LSX_API -sox_get_effect_fns(void); - -/** -Client API: -Deprecated macro that returns an array containing the known effect handlers. -*/ -#define sox_effect_fns (sox_get_effect_fns()) - -/** -Client API: -Initializes an effects chain. Returned handle must be closed with sox_delete_effects_chain(). -@returns Handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_effects_chain_t * -LSX_API -sox_create_effects_chain( - LSX_PARAM_IN sox_encodinginfo_t const * in_enc, /**< Input encoding. */ - LSX_PARAM_IN sox_encodinginfo_t const * out_enc /**< Output encoding. */ - ); - -/** -Client API: -Closes an effects chain. -*/ -void -LSX_API -sox_delete_effects_chain( - LSX_PARAM_INOUT sox_effects_chain_t *ecp /**< Effects chain pointer. */ - ); - -/** -Client API: -Adds an effect to the effects chain, returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_add_effect( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to which effect should be added . */ - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect to be added. */ - LSX_PARAM_INOUT sox_signalinfo_t * in, /**< Input format. */ - LSX_PARAM_IN sox_signalinfo_t const * out /**< Output format. */ - ); - -/** -Client API: -Runs the effects chain, returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_flow_effects( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to run. */ - LSX_PARAM_IN_OPT sox_flow_effects_callback callback, /**< Callback for monitoring flow progress. */ - LSX_PARAM_IN_OPT void * client_data /**< Data to pass into callback. */ - ); - -/** -Client API: -Gets the number of clips that occurred while running an effects chain. -@returns the number of clips that occurred while running an effects chain. -*/ -sox_uint64_t -LSX_API -sox_effects_clips( - LSX_PARAM_IN sox_effects_chain_t * chain /**< Effects chain from which to read clip information. */ - ); - -/** -Client API: -Shuts down an effect (calls stop on each of its flows). -@returns the number of clips from all flows. -*/ -sox_uint64_t -LSX_API -sox_stop_effect( - LSX_PARAM_INOUT_COUNT(effp->flows) sox_effect_t * effp /**< Effect to stop. */ - ); - -/** -Client API: -Adds an already-initialized effect to the end of the chain. -*/ -void -LSX_API -sox_push_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to which effect should be added. */ - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect to be added. */ - ); - -/** -Client API: -Removes and returns an effect from the end of the chain. -@returns the removed effect, or null if no effects. -*/ -LSX_RETURN_OPT -sox_effect_t * -LSX_API -sox_pop_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to remove an effect. */ - ); - -/** -Client API: -Shut down and delete an effect. -*/ -void -LSX_API -sox_delete_effect( - LSX_PARAM_INOUT_COUNT(effp->flows) sox_effect_t *effp /**< Effect to be deleted. */ - ); - -/** -Client API: -Shut down and delete the last effect in the chain. -*/ -void -LSX_API -sox_delete_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to remove the last effect. */ - ); - -/** -Client API: -Shut down and delete all effects in the chain. -*/ -void -LSX_API -sox_delete_effects( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to delete effects. */ - ); - -/** -Client API: -Gets the sample offset of the start of the trim, useful for efficiently -skipping the part that will be trimmed anyway (get trim start, seek, then -clear trim start). -@returns the sample offset of the start of the trim. -*/ -sox_uint64_t -LSX_API -sox_trim_get_start( - LSX_PARAM_IN sox_effect_t * effp /**< Trim effect. */ - ); - -/** -Client API: -Clears the start of the trim to 0. -*/ -void -LSX_API -sox_trim_clear_start( - LSX_PARAM_INOUT sox_effect_t * effp /**< Trim effect. */ - ); - -/** -Client API: -Returns true if the specified file is a known playlist file type. -@returns true if the specified file is a known playlist file type. -*/ -sox_bool -LSX_API -sox_is_playlist( - LSX_PARAM_IN_Z char const * filename /**< Name of file to examine. */ - ); - -/** -Client API: -Parses the specified playlist file. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_parse_playlist( - LSX_PARAM_IN sox_playlist_callback_t callback, /**< Callback to call for each item in the playlist. */ - void * p, /**< Data to pass to callback. */ - LSX_PARAM_IN char const * const listname /**< Filename of playlist file. */ - ); - -/** -Client API: -Converts a SoX error code into an error string. -@returns error string corresponding to the specified error code, -or a generic message if the error code is not recognized. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -sox_strerror( - int sox_errno /**< Error code to look up. */ - ); - -/** -Client API: -Gets the basename of the specified file; for example, the basename of -"/a/b/c.d" would be "c". -@returns the number of characters written to base_buffer, excluding the null, -or 0 on failure. -*/ -size_t -LSX_API -sox_basename( - LSX_PARAM_OUT_Z_CAP_POST_COUNT(base_buffer_len,return) char * base_buffer, /**< Buffer into which basename should be written. */ - size_t base_buffer_len, /**< Size of base_buffer, in bytes. */ - LSX_PARAM_IN_Z char const * filename /**< Filename from which to extract basename. */ - ); - -/***************************************************************************** -Internal API: -WARNING - The items in this section are subject to instability. They only -exist in the public header because sox (the application) currently uses them. -These may be changed or removed in future versions of libSoX. -*****************************************************************************/ - -/** -Plugins API: -Print a fatal error in libSoX. -*/ -void -LSX_API -lsx_fail_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print a warning in libSoX. -*/ -void -LSX_API -lsx_warn_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print an informational message in libSoX. -*/ -void -LSX_API -lsx_report_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print a debug message in libSoX. -*/ -void -LSX_API -lsx_debug_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Report a fatal error in libSoX; printf-style arguments must follow. -*/ -#define lsx_fail sox_get_globals()->subsystem=__FILE__,lsx_fail_impl - -/** -Plugins API: -Report a warning in libSoX; printf-style arguments must follow. -*/ -#define lsx_warn sox_get_globals()->subsystem=__FILE__,lsx_warn_impl - -/** -Plugins API: -Report an informational message in libSoX; printf-style arguments must follow. -*/ -#define lsx_report sox_get_globals()->subsystem=__FILE__,lsx_report_impl - -/** -Plugins API: -Report a debug message in libSoX; printf-style arguments must follow. -*/ -#define lsx_debug sox_get_globals()->subsystem=__FILE__,lsx_debug_impl - -/** -Plugins API: -String name and integer values for enumerated types (type metadata), for use -with LSX_ENUM_ITEM, lsx_find_enum_text, and lsx_find_enum_value. -*/ -typedef struct lsx_enum_item { - char const *text; /**< String name of enumeration. */ - unsigned value; /**< Integer value of enumeration. */ -} lsx_enum_item; - -/** -Plugins API: -Declares a static instance of an lsx_enum_item structure in format -{ "item", prefixitem }, for use in declaring lsx_enum_item[] arrays. -@param prefix The prefix to prepend to the item in the enumeration symbolic name. -@param item The user-visible text name of the item (must also be a valid C symbol name). -*/ -#define LSX_ENUM_ITEM(prefix, item) {#item, prefix##item}, - -/** -Plugins API: -Flags for use with lsx_find_enum_item. -*/ -enum -{ - lsx_find_enum_item_none = 0, /**< Default parameters (case-insensitive). */ - lsx_find_enum_item_case_sensitive = 1 /**< Enable case-sensitive search. */ -}; - -/** -Plugins API: -Looks up an enumeration by name in an array of lsx_enum_items. -@returns the corresponding item, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -lsx_enum_item const * -LSX_API -lsx_find_enum_text( - LSX_PARAM_IN_Z char const * text, /**< Name of enumeration to find. */ - LSX_PARAM_IN lsx_enum_item const * lsx_enum_items, /**< Array of items to search, with text == NULL for last item. */ - int flags /**< Search flags: 0 (case-insensitive) or lsx_find_enum_item_case_sensitive (case-sensitive). */ - ); - -/** -Plugins API: -Looks up an enumeration by value in an array of lsx_enum_items. -@returns the corresponding item, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -lsx_enum_item const * -LSX_API -lsx_find_enum_value( - unsigned value, /**< Enumeration value to find. */ - LSX_PARAM_IN lsx_enum_item const * lsx_enum_items /**< Array of items to search, with text == NULL for last item. */ - ); - -/** -Plugins API: -Looks up a command-line argument in a set of enumeration names, showing an -error message if the argument is not found in the set of names. -@returns The enumeration value corresponding to the matching enumeration, or -INT_MAX if the argument does not match any enumeration name. -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_enum_option( - int c, /**< Option character to which arg is associated, for example with -a, c would be 'a'. */ - LSX_PARAM_IN_Z char const * arg, /**< Argument to find in enumeration list. */ - LSX_PARAM_IN lsx_enum_item const * items /**< Array of items to search, with text == NULL for last item. */ - ); - -/** -Plugins API: -Determines whether the specified string ends with the specified suffix (case-sensitive). -@returns true if the specified string ends with the specified suffix. -*/ -LSX_RETURN_PURE -sox_bool -LSX_API -lsx_strends( - LSX_PARAM_IN_Z char const * str, /**< String to search. */ - LSX_PARAM_IN_Z char const * end /**< Suffix to search for. */ - ); - -/** -Plugins API: -Finds the file extension for a filename. -@returns the file extension, not including the '.', or null if filename does -not have an extension. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -lsx_find_file_extension( - LSX_PARAM_IN_Z char const * pathname /**< Filename to search for extension. */ - ); - -/** -Plugins API: -Formats the specified number with up to three significant figures and adds a -metric suffix in place of the exponent, such as 1.23G. -@returns A static buffer with the formatted number, valid until the next time -this function is called (note: not thread safe). -*/ -LSX_RETURN_VALID_Z -char const * -LSX_API -lsx_sigfigs3( - double number /**< Number to be formatted. */ - ); - -/** -Plugins API: -Formats the specified number as a percentage, showing up to three significant -figures. -@returns A static buffer with the formatted number, valid until the next time -this function is called (note: not thread safe). -*/ -LSX_RETURN_VALID_Z -char const * -LSX_API -lsx_sigfigs3p( - double percentage /**< Number to be formatted. */ - ); - -/** -Plugins API: -Allocates, deallocates, or resizes; like C's realloc, except that this version -terminates the running application if unable to allocate the requested memory. -@returns New buffer, or null if buffer was freed. -*/ -LSX_RETURN_OPT -void * -LSX_API -lsx_realloc( - LSX_PARAM_IN_OPT void *ptr, /**< Pointer to be freed or resized, or null if allocating a new buffer. */ - size_t newsize /**< New size for buffer, or 0 to free the buffer. */ - ); - -/** -Plugins API: -Like strcmp, except that the characters are compared without regard to case. -@returns 0 (s1 == s2), negative (s1 < s2), or positive (s1 > s2). -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_strcasecmp( - LSX_PARAM_IN_Z char const * s1, /**< First string. */ - LSX_PARAM_IN_Z char const * s2 /**< Second string. */ - ); - - -/** -Plugins API: -Like strncmp, except that the characters are compared without regard to case. -@returns 0 (s1 == s2), negative (s1 < s2), or positive (s1 > s2). -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_strncasecmp( - LSX_PARAM_IN_Z char const * s1, /**< First string. */ - LSX_PARAM_IN_Z char const * s2, /**< Second string. */ - size_t n /**< Maximum number of characters to examine. */ - ); - -/** -Plugins API: -Is option argument unsupported, required, or optional. -*/ -typedef enum lsx_option_arg_t { - lsx_option_arg_none, /**< Option does not have an argument. */ - lsx_option_arg_required, /**< Option requires an argument. */ - lsx_option_arg_optional /**< Option can optionally be followed by an argument. */ -} lsx_option_arg_t; - -/** -Plugins API: -lsx_getopt_init options. -*/ -typedef enum lsx_getopt_flags_t { - lsx_getopt_flag_none = 0, /**< no flags (no output, not long-only) */ - lsx_getopt_flag_opterr = 1, /**< if set, invalid options trigger lsx_warn output */ - lsx_getopt_flag_longonly = 2 /**< if set, recognize -option as a long option */ -} lsx_getopt_flags_t; - -/** -Plugins API: -lsx_getopt long option descriptor. -*/ -typedef struct lsx_option_t { - char const * name; /**< Name of the long option. */ - lsx_option_arg_t has_arg; /**< Whether the long option supports an argument and, if so, whether the argument is required or optional. */ - int * flag; /**< Flag to set if argument is present. */ - int val; /**< Value to put in flag if argument is present. */ -} lsx_option_t; - -/** -Plugins API: -lsx_getopt session information (initialization data and state). -*/ -typedef struct lsx_getopt_t { - int argc; /**< IN argc: Number of arguments in argv */ - char * const * argv; /**< IN argv: Array of arguments */ - char const * shortopts;/**< IN shortopts: Short option characters */ - lsx_option_t const * longopts; /**< IN longopts: Array of long option descriptors */ - lsx_getopt_flags_t flags; /**< IN flags: Flags for longonly and opterr */ - char const * curpos; /**< INOUT curpos: Maintains state between calls to lsx_getopt */ - int ind; /**< INOUT optind: Maintains the index of next element to be processed */ - int opt; /**< OUT optopt: Receives the option character that caused error */ - char const * arg; /**< OUT optarg: Receives the value of the option's argument */ - int lngind; /**< OUT lngind: Receives the index of the matched long option or -1 if not a long option */ -} lsx_getopt_t; - -/** -Plugins API: -Initializes an lsx_getopt_t structure for use with lsx_getopt. -*/ -void -LSX_API -lsx_getopt_init( - LSX_PARAM_IN int argc, /**< Number of arguments in argv */ - LSX_PARAM_IN_COUNT(argc) char * const * argv, /**< Array of arguments */ - LSX_PARAM_IN_Z char const * shortopts, /**< Short options, for example ":abc:def::ghi" (+/- not supported) */ - LSX_PARAM_IN_OPT lsx_option_t const * longopts, /**< Array of long option descriptors */ - LSX_PARAM_IN lsx_getopt_flags_t flags, /**< Flags for longonly and opterr */ - LSX_PARAM_IN int first, /**< First argv to check (usually 1) */ - LSX_PARAM_OUT lsx_getopt_t * state /**< State object to be initialized */ - ); - -/** -Plugins API: -Gets the next option. Options are parameters that start with "-" or "--". -If no more options, returns -1. If unrecognized short option, returns '?'. -If a recognized short option is missing a required argument, -return (shortopts[0]==':' ? ':' : '?'). If successfully recognized short -option, return the recognized character. If successfully recognized long -option, returns (option.flag ? 0 : option.val). -Note: lsx_getopt does not permute the non-option arguments. -@returns option character (short), val or 0 (long), or -1 (no more). -*/ -int -LSX_API -lsx_getopt( - LSX_PARAM_INOUT lsx_getopt_t * state /**< The getopt state pointer. */ - ); - -/* WARNING END */ - -#if defined(__cplusplus) -} -#endif - -#endif /* SOX_H */ diff --git a/freedv/branches/1.2/src/sox/sox_i.h b/freedv/branches/1.2/src/sox/sox_i.h deleted file mode 100644 index 9d533263..00000000 --- a/freedv/branches/1.2/src/sox/sox_i.h +++ /dev/null @@ -1,417 +0,0 @@ -/* libSoX Internal header - * - * This file is meant for libSoX internal use only - * - * Copyright 2001-2008 Chris Bagwell and SoX Contributors - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Chris Bagwell And SoX Contributors are not responsible for - * the consequences of using this software. - */ - -#ifndef SOX_I_H -#define SOX_I_H - -#include "soxomp.h" /* Note: soxomp.h includes soxconfig.h */ -#include "sox.h" - -#define __FREEDV__ - -#if defined HAVE_FMEMOPEN -#define _GNU_SOURCE -#endif - -#include -#include -#include - -#include "util.h" - -#if defined(LSX_EFF_ALIAS) -#undef lsx_debug -#undef lsx_fail -#undef lsx_report -#undef lsx_warn -#define lsx_debug sox_globals.subsystem=effp->handler.name,lsx_debug_impl -#define lsx_fail sox_globals.subsystem=effp->handler.name,lsx_fail_impl -#define lsx_report sox_globals.subsystem=effp->handler.name,lsx_report_impl -#define lsx_warn sox_globals.subsystem=effp->handler.name,lsx_warn_impl -#endif - -#define RANQD1 ranqd1(sox_globals.ranqd1) -#define DRANQD1 dranqd1(sox_globals.ranqd1) - -typedef enum {SOX_SHORT, SOX_INT, SOX_FLOAT, SOX_DOUBLE} sox_data_t; -typedef enum {SOX_WAVE_SINE, SOX_WAVE_TRIANGLE} lsx_wave_t; -lsx_enum_item const * lsx_get_wave_enum(void); - -/* Define fseeko and ftello for platforms lacking them */ -#ifndef HAVE_FSEEKO -#define fseeko fseek -#define ftello ftell -#endif - -#ifdef _FILE_OFFSET_BITS -assert_static(sizeof(off_t) == _FILE_OFFSET_BITS >> 3, OFF_T_BUILD_PROBLEM); -#endif - -FILE * lsx_tmpfile(void); - -void lsx_debug_more_impl(char const * fmt, ...) LSX_PRINTF12; -void lsx_debug_most_impl(char const * fmt, ...) LSX_PRINTF12; - -#define lsx_debug_more sox_get_globals()->subsystem=__FILE__,lsx_debug_more_impl -#define lsx_debug_most sox_get_globals()->subsystem=__FILE__,lsx_debug_most_impl - -/* Digitise one cycle of a wave and store it as - * a table of samples of a specified data-type. - */ -void lsx_generate_wave_table( - lsx_wave_t wave_type, - sox_data_t data_type, - void * table, /* Really of type indicated by data_type. */ - size_t table_size, /* Number of points on the x-axis. */ - double min, /* Minimum value on the y-axis. (e.g. -1) */ - double max, /* Maximum value on the y-axis. (e.g. +1) */ - double phase); /* Phase at 1st point; 0..2pi. (e.g. pi/2 for cosine) */ -char const * lsx_parsesamples(sox_rate_t rate, const char *str, uint64_t *samples, int def); -int lsx_parse_note(char const * text, char * * end_ptr); -double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key); -#define lsx_parse_frequency(a, b) lsx_parse_frequency_k(a, b, INT_MAX) -FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename); - -void lsx_prepare_spline3(double const * x, double const * y, int n, - double start_1d, double end_1d, double * y_2d); -double lsx_spline3(double const * x, double const * y, double const * y_2d, - int n, double x1); - -double lsx_bessel_I_0(double x); -int lsx_set_dft_length(int num_taps); -void init_fft_cache(void); -void clear_fft_cache(void); -void lsx_safe_rdft(int len, int type, double * d); -void lsx_safe_cdft(int len, int type, double * d); -void lsx_power_spectrum(int n, double const * in, double * out); -void lsx_power_spectrum_f(int n, float const * in, float * out); -void lsx_apply_hann_f(float h[], const int num_points); -void lsx_apply_hann(double h[], const int num_points); -void lsx_apply_hamming(double h[], const int num_points); -void lsx_apply_bartlett(double h[], const int num_points); -void lsx_apply_blackman(double h[], const int num_points, double alpha); -void lsx_apply_blackman_nutall(double h[], const int num_points); -double lsx_kaiser_beta(double att); -void lsx_apply_kaiser(double h[], const int num_points, double beta); -double * lsx_make_lpf(int num_taps, double Fc, double beta, double scale, sox_bool dc_norm); -int lsx_lpf_num_taps(double att, double tr_bw, int k); -double * lsx_design_lpf( - double Fp, /* End of pass-band; ~= 0.01dB point */ - double Fc, /* Start of stop-band */ - double Fn, /* Nyquist freq; e.g. 0.5, 1, PI */ - sox_bool allow_aliasing, - double att, /* Stop-band attenuation in dB */ - int * num_taps, /* (Single phase.) 0: value will be estimated */ - int k); /* Number of phases; 0 for single-phase */ -void lsx_fir_to_phase(double * * h, int * len, - int * post_len, double phase0); -#define LSX_TO_6dB .5869 -#define LSX_TO_3dB ((2/3.) * (.5 + LSX_TO_6dB)) -#define LSX_MAX_TBW0 36. -#define LSX_MAX_TBW0A (LSX_MAX_TBW0 / (1 + LSX_TO_3dB)) -#define LSX_MAX_TBW3 floor(LSX_MAX_TBW0 * LSX_TO_3dB) -#define LSX_MAX_TBW3A floor(LSX_MAX_TBW0A * LSX_TO_3dB) -void lsx_plot_fir(double * h, int num_points, sox_rate_t rate, sox_plot_t type, char const * title, double y1, double y2); - -#ifdef HAVE_BYTESWAP_H -#include -#define lsx_swapw(x) bswap_16(x) -#define lsx_swapdw(x) bswap_32(x) -#elif defined(_MSC_VER) -#define lsx_swapw(x) _byteswap_ushort(x) -#define lsx_swapdw(x) _byteswap_ulong(x) -#else -#define lsx_swapw(uw) (((uw >> 8) | (uw << 8)) & 0xffff) -#define lsx_swapdw(udw) ((udw >> 24) | ((udw >> 8) & 0xff00) | ((udw << 8) & 0xff0000) | (udw << 24)) -#endif - - - -/*------------------------ Implemented in libsoxio.c -------------------------*/ - -/* Read and write basic data types from "ft" stream. */ -size_t lsx_readbuf(sox_format_t * ft, void *buf, size_t len); -int lsx_skipbytes(sox_format_t * ft, size_t n); -int lsx_padbytes(sox_format_t * ft, size_t n); -size_t lsx_writebuf(sox_format_t * ft, void const *buf, size_t len); -int lsx_reads(sox_format_t * ft, char *c, size_t len); -int lsx_writes(sox_format_t * ft, char const * c); -void lsx_set_signal_defaults(sox_format_t * ft); -#define lsx_writechars(ft, chars, len) (lsx_writebuf(ft, chars, len) == len? SOX_SUCCESS : SOX_EOF) - -size_t lsx_read_3_buf(sox_format_t * ft, sox_uint24_t *buf, size_t len); -size_t lsx_read_b_buf(sox_format_t * ft, uint8_t *buf, size_t len); -size_t lsx_read_df_buf(sox_format_t * ft, double *buf, size_t len); -size_t lsx_read_dw_buf(sox_format_t * ft, uint32_t *buf, size_t len); -size_t lsx_read_qw_buf(sox_format_t * ft, uint64_t *buf, size_t len); -size_t lsx_read_f_buf(sox_format_t * ft, float *buf, size_t len); -size_t lsx_read_w_buf(sox_format_t * ft, uint16_t *buf, size_t len); - -size_t lsx_write_3_buf(sox_format_t * ft, sox_uint24_t *buf, size_t len); -size_t lsx_write_b_buf(sox_format_t * ft, uint8_t *buf, size_t len); -size_t lsx_write_df_buf(sox_format_t * ft, double *buf, size_t len); -size_t lsx_write_dw_buf(sox_format_t * ft, uint32_t *buf, size_t len); -size_t lsx_write_qw_buf(sox_format_t * ft, uint64_t *buf, size_t len); -size_t lsx_write_f_buf(sox_format_t * ft, float *buf, size_t len); -size_t lsx_write_w_buf(sox_format_t * ft, uint16_t *buf, size_t len); - -int lsx_read3(sox_format_t * ft, sox_uint24_t * u3); -int lsx_readb(sox_format_t * ft, uint8_t * ub); -int lsx_readchars(sox_format_t * ft, char * chars, size_t len); -int lsx_readdf(sox_format_t * ft, double * d); -int lsx_readdw(sox_format_t * ft, uint32_t * udw); -int lsx_readqw(sox_format_t * ft, uint64_t * udw); -int lsx_readf(sox_format_t * ft, float * f); -int lsx_readw(sox_format_t * ft, uint16_t * uw); - -#if 1 /* FIXME: use defines */ -UNUSED static int lsx_readsb(sox_format_t * ft, int8_t * sb) -{return lsx_readb(ft, (uint8_t *)sb);} -UNUSED static int lsx_readsw(sox_format_t * ft, int16_t * sw) -{return lsx_readw(ft, (uint16_t *)sw);} -#else -#define lsx_readsb(ft, sb) lsx_readb(ft, (uint8_t *)sb) -#define lsx_readsw(ft, sw) lsx_readb(ft, (uint16_t *)sw) -#endif - -int lsx_write3(sox_format_t * ft, unsigned u3); -int lsx_writeb(sox_format_t * ft, unsigned ub); -int lsx_writedf(sox_format_t * ft, double d); -int lsx_writedw(sox_format_t * ft, unsigned udw); -int lsx_writeqw(sox_format_t * ft, uint64_t uqw); -int lsx_writef(sox_format_t * ft, double f); -int lsx_writew(sox_format_t * ft, unsigned uw); - -int lsx_writesb(sox_format_t * ft, signed); -int lsx_writesw(sox_format_t * ft, signed); - -int lsx_eof(sox_format_t * ft); -int lsx_error(sox_format_t * ft); -int lsx_flush(sox_format_t * ft); -int lsx_seeki(sox_format_t * ft, off_t offset, int whence); -int lsx_unreadb(sox_format_t * ft, unsigned ub); -uint64_t lsx_filelength(sox_format_t * ft); -off_t lsx_tell(sox_format_t * ft); -void lsx_clearerr(sox_format_t * ft); -void lsx_rewind(sox_format_t * ft); - -int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, off_t to_sample); - -void lsx_fail_errno(sox_format_t *, int, const char *, ...) -#ifdef __GNUC__ -__attribute__ ((format (printf, 3, 4))); -#else -; -#endif - -typedef struct sox_formats_globals { /* Global parameters (for formats) */ - sox_globals_t * global_info; -} sox_formats_globals; - - - -/*------------------------------ File Handlers -------------------------------*/ - -int lsx_check_read_params(sox_format_t * ft, unsigned channels, - sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, - uint64_t num_samples, sox_bool check_length); -#define LSX_FORMAT_HANDLER(name) \ -sox_format_handler_t const * lsx_##name##_format_fn(void); \ -sox_format_handler_t const * lsx_##name##_format_fn(void) -#define div_bits(size, bits) ((uint64_t)(size) * 8 / bits) - -/* Raw I/O */ -int lsx_rawstartread(sox_format_t * ft); -size_t lsx_rawread(sox_format_t * ft, sox_sample_t *buf, size_t nsamp); -int lsx_rawstopread(sox_format_t * ft); -int lsx_rawstartwrite(sox_format_t * ft); -size_t lsx_rawwrite(sox_format_t * ft, const sox_sample_t *buf, size_t nsamp); -int lsx_rawseek(sox_format_t * ft, uint64_t offset); -int lsx_rawstart(sox_format_t * ft, sox_bool default_rate, sox_bool default_channels, sox_bool default_length, sox_encoding_t encoding, unsigned bits_per_sample); -#define lsx_rawstartread(ft) lsx_rawstart(ft, sox_false, sox_false, sox_false, SOX_ENCODING_UNKNOWN, 0) -#define lsx_rawstartwrite lsx_rawstartread -#define lsx_rawstopread NULL -#define lsx_rawstopwrite NULL - -extern sox_format_handler_t const * lsx_sndfile_format_fn(void); - -char * lsx_cat_comments(sox_comments_t comments); - -/*--------------------------------- Effects ----------------------------------*/ - -int lsx_flow_copy(sox_effect_t * effp, const sox_sample_t * ibuf, - sox_sample_t * obuf, size_t * isamp, size_t * osamp); -int lsx_usage(sox_effect_t * effp); -char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n); -#define EFFECT(f) extern sox_effect_handler_t const * lsx_##f##_effect_fn(void); -#include "effects.h" -#undef EFFECT - -#define NUMERIC_PARAMETER(name, min, max) { \ - char * end_ptr; \ - double d; \ - if (argc == 0) break; \ - d = strtod(*argv, &end_ptr); \ - if (end_ptr != *argv) { \ - if (d < min || d > max || *end_ptr != '\0') {\ - lsx_fail("parameter `%s' must be between %g and %g", #name, (double)min, (double)max); \ - return lsx_usage(effp); \ - } \ - p->name = d; \ - --argc, ++argv; \ - } \ -} - -#define TEXTUAL_PARAMETER(name, enum_table) { \ - lsx_enum_item const * e; \ - if (argc == 0) break; \ - e = lsx_find_enum_text(*argv, enum_table, 0); \ - if (e != NULL) { \ - p->name = e->value; \ - --argc, ++argv; \ - } \ -} - -#define GETOPT_NUMERIC(state, ch, name, min, max) case ch:{ \ - char * end_ptr; \ - double d = strtod(state.arg, &end_ptr); \ - if (end_ptr == state.arg || d < min || d > max || *end_ptr != '\0') {\ - lsx_fail("parameter `%s' must be between %g and %g", #name, (double)min, (double)max); \ - return lsx_usage(effp); \ - } \ - p->name = d; \ - break; \ -} - -int lsx_effect_set_imin(sox_effect_t * effp, size_t imin); - -int lsx_effects_init(void); -int lsx_effects_quit(void); - -/*--------------------------------- Dynamic Library ----------------------------------*/ - -#if defined(HAVE_WIN32_LTDL_H) - #include "win32-ltdl.h" - #define HAVE_LIBLTDL 1 - typedef lt_dlhandle lsx_dlhandle; -#elif defined(HAVE_LIBLTDL) - #include - typedef lt_dlhandle lsx_dlhandle; -#else - struct lsx_dlhandle_tag; - typedef struct lsx_dlhandle_tag *lsx_dlhandle; -#endif - -typedef void (*lsx_dlptr)(void); - -typedef struct lsx_dlfunction_info -{ - const char* name; - lsx_dlptr static_func; - lsx_dlptr stub_func; -} lsx_dlfunction_info; - -int lsx_open_dllibrary( - int show_error_on_failure, - const char* library_description, - const char * const library_names[], - const lsx_dlfunction_info func_infos[], - lsx_dlptr selected_funcs[], - lsx_dlhandle* pdl); - -void lsx_close_dllibrary( - lsx_dlhandle dl); - -#define LSX_DLENTRIES_APPLY__(entries, f, x) entries(f, x) - -#define LSX_DLENTRY_TO_PTR__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - func_return (*func_ptr) func_args; - -#define LSX_DLENTRIES_TO_FUNCTIONS__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - func_return func_name func_args; - -/* LSX_DLENTRIES_TO_PTRS: Given an ENTRIES macro and the name of the dlhandle - variable, declares the corresponding function pointer variables and the - dlhandle variable. */ -#define LSX_DLENTRIES_TO_PTRS(entries, dlhandle) \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRY_TO_PTR__, 0) \ - lsx_dlhandle dlhandle - -/* LSX_DLENTRIES_TO_FUNCTIONS: Given an ENTRIES macro, declares the corresponding - functions. */ -#define LSX_DLENTRIES_TO_FUNCTIONS(entries) \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRIES_TO_FUNCTIONS__, 0) - -#define LSX_DLLIBRARY_OPEN1__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - { #func_name, (lsx_dlptr)(static_func), (lsx_dlptr)(stub_func) }, - -#define LSX_DLLIBRARY_OPEN2__(ptr_container, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - (ptr_container)->func_ptr = (func_return (*)func_args)lsx_dlfunction_open_library_funcs[lsx_dlfunction_open_library_index++]; - -/* LSX_DLLIBRARY_OPEN: Input an ENTRIES macro, the library's description, - a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }), - the name of the dlhandle variable, the name of the structure that contains - the function pointer and dlhandle variables, and the name of the variable in - which the result of the lsx_open_dllibrary call should be stored. This will - call lsx_open_dllibrary and copy the resulting function pointers into the - structure members. If the library cannot be opened, show a failure message. */ -#define LSX_DLLIBRARY_OPEN(ptr_container, dlhandle, entries, library_description, library_names, return_var) \ - LSX_DLLIBRARY_TRYOPEN(1, ptr_container, dlhandle, entries, library_description, library_names, return_var) - -/* LSX_DLLIBRARY_TRYOPEN: Input an ENTRIES macro, the library's description, - a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }), - the name of the dlhandle variable, the name of the structure that contains - the function pointer and dlhandle variables, and the name of the variable in - which the result of the lsx_open_dllibrary call should be stored. This will - call lsx_open_dllibrary and copy the resulting function pointers into the - structure members. If the library cannot be opened, show a report or a failure - message, depending on whether error_on_failure is non-zero. */ -#define LSX_DLLIBRARY_TRYOPEN(error_on_failure, ptr_container, dlhandle, entries, library_description, library_names, return_var) \ - do { \ - lsx_dlfunction_info lsx_dlfunction_open_library_infos[] = { \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN1__, 0) \ - {NULL,NULL,NULL} }; \ - int lsx_dlfunction_open_library_index = 0; \ - lsx_dlptr lsx_dlfunction_open_library_funcs[sizeof(lsx_dlfunction_open_library_infos)/sizeof(lsx_dlfunction_open_library_infos[0])]; \ - (return_var) = lsx_open_dllibrary((error_on_failure), (library_description), (library_names), lsx_dlfunction_open_library_infos, lsx_dlfunction_open_library_funcs, &(ptr_container)->dlhandle); \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN2__, ptr_container) \ - } while(0) - -#define LSX_DLLIBRARY_CLOSE(ptr_container, dlhandle) \ - lsx_close_dllibrary((ptr_container)->dlhandle) - - /* LSX_DLENTRY_STATIC: For use in creating an ENTRIES macro. func is - expected to be available at link time. If not present, link will fail. */ -#define LSX_DLENTRY_STATIC(f,x, ret, func, args) f(x, ret, func, args, func, NULL, func) - - /* LSX_DLENTRY_DYNAMIC: For use in creating an ENTRIES macro. func need - not be available at link time (and if present, the link time version will - not be used). func will be loaded via dlsym. If this function is not - found in the shared library, the shared library will not be used. */ -#define LSX_DLENTRY_DYNAMIC(f,x, ret, func, args) f(x, ret, func, args, NULL, NULL, func) - - /* LSX_DLENTRY_STUB: For use in creating an ENTRIES macro. func need not - be available at link time (and if present, the link time version will not - be used). If using DL_LAME, the func may be loaded via dlopen/dlsym, but - if not found, the shared library will still be used if all of the - non-stub functions are found. If the function is not found via dlsym (or - if we are not loading any shared libraries), the stub will be used. This - assumes that the name of the stub function is the name of the function + - "_stub". */ -#define LSX_DLENTRY_STUB(f,x, ret, func, args) f(x, ret, func, args, NULL, func##_stub, func) - - /* LSX_DLFUNC_IS_STUB: returns true if the named function is a do-nothing - stub. Assumes that the name of the stub function is the name of the - function + "_stub". */ -#define LSX_DLFUNC_IS_STUB(ptr_container, func) ((ptr_container)->func == func##_stub) - -#endif diff --git a/freedv/branches/1.2/src/sox/soxomp.h b/freedv/branches/1.2/src/sox/soxomp.h deleted file mode 100644 index 6fce07d9..00000000 --- a/freedv/branches/1.2/src/sox/soxomp.h +++ /dev/null @@ -1,38 +0,0 @@ -#include "soxconfig.h" - -#ifdef HAVE_OPENMP - #include -#else - -typedef int omp_lock_t; -typedef int omp_nest_lock_t; - -#define omp_set_num_threads(int) (void)0 -#define omp_get_num_threads() 1 -#define omp_get_max_threads() 1 -#define omp_get_thread_num() 0 -#define omp_get_num_procs() 1 -#define omp_in_parallel() 1 - -#define omp_set_dynamic(int) (void)0 -#define omp_get_dynamic() 0 - -#define omp_set_nested(int) (void)0 -#define omp_get_nested() 0 - -#define omp_init_lock(omp_lock_t) (void)0 -#define omp_destroy_lock(omp_lock_t) (void)0 -#define omp_set_lock(omp_lock_t) (void)0 -#define omp_unset_lock(omp_lock_t) (void)0 -#define omp_test_lock(omp_lock_t) 0 - -#define omp_init_nest_lock(omp_nest_lock_t) (void)0 -#define omp_destroy_nest_lock(omp_nest_lock_t) (void)0 -#define omp_set_nest_lock(omp_nest_lock_t) (void)0 -#define omp_unset_nest_lock(omp_nest_lock_t) (void)0 -#define omp_test_nest_lock(omp_nest_lock_t) 0 - -#define omp_get_wtime() 0 -#define omp_get_wtick() 0 - -#endif diff --git a/freedv/branches/1.2/src/sox/util.h b/freedv/branches/1.2/src/sox/util.h deleted file mode 100644 index 89bbe752..00000000 --- a/freedv/branches/1.2/src/sox/util.h +++ /dev/null @@ -1,231 +0,0 @@ -/* General purpose, i.e. non SoX specific, utility functions and macros. - * - * (c) 2006-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "soxconfig.h" - -#ifdef HAVE_SYS_TYPES_H -#include /* For off_t not found in stdio.h */ -#endif - -#ifdef HAVE_SYS_STAT_H -#include /* Needs to be included before we redefine off_t. */ -#endif - -#include "xmalloc.h" - -/*---------------------------- Portability stuff -----------------------------*/ - -#if defined(HAVE_INTTYPES_H) - #include -#elif defined(HAVE_STDINT_H) - #include -#else - typedef sox_int8_t int8_t; - typedef sox_uint8_t uint8_t; - typedef sox_int16_t int16_t; - typedef sox_uint16_t uint16_t; - typedef sox_int32_t int32_t; - typedef sox_uint32_t uint32_t; - typedef sox_int64_t int64_t; - typedef sox_uint64_t uint64_t; -#endif - -/* Define the format specifier to use for int64_t values. - * Example: printf("You may have already won $ %" PRId64 " !!!", n64); */ -#ifndef PRId64 /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %lld. */ -#define PRId64 "I64d" -#elif LONG_MAX==9223372036854775807 -#define PRId64 "ld" -#else -#define PRId64 "lld" -#endif -#endif /* PRId64 */ - -/* Define the format specifier to use for uint64_t values. */ -#ifndef PRIu64 /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %llu. */ -#define PRIu64 "I64u" -#elif ULONG_MAX==0xffffffffffffffff -#define PRIu64 "lu" -#else -#define PRIu64 "llu" -#endif -#endif /* PRIu64 */ - -/* Define the format specifier to use for size_t values. - * Example: printf("Sizeof(x) = %" PRIuPTR " bytes", sizeof(x)); */ -#ifndef PRIuPTR /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %zu. */ -#define PRIuPTR "Iu" -#else -#define PRIuPTR "zu" -#endif -#endif /* PRIuPTR */ - -#ifdef __GNUC__ -#define NORET __attribute__((noreturn)) -#define UNUSED __attribute__ ((unused)) -#else -#define NORET -#define UNUSED -#endif - -#ifdef _MSC_VER - -#define __STDC__ 1 -#define O_BINARY _O_BINARY -#define O_CREAT _O_CREAT -#define O_RDWR _O_RDWR -#define O_TRUNC _O_TRUNC -#define S_IFMT _S_IFMT -#define S_IFREG _S_IFREG -#define S_IREAD _S_IREAD -#define S_IWRITE _S_IWRITE -#define close _close -#define dup _dup -#define fdopen _fdopen -#define fileno _fileno - -#ifdef _fstati64 -#define fstat _fstati64 -#else -#define fstat _fstat -#endif - -#define ftime _ftime -#define inline __inline -#define isatty _isatty -#define kbhit _kbhit -#define mktemp _mktemp -#define off_t _off_t -#define open _open -#define pclose _pclose -#define popen _popen -#define setmode _setmode -#define snprintf _snprintf - -#ifdef _stati64 -#define stat _stati64 -#else -#define stat _stat -#endif - -#define strdup _strdup -#define timeb _timeb -#define unlink _unlink - -#if defined(HAVE__FSEEKI64) && !defined(HAVE_FSEEKO) -#undef off_t -#define fseeko _fseeki64 -#define ftello _ftelli64 -#define off_t __int64 -#define HAVE_FSEEKO 1 -#endif - -#elif defined(__MINGW32__) - -#if !defined(HAVE_FSEEKO) -#undef off_t -#define fseeko fseeko64 -#define fstat _fstati64 -#define ftello ftello64 -#define off_t off64_t -#define stat _stati64 -#define HAVE_FSEEKO 1 -#endif - -#endif - -#if defined(DOS) || defined(WIN32) || defined(__NT__) || defined(__DJGPP__) || defined(__OS2__) - #define LAST_SLASH(path) max(strrchr(path, '/'), strrchr(path, '\\')) - #define IS_ABSOLUTE(path) ((path)[0] == '/' || (path)[0] == '\\' || (path)[1] == ':') - #define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) - #define POPEN_MODE "rb" -#else - #define LAST_SLASH(path) strrchr(path, '/') - #define IS_ABSOLUTE(path) ((path)[0] == '/') - #define SET_BINARY_MODE(file) -#endif - -#ifdef WORDS_BIGENDIAN - #define MACHINE_IS_BIGENDIAN 1 - #define MACHINE_IS_LITTLEENDIAN 0 -#else - #define MACHINE_IS_BIGENDIAN 0 - #define MACHINE_IS_LITTLEENDIAN 1 -#endif - -/*--------------------------- Language extensions ----------------------------*/ - -/* Compile-time ("static") assertion */ -/* e.g. assert_static(sizeof(int) >= 4, int_type_too_small) */ -#define assert_static(e,f) enum {assert_static__##f = 1/(e)} -#define array_length(a) (sizeof(a)/sizeof(a[0])) -#define field_offset(type, field) ((size_t)&(((type *)0)->field)) -#define unless(x) if (!(x)) - -/*------------------------------- Maths stuff --------------------------------*/ - -#include - -#ifdef min -#undef min -#endif -#define min(a, b) ((a) <= (b) ? (a) : (b)) - -#ifdef max -#undef max -#endif -#define max(a, b) ((a) >= (b) ? (a) : (b)) - -#define range_limit(x, lower, upper) (min(max(x, lower), upper)) - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#endif -#ifndef M_LN10 -#define M_LN10 2.30258509299404568402 /* natural log of 10 */ -#endif -#ifndef M_SQRT2 -#define M_SQRT2 sqrt(2.) -#endif - -#define sqr(a) ((a) * (a)) -#define sign(x) ((x) < 0? -1 : 1) - -/* Numerical Recipes in C, p. 284 */ -#define ranqd1(x) ((x) = 1664525L * (x) + 1013904223L) /* int32_t x */ -#define dranqd1(x) (ranqd1(x) * (1. / (65536. * 32768.))) /* [-1,1) */ - -#define dB_to_linear(x) exp((x) * M_LN10 * 0.05) -#define linear_to_dB(x) (log10(x) * 20) - -extern int lsx_strcasecmp(const char *s1, const char *st); -extern int lsx_strncasecmp(char const *s1, char const *s2, size_t n); - -#ifndef HAVE_STRCASECMP -#define strcasecmp(s1, s2) lsx_strcasecmp((s1), (s2)) -#define strncasecmp(s1, s2, n) lsx_strncasecmp((s1), (s2), (n)) -#endif diff --git a/freedv/branches/1.2/src/sox/xmalloc.c b/freedv/branches/1.2/src/sox/xmalloc.c deleted file mode 100644 index 9bf15969..00000000 --- a/freedv/branches/1.2/src/sox/xmalloc.c +++ /dev/null @@ -1,43 +0,0 @@ -/* SoX Memory allocation functions - * - * Copyright (c) 2005-2006 Reuben Thomas. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include - -/* Resize an allocated memory area; abort if not possible. - * - * For malloc, `If the size of the space requested is zero, the behavior is - * implementation defined: either a null pointer is returned, or the - * behavior is as if the size were some nonzero value, except that the - * returned pointer shall not be used to access an object' - */ -void *lsx_realloc(void *ptr, size_t newsize) -{ - if (ptr && newsize == 0) { - free(ptr); - return NULL; - } - - if ((ptr = realloc(ptr, newsize)) == NULL) { - lsx_fail("out of memory"); - exit(2); - } - - return ptr; -} diff --git a/freedv/branches/1.2/src/sox/xmalloc.h b/freedv/branches/1.2/src/sox/xmalloc.h deleted file mode 100644 index 9ee77f63..00000000 --- a/freedv/branches/1.2/src/sox/xmalloc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* libSoX Memory allocation functions - * - * Copyright (c) 2005-2006 Reuben Thomas. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LSX_MALLOC_H -#define LSX_MALLOC_H - -#include -#include - -#define lsx_malloc(size) lsx_realloc(NULL, (size)) -#define lsx_calloc(n,s) (((n)*(s))? memset(lsx_malloc((n)*(s)),0,(n)*(s)) : NULL) -#define lsx_Calloc(v,n) v = lsx_calloc(n,sizeof(*(v))) -#define lsx_strdup(p) ((p)? strcpy((char *)lsx_malloc(strlen(p) + 1), p) : NULL) -#define lsx_memdup(p,s) ((p)? memcpy(lsx_malloc(s), p, s) : NULL) -#define lsx_valloc(v,n) v = lsx_malloc((n)*sizeof(*(v))) -#define lsx_revalloc(v,n) v = lsx_realloc(v, (n)*sizeof(*(v))) - -#endif diff --git a/freedv/branches/1.2/src/sox_biquad.c b/freedv/branches/1.2/src/sox_biquad.c deleted file mode 100644 index 548f4249..00000000 --- a/freedv/branches/1.2/src/sox_biquad.c +++ /dev/null @@ -1,134 +0,0 @@ -//========================================================================== -// Name: sox_biquad.h -// Purpose: Interface into Sox Biquad filters -// Created: Dec 1, 2012 -// Authors: David Rowe -// -// To test: -/* - $ gcc sox_biquad.c sox/effects_i.c sox/effects.c sox/formats_i.c \ - sox/biquad.c sox/biquads.c sox/xmalloc.c sox/libsox.c \ - -o sox_biquad -DSOX_BIQUAD_UNITTEST -D__FREEDV__ \ - -Wall -lm -lsndfile -g - $ ./sox_biquad -*/ -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include -#include -#include -#include "sox/sox.h" - -#include "sox_biquad.h" - - -#define N_MAX 1024 - -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, - sox_sample_t *obuf, size_t *isamp, size_t *osamp); - -void sox_biquad_start(void) -{ - int r = sox_init(); - assert(r == SOX_SUCCESS); -} - -void sox_biquad_finish(void) -{ - sox_quit(); -} - -/* - Effect must be implemented by biquads.c in sox, arguments are just - like sox command line, for example: - - char *argv[10]; - argv[0] = "highpass"; argv[1]="1000"; argc=1; -*/ - -void *sox_biquad_create(int argc, const char *argv[]) -{ - int ret; - sox_effect_t *e; - int (*start)(sox_effect_t *); /* function pointer to effect start func */ - - e = sox_create_effect(sox_find_effect(argv[0])); assert(e != NULL); - ret = sox_effect_options(e, argc, (char * const*)&argv[1]); - assert(ret == SOX_SUCCESS); - - start = e->handler.start; - e->in_signal.rate = 8000; /* locked at FS=8000 Hz */ - ret = start(e); assert(ret == SOX_SUCCESS); - - return (void *)e; -} - -void sox_biquad_destroy(void *sbq) { - sox_effect_t *e = (sox_effect_t *)sbq; - free(e); -} - -void sox_biquad_filter(void *sbq, short out[], short in[], int n) -{ - sox_effect_t *e = (sox_effect_t *)sbq; - sox_sample_t ibuf[N_MAX]; - sox_sample_t obuf[N_MAX]; - size_t isamp, osamp; - unsigned int clips; - SOX_SAMPLE_LOCALS; - int i; - - assert(n <= N_MAX); - - clips = 0; - for(i=0; i. -// -//========================================================================== - -#ifndef __SOX_BIQUAD__ -#define __SOX_BIQUAD__ - -#ifdef __cplusplus -extern "C" { - -#endif - -void sox_biquad_start(void); -void sox_biquad_finish(void); -void *sox_biquad_create(int argc, const char *argv[]); -void sox_biquad_destroy(void *sbq); -void sox_biquad_filter(void *sbq, short out[], short in[], int n); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/freedv/branches/1.2/src/topFrame.cpp b/freedv/branches/1.2/src/topFrame.cpp deleted file mode 100644 index c1044b33..00000000 --- a/freedv/branches/1.2/src/topFrame.cpp +++ /dev/null @@ -1,592 +0,0 @@ -//========================================================================== -// Name: topFrame.cpp -// -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "topFrame.h" - -extern int g_playFileToMicInEventId; -extern int g_recFileFromRadioEventId; -extern int g_playFileFromRadioEventId; - -//========================================================================= -// Code that lays out the main application window -//========================================================================= -TopFrame::TopFrame(wxString plugInName, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) -{ - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); - //===================================================== - // Menubar Setup - m_menubarMain = new wxMenuBar(wxMB_DOCKABLE); - file = new wxMenu(); - - wxMenuItem* m_menuItemOnTop; - m_menuItemOnTop = new wxMenuItem(file, wxID_ANY, wxString(_("On Top")) , _("Always Top Window"), wxITEM_NORMAL); - file->Append(m_menuItemOnTop); - - wxMenuItem* m_menuItemExit; - m_menuItemExit = new wxMenuItem(file, ID_EXIT, wxString(_("E&xit")) , _("Exit Program"), wxITEM_NORMAL); - file->Append(m_menuItemExit); - - m_menubarMain->Append(file, _("&File")); - - tools = new wxMenu(); - wxMenuItem* m_menuItemAudio; - m_menuItemAudio = new wxMenuItem(tools, wxID_ANY, wxString(_("&Audio Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemAudio); - - wxMenuItem* m_menuItemRigCtrlCfg; - m_menuItemRigCtrlCfg = new wxMenuItem(tools, wxID_ANY, wxString(_("&PTT Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemRigCtrlCfg); - - wxMenuItem* m_menuItemOptions; - m_menuItemOptions = new wxMenuItem(tools, wxID_ANY, wxString(_("Options")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemOptions); - - wxMenuItem* m_menuItemFilter; - m_menuItemFilter = new wxMenuItem(tools, wxID_ANY, wxString(_("&Filter")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemFilter); - - wxMenuItem* m_menuItemPlugIn; - if (!wxIsEmpty(plugInName)) { - m_menuItemPlugIn = new wxMenuItem(tools, wxID_ANY, plugInName + wxString(_(" Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemPlugIn); - } - - wxMenuItem* m_menuItemPlayFileToMicIn; - m_menuItemPlayFileToMicIn = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Play File - Mic In")) , wxEmptyString, wxITEM_NORMAL); - g_playFileToMicInEventId = m_menuItemPlayFileToMicIn->GetId(); - tools->Append(m_menuItemPlayFileToMicIn); - - wxMenuItem* m_menuItemRecFileFromRadio; - m_menuItemRecFileFromRadio = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Record File - From Radio")) , wxEmptyString, wxITEM_NORMAL); - g_recFileFromRadioEventId = m_menuItemRecFileFromRadio->GetId(); - tools->Append(m_menuItemRecFileFromRadio); - - wxMenuItem* m_menuItemPlayFileFromRadio; - m_menuItemPlayFileFromRadio = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Play File - From Radio")) , wxEmptyString, wxITEM_NORMAL); - g_playFileFromRadioEventId = m_menuItemPlayFileFromRadio->GetId(); - tools->Append(m_menuItemPlayFileFromRadio); - m_menubarMain->Append(tools, _("&Tools")); - - help = new wxMenu(); - wxMenuItem* m_menuItemHelpUpdates; - m_menuItemHelpUpdates = new wxMenuItem(help, wxID_ANY, wxString(_("Check for Updates")) , wxEmptyString, wxITEM_NORMAL); - help->Append(m_menuItemHelpUpdates); - m_menuItemHelpUpdates->Enable(false); - - wxMenuItem* m_menuItemAbout; - m_menuItemAbout = new wxMenuItem(help, ID_ABOUT, wxString(_("&About")) , _("About this program"), wxITEM_NORMAL); - help->Append(m_menuItemAbout); - - m_menubarMain->Append(help, _("&Help")); - - this->SetMenuBar(m_menubarMain); - - wxBoxSizer* bSizer1; - bSizer1 = new wxBoxSizer(wxHORIZONTAL); - - //===================================================== - // Left side - //===================================================== - wxBoxSizer* leftSizer; - leftSizer = new wxBoxSizer(wxVERTICAL); - - wxStaticBoxSizer* snrSizer; - snrSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("SNR")), wxVERTICAL); - - //------------------------------ - // S/N ratio Guage (vert. bargraph) - //------------------------------ - m_gaugeSNR = new wxGauge(this, wxID_ANY, 25, wxDefaultPosition, wxSize(15,135), wxGA_SMOOTH|wxGA_VERTICAL); - m_gaugeSNR->SetToolTip(_("Displays signal to noise ratio in dB.")); - snrSizer->Add(m_gaugeSNR, 1, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); - - //------------------------------ - // Box for S/N ratio (Numeric) - //------------------------------ - m_textSNR = new wxStaticText(this, wxID_ANY, wxT(" 0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - snrSizer->Add(m_textSNR, 0, wxALIGN_CENTER_HORIZONTAL, 1); - - //------------------------------ - // S/N ratio slow Checkbox - //------------------------------ - m_ckboxSNR = new wxCheckBox(this, wxID_ANY, _("Slow"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - m_ckboxSNR->SetToolTip(_("Smooth but slow SNR estimation")); - snrSizer->Add(m_ckboxSNR, 0, wxALIGN_CENTER_HORIZONTAL, 5); - - leftSizer->Add(snrSizer, 2, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 1); - - //------------------------------ - // Sync Indicator box - //------------------------------ - wxStaticBoxSizer* sbSizer3_33; - sbSizer3_33 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Sync")), wxVERTICAL); - - m_rbSync = new wxRadioButton( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - m_rbSync->SetForegroundColour( wxColour( 255, 0, 0 ) ); - sbSizer3_33->Add(m_rbSync, 0, wxALIGN_CENTER|wxALL, 1); - leftSizer->Add(sbSizer3_33,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // BER Frames box - //------------------------------ - - wxStaticBoxSizer* sbSizer_ber; - sbSizer_ber = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Bit Error Rate")), wxVERTICAL); - - m_BtnBerReset = new wxButton(this, wxID_ANY, _("Reset"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_ber->Add(m_BtnBerReset, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - m_textBits = new wxStaticText(this, wxID_ANY, wxT("Bits: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textBits, 0, wxALIGN_LEFT, 1); - m_textErrors = new wxStaticText(this, wxID_ANY, wxT("Errs: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textErrors, 0, wxALIGN_LEFT, 1); - m_textBER = new wxStaticText(this, wxID_ANY, wxT("BER: 0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textBER, 0, wxALIGN_LEFT, 1); - - leftSizer->Add(sbSizer_ber,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Signal Level(vert. bargraph) - //------------------------------ - wxStaticBoxSizer* levelSizer; - levelSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Level")), wxVERTICAL); - - m_textLevel = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(60,-1), wxALIGN_CENTRE); - m_textLevel->SetForegroundColour(wxColour(255,0,0)); - levelSizer->Add(m_textLevel, 0, wxALIGN_LEFT, 1); - - m_gaugeLevel = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(15,135), wxGA_SMOOTH|wxGA_VERTICAL); - m_gaugeLevel->SetToolTip(_("Peak of From Radio in Rx, or peak of From Mic in Tx mode. If Red you should reduce your levels")); - levelSizer->Add(m_gaugeLevel, 1, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); - - leftSizer->Add(levelSizer, 2, wxALIGN_CENTER|wxALL|wxEXPAND, 1); - - bSizer1->Add(leftSizer, 0, wxALL|wxEXPAND, 5); - - //===================================================== - // Center Section - //===================================================== - wxBoxSizer* centerSizer; - centerSizer = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* upperSizer; - upperSizer = new wxBoxSizer(wxVERTICAL); - - //===================================================== - // Tabbed Notebook control containing display graphs - //===================================================== - //m_auiNbookCtrl = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_NB_BOTTOM|wxAUI_NB_DEFAULT_STYLE); - //long style = wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_CLOSE_ON_ACTIVE_TAB | wxAUI_NB_MIDDLE_CLICK_CLOSE; - long nb_style = wxAUI_NB_BOTTOM | wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS; - m_auiNbookCtrl = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, nb_style); - // This line sets the fontsize for the tabs on the notebook control - m_auiNbookCtrl->SetFont(wxFont(8, 70, 90, 90, false, wxEmptyString)); - - upperSizer->Add(m_auiNbookCtrl, 1, wxALIGN_TOP|wxEXPAND, 1); - centerSizer->Add(upperSizer, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALIGN_TOP|wxEXPAND, 0); - - // lower middle used for user ID - - wxBoxSizer* lowerSizer; - lowerSizer = new wxBoxSizer(wxHORIZONTAL); - - m_BtnCallSignReset = new wxButton(this, wxID_ANY, _("Clear"), wxDefaultPosition, wxDefaultSize, 0); - lowerSizer->Add(m_BtnCallSignReset, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - wxBoxSizer* bSizer15; - bSizer15 = new wxBoxSizer(wxVERTICAL); - m_txtCtrlCallSign = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_txtCtrlCallSign->SetToolTip(_("Call Sign of transmitting station will appear here")); - bSizer15->Add(m_txtCtrlCallSign, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5); - lowerSizer->Add(bSizer15, 1, wxEXPAND, 5); - -#ifdef __EXPERIMENTAL_UDP__ - wxStaticBoxSizer* sbSizer_Checksum = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Checksums")), wxHORIZONTAL); - - wxStaticText *goodLabel = new wxStaticText(this, wxID_ANY, wxT("Good: "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - sbSizer_Checksum->Add(goodLabel, 0, 0, 2); - m_txtChecksumGood = new wxStaticText(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(30,-1), wxALIGN_CENTRE); - sbSizer_Checksum->Add(m_txtChecksumGood, 0, 0, 2); - - wxStaticText *badLabel = new wxStaticText(this, wxID_ANY, wxT("Bad: "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - sbSizer_Checksum->Add(badLabel, 0, 0, 1); - m_txtChecksumBad = new wxStaticText(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(30,-1), wxALIGN_CENTRE); - sbSizer_Checksum->Add(m_txtChecksumBad, 0, 0, 1); - - lowerSizer->Add(sbSizer_Checksum, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - //===================================================== - // These are the buttons that autosend the userid (?) - //===================================================== - - // DR 4 Dec - taken off for screen for Beta release to avoid questions on their use until - // we implement this feature - #ifdef UNIMPLEMENTED - wxBoxSizer* bSizer141; - bSizer141 = new wxBoxSizer(wxHORIZONTAL); - - // TxID - //--------- - m_togTxID = new wxToggleButton(this, wxID_ANY, _("TxID"), wxDefaultPosition, wxDefaultSize, 0); - m_togTxID->SetToolTip(_("Send Tx ID information")); - bSizer141->Add(m_togTxID, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); - - // RxID - //--------- - m_togRxID = new wxToggleButton(this, wxID_ANY, _("RxID"), wxDefaultPosition, wxDefaultSize, 0); - m_togRxID->SetToolTip(_("Enable reception of ID information")); - bSizer141->Add(m_togRxID, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxALL|wxFIXED_MINSIZE, 5); - - lowerSizer->Add(bSizer141, 0, wxALIGN_RIGHT, 5); -#endif - - centerSizer->Add(lowerSizer, 0, wxALIGN_BOTTOM|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 2); - bSizer1->Add(centerSizer, 4, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 1); - - //===================================================== - // Right side - //===================================================== - wxBoxSizer* rightSizer; - rightSizer = new wxBoxSizer(wxVERTICAL); - - //===================================================== - // Squelch Slider Control - //===================================================== - wxStaticBoxSizer* sbSizer3; - sbSizer3 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Squelch")), wxVERTICAL); - - m_sliderSQ = new wxSlider(this, wxID_ANY, 0, 0, 40, wxDefaultPosition, wxSize(-1,80), wxSL_AUTOTICKS|wxSL_INVERSE|wxSL_VERTICAL); - m_sliderSQ->SetToolTip(_("Set Squelch level in dB.")); - - sbSizer3->Add(m_sliderSQ, 1, wxALIGN_CENTER_HORIZONTAL, 0); - - //------------------------------ - // Squelch Level static text box - //------------------------------ - m_textSQ = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - - sbSizer3->Add(m_textSQ, 0, wxALIGN_CENTER_HORIZONTAL, 0); - - //------------------------------ - // Squelch Toggle Checkbox - //------------------------------ - m_ckboxSQ = new wxCheckBox(this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - - sbSizer3->Add(m_ckboxSQ, 0, wxALIGN_CENTER_HORIZONTAL, 0); - rightSizer->Add(sbSizer3, 2, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 0); - - //rightSizer->Add(sbSizer3_33,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - /* new --- */ - - //------------------------------ - // Mode box - //------------------------------ - wxStaticBoxSizer* sbSizer_mode; - sbSizer_mode = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Mode")), wxVERTICAL); - -#ifdef DISABLED_FEATURE - m_rb1400old = new wxRadioButton( this, wxID_ANY, wxT("1400 V0.91"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb1400old, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1400 = new wxRadioButton( this, wxID_ANY, wxT("1400"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1400, 0, wxALIGN_LEFT|wxALL, 1); - m_rb700 = new wxRadioButton( this, wxID_ANY, wxT("700"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700, 0, wxALIGN_LEFT|wxALL, 1); -#endif - m_rb700b = new wxRadioButton( this, wxID_ANY, wxT("700B"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700b, 0, wxALIGN_LEFT|wxALL, 1); - m_rb700c = new wxRadioButton( this, wxID_ANY, wxT("700C"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700c, 0, wxALIGN_LEFT|wxALL, 1); - m_rb800xa = new wxRadioButton( this, wxID_ANY, wxT("800XA"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb800xa, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1600 = new wxRadioButton( this, wxID_ANY, wxT("1600"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1600, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1600->SetValue(true); - - m_rbPlugIn = NULL; - if (!wxIsEmpty(plugInName)) { - // Optional plug in - - m_rbPlugIn = new wxRadioButton( this, wxID_ANY, plugInName, wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rbPlugIn, 0, wxALIGN_LEFT|wxALL, 1); - } - -#ifdef DISABLED_FEATURE - m_rb1600Wide = new wxRadioButton( this, wxID_ANY, wxT("1600 Wide"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1600Wide, 0, wxALIGN_LEFT|wxALL, 1); - m_rb2000 = new wxRadioButton( this, wxID_ANY, wxT("2000"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb2000, 0, wxALIGN_LEFT|wxALL, 1); -#endif - - rightSizer->Add(sbSizer_mode,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - #ifdef MOVED_TO_OPTIONS_DIALOG - /* new --- */ - - //------------------------------ - // Test Frames box - //------------------------------ - - wxStaticBoxSizer* sbSizer_testFrames; - sbSizer_testFrames = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Test Frames")), wxVERTICAL); - - m_ckboxTestFrame = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxTestFrame, 0, wxALIGN_LEFT, 0); - - rightSizer->Add(sbSizer_testFrames,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - #endif - - //===================================================== - // Control Toggles box - //===================================================== - wxStaticBoxSizer* sbSizer5; - sbSizer5 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Control")), wxVERTICAL); - wxBoxSizer* bSizer1511; - bSizer1511 = new wxBoxSizer(wxVERTICAL); - - //------------------------------- - // Stop/Stop signal processing (rx and tx) - //------------------------------- - m_togBtnOnOff = new wxToggleButton(this, wxID_ANY, _("Start"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnOnOff->SetToolTip(_("Begin/End receiving data.")); - bSizer1511->Add(m_togBtnOnOff, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer1511, 0, wxEXPAND, 1); - -#ifdef UNIMPLEMENTED - //------------------------------ - // Toggle Loopback button for RX - //------------------------------ - wxBoxSizer* bSizer15113; - bSizer15113 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* bSizer15111; - bSizer15111 = new wxBoxSizer(wxVERTICAL); - wxSize wxSz = wxSize(44, 30); - m_togBtnLoopRx = new wxToggleButton(this, wxID_ANY, _("Loop\nRX"), wxDefaultPosition, wxSz, 0); - m_togBtnLoopRx->SetFont(wxFont(6, 70, 90, 90, false, wxEmptyString)); - m_togBtnLoopRx->SetToolTip(_("Loopback Receive audio data.")); - - bSizer15111->Add(m_togBtnLoopRx, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - //sbSizer5->Add(bSizer15111, 0, wxEXPAND, 1); - bSizer15113->Add(bSizer15111, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - //------------------------------ - // Toggle Loopback button for Tx - //------------------------------ - wxBoxSizer* bSizer15112; - bSizer15112 = new wxBoxSizer(wxVERTICAL); - m_togBtnLoopTx = new wxToggleButton(this, wxID_ANY, _("Loop\nTX"), wxDefaultPosition, wxSz, 0); - m_togBtnLoopTx->SetFont(wxFont(6, 70, 90, 90, false, wxEmptyString)); - m_togBtnLoopTx->SetToolTip(_("Loopback Transmit audio data.")); - - bSizer15112->Add(m_togBtnLoopTx, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - bSizer15113->Add(bSizer15112, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - sbSizer5->Add(bSizer15113, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - //------------------------------ - // Split Frequency Mode Toggle - //------------------------------ - wxBoxSizer* bSizer151; - bSizer151 = new wxBoxSizer(wxVERTICAL); - - m_togBtnSplit = new wxToggleButton(this, wxID_ANY, _("Split"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnSplit->SetToolTip(_("Toggle split frequency mode.")); - - bSizer151->Add(m_togBtnSplit, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 1); - wxBoxSizer* bSizer13; - bSizer13 = new wxBoxSizer(wxVERTICAL); - - //------------------------------ - // Analog Passthrough Toggle - //------------------------------ - m_togBtnAnalog = new wxToggleButton(this, wxID_ANY, _("Analog"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnAnalog->SetToolTip(_("Toggle analog/digital operation.")); - bSizer13->Add(m_togBtnAnalog, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer13, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - //------------------------------ - // Voice Keyer Toggle - //------------------------------ - m_togBtnVoiceKeyer = new wxToggleButton(this, wxID_ANY, _("Voice Keyer"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnVoiceKeyer->SetToolTip(_("Toggle Voice Keyer")); - wxBoxSizer* bSizer13a = new wxBoxSizer(wxVERTICAL); - bSizer13a->Add(m_togBtnVoiceKeyer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer13a, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - // not implemented on fdmdv2 -#ifdef ALC - //------------------------------ - // Toggle for ALC - //------------------------------ - wxBoxSizer* bSizer14; - bSizer14 = new wxBoxSizer(wxVERTICAL); - m_togBtnALC = new wxToggleButton(this, wxID_ANY, _("ALC"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnALC->SetToolTip(_("Toggle automatic level control mode.")); - - bSizer14->Add(m_togBtnALC, 0, wxALL, 1); - sbSizer5->Add(bSizer14, 0, wxALIGN_CENTER|wxALIGN_CENTER_HORIZONTAL|wxALL, 1); -#endif - - //------------------------------ - // PTT button: Toggle Transmit/Receive mode - //------------------------------ - wxBoxSizer* bSizer11; - bSizer11 = new wxBoxSizer(wxVERTICAL); - m_btnTogPTT = new wxToggleButton(this, wxID_ANY, _("PTT"), wxDefaultPosition, wxDefaultSize, 0); - m_btnTogPTT->SetToolTip(_("Push to Talk - Switch between Receive and Transmit - you can also use the space bar ")); - bSizer11->Add(m_btnTogPTT, 1, wxALIGN_CENTER|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer11, 2, wxEXPAND, 1); - rightSizer->Add(sbSizer5, 2, wxALIGN_CENTER|wxALL|wxEXPAND, 3); - bSizer1->Add(rightSizer, 0, wxALL|wxEXPAND, 3); - this->SetSizer(bSizer1); - this->Layout(); - m_statusBar1 = this->CreateStatusBar(3, wxST_SIZEGRIP, wxID_ANY); - - //===================================================== - // End of layout - //===================================================== - - //------------------- - // Connect Events - //------------------- - this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TopFrame::topFrame_OnClose)); - this->Connect(wxEVT_PAINT, wxPaintEventHandler(TopFrame::topFrame_OnPaint)); - this->Connect(wxEVT_SIZE, wxSizeEventHandler(TopFrame::topFrame_OnSize)); - this->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::topFrame_OnUpdateUI)); - - this->Connect(m_menuItemExit->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnExit)); - this->Connect(m_menuItemOnTop->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnTop)); - - this->Connect(m_menuItemAudio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsAudio)); - this->Connect(m_menuItemAudio->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsAudioUI)); - this->Connect(m_menuItemFilter->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsFilter)); - this->Connect(m_menuItemFilter->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsFilterUI)); - this->Connect(m_menuItemRigCtrlCfg->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsComCfg)); - this->Connect(m_menuItemRigCtrlCfg->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsComCfgUI)); - this->Connect(m_menuItemOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsOptions)); - this->Connect(m_menuItemOptions->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsOptionsUI)); - - if (!wxIsEmpty(plugInName)) { - this->Connect(m_menuItemPlugIn->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsPlugInCfg)); - this->Connect(m_menuItemPlugIn->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsPlugInCfgUI)); - } - - this->Connect(m_menuItemPlayFileToMicIn->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); - this->Connect(m_menuItemRecFileFromRadio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); - this->Connect(m_menuItemPlayFileFromRadio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileFromRadio)); - - this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); - this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - this->Connect(m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpAbout)); - //m_togRxID->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnRxID), NULL, this); - //m_togTxID->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnTxID), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnSliderScrollBottom), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScrollChanged), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnSliderScrollTop), NULL, this); - m_ckboxSQ->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSQClick), NULL, this); - - m_ckboxSNR->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSNRClick), NULL, this); - - m_togBtnOnOff->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnOnOff), NULL, this); - m_togBtnSplit->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnSplitClick), NULL, this); - m_togBtnAnalog->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnAnalogClick), NULL, this); - m_togBtnVoiceKeyer->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnVoiceKeyerClick), NULL, this); -#ifdef ALC - m_togBtnALC->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnALCClick), NULL, this); -#endif - m_btnTogPTT->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnPTT), NULL, this); - - m_BtnCallSignReset->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnCallSignReset), NULL, this); - m_BtnBerReset->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnBerReset), NULL, this); -} - -TopFrame::~TopFrame() -{ - //------------------- - // Disconnect Events - //------------------- - this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TopFrame::topFrame_OnClose)); - this->Disconnect(wxEVT_PAINT, wxPaintEventHandler(TopFrame::topFrame_OnPaint)); - this->Disconnect(wxEVT_SIZE, wxSizeEventHandler(TopFrame::topFrame_OnSize)); - this->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::topFrame_OnUpdateUI)); - this->Disconnect(ID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnExit)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsAudio)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsAudioUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsFilter)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsFilterUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsComCfg)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsComCfgUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsOptions)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsOptionsUI)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsPlugInCfg)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileFromRadio)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - this->Disconnect(ID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpAbout)); - //m_togRxID->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnRxID), NULL, this); - //m_togTxID->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnTxID), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnSliderScrollBottom), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScrollChanged), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnSliderScrollTop), NULL, this); - m_ckboxSQ->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSQClick), NULL, this); - - m_togBtnOnOff->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnOnOff), NULL, this); - m_togBtnSplit->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnSplitClick), NULL, this); - m_togBtnAnalog->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnAnalogClick), NULL, this); - m_togBtnVoiceKeyer->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnVoiceKeyerClick), NULL, this); -#ifdef ALC - m_togBtnALC->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnALCClick), NULL, this); -#endif - m_btnTogPTT->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnPTT), NULL, this); - -} - diff --git a/freedv/branches/1.2/src/topFrame.h b/freedv/branches/1.2/src/topFrame.h deleted file mode 100644 index e4ed5830..00000000 --- a/freedv/branches/1.2/src/topFrame.h +++ /dev/null @@ -1,193 +0,0 @@ -//========================================================================== -// Name: topFrame.h -// -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// 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 . -// -//========================================================================== -#ifndef __TOPFRAME_H__ -#define __TOPFRAME_H__ - -#include "version.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/////////////////////////////////////////////////////////////////////////// - -#define ID_OPEN 1000 -#define ID_SAVE 1001 -#define ID_CLOSE 1002 -#define ID_EXIT 1003 -#define ID_COPY 1004 -#define ID_CUT 1005 -#define ID_PASTE 1006 -#define ID_OPTIONS 1007 -#define ID_ABOUT 1008 - -/////////////////////////////////////////////////////////////////////////////// -/// Class TopFrame -/////////////////////////////////////////////////////////////////////////////// -class TopFrame : public wxFrame -{ - private: - - protected: - wxMenuBar* m_menubarMain; - wxMenu* file; - wxMenu* edit; - wxMenu* tools; - wxMenu* help; - wxGauge* m_gaugeSNR; - wxStaticText* m_textSNR; - wxCheckBox* m_ckboxSNR; - wxGauge* m_gaugeLevel; - wxStaticText* m_textLevel; - - wxButton* m_BtnCallSignReset; - wxTextCtrl* m_txtCtrlCallSign; - wxStaticText* m_txtChecksumGood; - wxStaticText* m_txtChecksumBad; - - wxSlider* m_sliderSQ; - wxCheckBox* m_ckboxSQ; - wxStaticText* m_textSQ; - wxStatusBar* m_statusBar1; - - wxButton* m_BtnBerReset; - wxStaticText *m_textBits; - wxStaticText *m_textErrors; - wxStaticText *m_textBER; - - wxRadioButton *m_rbSync; - wxRadioButton *m_rb1400old; - wxRadioButton *m_rb1400; - wxRadioButton *m_rb700; - wxRadioButton *m_rb700b; - wxRadioButton *m_rb700c; - wxRadioButton *m_rb800xa; - wxRadioButton *m_rb1600; - wxRadioButton *m_rb2000; - wxRadioButton *m_rb1600Wide; - wxRadioButton *m_rbPlugIn; - - // Virtual event handlers, overide them in your derived class - virtual void topFrame_OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void topFrame_OnPaint( wxPaintEvent& event ) { event.Skip(); } - virtual void topFrame_OnSize( wxSizeEvent& event ) { event.Skip(); } - virtual void topFrame_OnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnExit( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTop( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsAudio( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsAudioUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsFilter( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsFilterUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsOptions( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnToolsPlugInCfg( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsPlugInCfgUI( wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnToolsUDP( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsOptionsUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsComCfg( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsComCfgUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnPlayFileToMicIn( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRecFileFromRadio( wxCommandEvent& event ) { event.Skip(); } - virtual void OnPlayFileFromRadio( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnHelpCheckUpdates( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnHelpAbout( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnRxID( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnTxID( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCmdSliderScroll( wxScrollEvent& event ) { event.Skip(); } - virtual void OnSliderScrollBottom( wxScrollEvent& event ) { event.Skip(); } - virtual void OnCmdSliderScrollChanged( wxScrollEvent& event ) { event.Skip(); } - virtual void OnSliderScrollTop( wxScrollEvent& event ) { event.Skip(); } - virtual void OnCheckSQClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCheckSNRClick( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnTogBtnLoopRx( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnLoopTx( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnOnOff( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnSplitClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnAnalogClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnVoiceKeyerClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnALCClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnPTT( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnTogBtnSplitClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnAnalogClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnALCClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnRxIDUI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnTxIDUI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnPTT_UI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnOnOffUI(wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnCallSignReset( wxCommandEvent& event ) { event.Skip(); } - virtual void OnBerReset( wxCommandEvent& event ) { event.Skip(); } - - public: - wxToggleButton* m_togRxID; - wxToggleButton* m_togTxID; - wxToggleButton* m_togBtnOnOff; - wxToggleButton* m_togBtnSplit; - wxToggleButton* m_togBtnAnalog; - wxToggleButton* m_togBtnVoiceKeyer; - wxToggleButton* m_togBtnALC; - wxToggleButton* m_btnTogPTT; - wxToggleButton* m_togBtnLoopRx; - wxToggleButton* m_togBtnLoopTx; - wxAuiNotebook* m_auiNbookCtrl; - - TopFrame( wxString plugInName, wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeDV ") + _(FREEDV_VERSION), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(561,300 ), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL ); - - ~TopFrame(); -}; - -#endif //__TOPFRAME_H__ diff --git a/freedv/tags/1.2.2/.clang/.gitignore b/freedv/tags/1.2.2/.clang/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/CMakeLists.txt b/freedv/tags/1.2.2/CMakeLists.txt deleted file mode 100644 index a9f70496..00000000 --- a/freedv/tags/1.2.2/CMakeLists.txt +++ /dev/null @@ -1,463 +0,0 @@ -# -# FreeDV - HF Digital Voice for Radio Amateurs -# -# CMake configuration contributed by Richard Shaw (KF5OIM) -# Please report questions, comments, problems, or patches to the freetel -# mailing list: https://lists.sourceforge.net/lists/listinfo/freetel-codec2 -# - -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7" CACHE STRING "Minimum OS X deployment version") - -cmake_minimum_required(VERSION 2.8) - -# Prevent in-source builds to protect automake/autoconf config. -# If an in-source build is attempted, you will still need to clean up a few -# files manually. -set(CMAKE_DISABLE_SOURCE_CHANGES ON) -set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) -if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") - message(FATAL_ERROR "In-source builds in ${CMAKE_BINARY_DIR} are not " - "allowed, please remove ./CMakeCache.txt and ./CMakeFiles/, create a " - "separate build directory and run cmake from there.") -endif("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") - -# Set local module path. -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") - -project(FreeDV) - -# -# Set FreeDV version and generate src/version.h -# -set(FREEDV_VERSION_MAJOR 1) -set(FREEDV_VERSION_MINOR 2) -set(FREEDV_VERSION_PATCH 2) -set(FREEDV_VERSION ${FREEDV_VERSION_MAJOR}.${FREEDV_VERSION_MINOR}) -if(FREEDV_VERSION_PATCH) - set(FREEDV_VERSION ${FREEDV_VERSION}.${FREEDV_VERSION_PATCH}) -endif() -set(FREEDV_VERSION_SUFFIX FALSE) -if(FREEDV_VERSION_SUFFIX) - set(FREEDV_VERSION_STRING "${FREEDV_VERSION} ${FREEDV_VERSION_SUFFIX}") -else() - set(FREEDV_VERSION_STRING "${FREEDV_VERSION}") -endif() -message(STATUS "FreeDV version: ${FREEDV_VERSION_STRING}") -configure_file(cmake/version.h.in src/version.h @ONLY) - -# Set default build type -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug") - message(STATUS "Build type not specified, defaulting to ${CMAKE_BUILD_TYPE}") -endif(NOT CMAKE_BUILD_TYPE) - -# Work around for not using a svn working copy. -add_definitions(-D_NO_AUTOTOOLS_) -find_program(SVN_PATH svn) -if(SVN_PATH) - execute_process(COMMAND ${SVN_PATH} info --show-item revision - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE SVN_REVISION_RESULT - OUTPUT_VARIABLE SVN_CURRENT_REVISION - ERROR_QUIET - ) -else() - set(SVN_REVISION_RESULT 1) -endif() - -if(SVN_REVISION_RESULT EQUAL 0) - string(STRIP ${SVN_CURRENT_REVISION} SVN_REVISION) - add_definitions(-DSVN_REVISION="${SVN_REVISION}") -else() - add_definitions(-DSVN_REVISION="None") -endif() - - -# Set default build flags. -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") -if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11") -endif(APPLE) - -# -# Setup cmake options -# -set(CMAKE_VERBOSE_MAKEFILE TRUE CACHE BOOL "Verbose makefile.") -set(USE_STATIC_DEPS FALSE CACHE BOOL - "Download and build static libraries instead of system libraries.") -set(USE_STATIC_PORTAUDIO FALSE CACHE BOOL - "Download and build static portaudio instead of the system library.") -set(USE_STATIC_SNDFILE FALSE CACHE BOOL - "Download and build static sndfile instead of the system library.") -set(USE_STATIC_SAMPLERATE FALSE CACHE BOOL - "Download and build static samplerate instead of the system library.") -set(USE_STATIC_CODEC2 TRUE CACHE BOOL - "Download and build static codec2 instead of the system library.") -set(USE_STATIC_SPEEXDSP TRUE CACHE BOOL - "Download and build static speex instead of the system library.") -set(BOOTSTRAP_WXWIDGETS FALSE CACHE BOOL - "Download and build static wxWidgets instead of the system library.") - -if(USE_STATIC_DEPS) - set(USE_STATIC_PORTAUDIO TRUE FORCE) - set(USE_STATIC_SNDFILE TRUE FORCE) - set(USE_STATIC_SAMPLERATE TRUE FORCE) - set(USE_STATIC_CODEC2 TRUE FORCE) -endif(USE_STATIC_DEPS) - -# -# Pull in external wxWidgets target if performing static build. -# -if(BOOTSTRAP_WXWIDGETS) - message(STATUS "Adding wxWidgets build target...") - include(cmake/BuildWxWidgets.cmake) -endif(BOOTSTRAP_WXWIDGETS) - -# -# Perform bootstrap build of wxWidgets -# -if(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) - message(STATUS "Will perform bootstrap build of wxWidgets. - After make step completes, re-run cmake and make again to perform FreeDV build.") -# -# Continue normal build if not bootstrapping wxWidgets or is already built. -# -else(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) - - -# -# Various hacks and work arounds for building under MinGW. -# -if(MINGW) - message(STATUS "System is MinGW.") - # Setup HOST variable. - include(cmake/MinGW.cmake) - # This sets up the exe icon for windows under mingw. - set(RES_FILES "") - set(RES_FILES "${CMAKE_SOURCE_DIR}/contrib/freedv.rc") - set(CMAKE_RC_COMPILER_INIT windres) - enable_language(RC) - set(CMAKE_RC_COMPILE_OBJECT - " -O coff -i -o ") - include(InstallRequiredSystemLibraries) -endif(MINGW) - -# Math library is automatic on MinGW -if(UNIX) - set(CMAKE_REQUIRED_INCLUDES math.h) - set(CMAKE_REQUIRED_LIBRARIES m) -endif(UNIX) - -# Find some standard headers and functions. -include(CheckIncludeFiles) -check_include_files("byteswap.h" HAVE_BYTESWAP_H) -check_include_files("limits.h" HAVE_LIMITS_H) -check_include_files("stddef.h" HAVE_STDDEF_H) -check_include_files("stdlib.h" HAVE_STDLIB_H) -check_include_files("string.h" HAVE_STRING_H) -check_include_files("strings.h" HAVE_STRINGS_H) -check_include_files("ltdl.h" HAVE_LTDL_H) -check_include_files("inttypes.h" HAVE_INTTYPES_H) -check_include_files("sys/stat.h" HAVE_SYS_STAT_H) -check_include_files("sys/types.h" HAVE_SYS_TYPES_H) - -include(CheckTypeSize) -check_type_size("int" SIZEOF_INT) - -include(CheckFunctionExists) -check_function_exists(floor HAVE_FLOOR) -check_function_exists(memset HAVE_MEMSET) -check_function_exists(pow HAVE_POW) -check_function_exists(sqrt HAVE_SQRT) -check_function_exists(fseeko HAVE_FSEEKO) -check_function_exists(fmemopen HAVE_FMEMOPEN) -check_function_exists(strcasecmp HAVE_STRCASECMP) -check_function_exists(vsnprintf HAVE_VSNPRINTF) - -include(CheckSymbolExists) -check_symbol_exists("_fseeki64" "stdio.h" HAVE__FSEEKI64) - -# fdmdv2_main.h requires patching to find config.h as it current looks in the -# source directory and the generated file goes in the binary directory. -configure_file ("${PROJECT_SOURCE_DIR}/cmake/config.h.in" - "${PROJECT_BINARY_DIR}/config.h" ) -include_directories(${PROJECT_BINARY_DIR}) -add_definitions(-DHAVE_CONFIG_H) - -# Config file for bundled sox sources -configure_file("${PROJECT_SOURCE_DIR}/cmake/soxconfig.h.in" - "${PROJECT_BINARY_DIR}/soxconfig.h") - -# Pthread Library -find_package(Threads REQUIRED) -message(STATUS "Threads library flags: ${CMAKE_THREAD_LIBS_INIT}") - -# -# Find codec2 -# -if(NOT USE_STATIC_CODEC2) - message(STATUS "Looking for codec2...") - # 'CONFIG' removed due to incompatibility with cmake version - # in Ubuntu 12.04 (Precise) -- Stuart Longland - find_package(codec2 QUIET) - if(codec2_FOUND) - get_target_property(CODEC2_LIBRARY codec2 LOCATION) - message(STATUS " codec2 library: ${CODEC2_LIBRARY}") - message(STATUS " codec2 headers: ${codec2_INCLUDE_DIRS}") - else() - # Try to find manually - find_path(CODEC2_INCLUDE_DIRS codec2.h - PATH_SUFFIXES codec2) - find_library(CODEC2_LIBRARY NAMES codec2) - if(CODEC2_LIBRARY AND CODEC2_INCLUDE_DIRS) - message(STATUS " codec2 library: ${CODEC2_LIBRARY}") - message(STATUS " codec2 headers: ${CODEC2_INCLUDE_DIRS}") - list(APPEND FREEDV_LINK_LIBS ${CODEC2_LIBRARY}) - include_directories(${CODEC2_INCLUDE_DIRS}) - else() - message(FATAL_ERROR "codec2 library not found. -Linux: -Codec2 may not be in your distribution so build yourself or use the cmake option to build statically into FreeDV. -Windws: -It's easiest to use the cmake option: USE_STATIC_CODEC2" - ) - endif() - endif() -else(NOT USE_STATIC_CODEC2) - message(STATUS "Will attempt static build of codec2.") - include(cmake/BuildCodec2.cmake) -endif(NOT USE_STATIC_CODEC2) - -# -# Find or build portaudio Library -# -if(NOT USE_STATIC_PORTAUDIO) - message(STATUS "Looking for portaudio...") - find_package(Portaudio REQUIRED) - if(PORTAUDIO_FOUND) - message(STATUS " portaudio library: ${PORTAUDIO_LIBRARIES}") - message(STATUS " portaudio headers: ${PORTAUDIO_INCLUDE_DIRS}") - list(APPEND FREEDV_LINK_LIBS ${PORTAUDIO_LIBRARIES}) - include_directories(${PORTAUDIO_INCLUDE_DIRS}) - else() - message(FATAL_ERROR "portaudio library not found. -On Linux systems try installing: - portaudio-devel (RPM based systems) - libportaudio-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_PORTAUDIO" - ) - endif() - if(NOT ${PORTAUDIO_VERSION} EQUAL 19) - message(WARNING "Portaudio versions other than 19 are known to have issues. You have been warned!") - endif() -else(NOT USE_STATIC_PORTAUDIO) - message(STATUS "Will attempt static build of portaudio.") - include(cmake/BuildPortaudio.cmake) -endif(NOT USE_STATIC_PORTAUDIO) - -# -# Hamlib library -# -message(STATUS "Looking for hamlib...") -find_path(HAMLIB_INCLUDE_DIR hamlib/rig.h) -find_library(HAMLIB_LIBRARY hamlib PATH_SUFFIXES hamlib) -message(STATUS "Hamlib library: ${HAMLIB_LIBRARY}") -message(STATUS "Hamlib headers: ${HAMLIB_INCLUDE_DIR}") -if(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - message(STATUS "Hamlib library found.") - include_directories(${HAMLIB_INCLUDE_DIR}) - list(APPEND FREEDV_LINK_LIBS ${HAMLIB_LIBRARY}) -else(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - message(FATAL_ERROR "hamlib not found. -On Linux systems try installing: - hamlib-devel (RPM based systems) - libhamlib-dev (DEB based systems)" - ) -endif(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - - -# -# Samplerate Library -# -if(NOT USE_STATIC_SAMPLERATE) - message(STATUS "Looking for samplerate...") - find_library(LIBSAMPLERATE samplerate) - find_path(LIBSAMPLERATE_INCLUDE_DIR samplerate.h) - message(STATUS " samplerate library: ${LIBSAMPLERATE}") - message(STATUS " samplerate headers: ${LIBSAMPLERATE_INCLUDE_DIR}") - if(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) - list(APPEND FREEDV_LINK_LIBS ${LIBSAMPLERATE}) - include_directories(${LIBSAMPLERATE_INCLUDE_DIR}) - else(LIBSTAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) - message(FATAL_ERROR "samplerate library not found. -On Linux systems try installing: - samplerate-devel (RPM based systems) - libsamplerate-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_SAMPLERATE" - ) - endif(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) -else(NOT USE_STATIC_SAMPLERATE) - message(STATUS "Will attempt static build of samplerate.") - include(cmake/BuildSamplerate.cmake) -endif(NOT USE_STATIC_SAMPLERATE) - -# -# sndfile Library -# -if(NOT USE_STATIC_SNDFILE) - message(STATUS "Looking for sndfile...") - find_library(LIBSNDFILE sndfile) - find_path(LIBSNDFILE_INCLUDE_DIR sndfile.h) - message(STATUS " sndfile library: ${LIBSNDFILE}") - message(STATUS " sndfile headers: ${LIBSNDFILE_INCLUDE_DIR}") - if(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) - list(APPEND FREEDV_LINK_LIBS ${LIBSNDFILE}) - else(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) - message(FATAL_ERROR "sndfile library not found. -On Linux systems try installing: - libsndfile-devel (RPM based systems) - libsndfile-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_SNDFILE" - ) - endif(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) -else(NOT USE_STATIC_SNDFILE) - message(STATUS "Will attempt static build of sndfile.") - include(cmake/BuildSndfile.cmake) -endif(NOT USE_STATIC_SNDFILE) - -# -# Find wxWidgets -# -if(NOT BOOTSTRAP_WXWIDGETS) - set(WXCONFIG "" CACHE FILEPATH "Location of wx-config binary.") - set(WXRC "" CACHE FILEPATH "Location of wxrc binary.") -endif(NOT BOOTSTRAP_WXWIDGETS) -#if(BOOTSTRAP_WXWIDGETS) -# set(WXCONFIG "${CMAKE_BINARY_DIR}/external/dist/bin/wx-config") -# set(WXRC "${CMAKE_BINARY_DIR}/external/dist/bin/wxrc") -# list(APPEND FREEDV_STATIC_DEPS wxWidgets) -#endif(BOOTSTRAP_WXWIDGETS) -message(STATUS "Looking for wxWidgets...") -if(WXCONFIG) - message(STATUS "wx-config: ${WXCONFIG}") - set(wxWidgets_CONFIG_EXECUTABLE ${WXCONFIG}) -endif(WXCONFIG) -if(WXRC) - message(STATUS "wxrc: ${WXRC}") - set(wxWidgets_wxrc_EXECUTABLE ${WXRC}) -endif(WXRC) -set(WX_VERSION_MIN 3.0.0) -find_package(wxWidgets REQUIRED core base aui html net adv) -execute_process(COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" --version - OUTPUT_VARIABLE WX_VERSION) -string(STRIP ${WX_VERSION} WX_VERSION) -if(WX_VERSION VERSION_EQUAL ${WX_VERSION_MIN} - OR WX_VERSION VERSION_GREATER ${WX_VERSION_MIN}) - message(STATUS "wxWidgets version: ${WX_VERSION}") -else() - message(FATAL_ERROR "wxWidgets must be installed on your system. -Please check that wx-config is in path, the directory -where wxWidgets libraries are installed (returned by -'wx-config --libs' or 'wx-config --static --libs' command) -is in LD_LIBRARY_PATH or equivalent variable and -wxWidgets version is ${WX_VERSION_MIN} or above.") -endif() -if(wxWidgets_FOUND) - include("${wxWidgets_USE_FILE}") - list(APPEND FREEDV_LINK_LIBS ${wxWidgets_LIBRARIES}) -endif(wxWidgets_FOUND) - -# -# Find speex library -# -if(NOT USE_STATIC_SPEEXDSP) - message(STATUS "Looking for Speex DSP library.") - find_path(SPEEXDSP_INCLUDE_DIR NAMES speex/speex.h speex/speexdsp_types.h) - find_library(SPEEXDSP_LIBRARY speexdsp) - message(STATUS " Speex DSP headers: ${SPEEXDSP_INCLUDE_DIR}") - message(STATUS " Speex DSP library: ${SPEEXDSP_LIBRARY}") - if(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) - include_directories(${SPEEXDSP_INCLUDE_DIR}) - list(APPEND FREEDV_LINK_LIBS ${SPEEXDSP_LIBRARY}) - else(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) - message(FATAL_ERROR "Speex DSP library not found!") - endif(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) -else() - message(STATUS "Will attempt static build of speex.") - include(cmake/BuildSpeex.cmake) -endif() - -# -# Find libdl for dlopen/dlclose -# -if(UNIX) - message(STATUS "Looking for dl library.") - find_library(DL_LIBRARY dl) - if(DL_LIBRARY) - message(STATUS " dl library: ${DL_LIBRARY}") - list(APPEND FREEDV_LINK_LIBS ${DL_LIBRARY}) - else() - message(FATAL_ERROR "dl library not found. -On Linux systems try installing: - glibc-devel (RPM based systems) - glibc-dev (DEB based systems)" - ) - endif() -endif(UNIX) - - -#Freedv -add_subdirectory(src) - -# Icons and desktop file -add_subdirectory(contrib) - -message(STATUS "Build type will be: ${CMAKE_BUILD_TYPE}") - -# -# Cpack NSIS configuration for Windows. -# -if(WIN32) - # Detect if we're doing a 32-bit or 64-bit windows build. - if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(CMAKE_CL_64 TRUE) - set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") - endif() - if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - set(CPACK_STRIP_FILES TRUE) - endif() - configure_file(cmake/GetDependencies.cmake.in cmake/GetDependencies.cmake - @ONLY - ) - install(SCRIPT ${CMAKE_BINARY_DIR}/cmake/GetDependencies.cmake) - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "HF Digital Voice for Radio Amateurs") - set(CPACK_PACKAGE_VENDOR "CMake") - #set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README") - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") - set(CPACK_PACKAGE_VERSION_MAJOR ${FREEDV_VERSION_MAJOR}) - set(CPACK_PACKAGE_VERSION_MINOR ${FREEDV_VERSION_MINOR}) - # CPack expects a patch level version so set it here and override if we - # are actually setting one. - set(CPACK_PACKAGE_VERSION_PATCH 0) - if(FREEDV_VERSION_PATCH) - set(CPACK_PACKAGE_VERSION_PATCH ${FREEDV_VERSION_PATCH}) - endif() - if(FREEDV_VERSION_SUFFIX) - set(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${FREEDV_VERSION_SUFFIX}") - endif() - # There is a bug in NSI that does not handle full unix paths properly. Make - # sure there is at least one set of four (4) backlasshes. - #set(CPACK_PACKAGE_ICON "${CMake_SOURCE_DIR}/Utilities/Release\\\\InstallIcon.bmp") - set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\freedv.exe") - set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY}") - set(CPACK_NSIS_PACKAGE_NAME "FreeDV") - set(CPACK_PACKAGE_EXECUTABLES freedv;FreeDV) - set(CPACK_NSIS_URL_INFO_ABOUT "http://freedv.org") - set(CPACK_NSIS_MODIFY_PATH OFF) - set(CPACK_NSIS_MENU_LINKS - "http://freedv.org" "FreeDV Homepage") - include(CPack) -endif(WIN32) - -endif(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) diff --git a/freedv/tags/1.2.2/COPYING b/freedv/tags/1.2.2/COPYING deleted file mode 100644 index cfd4e991..00000000 --- a/freedv/tags/1.2.2/COPYING +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see - . - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/freedv/tags/1.2.2/README.osx b/freedv/tags/1.2.2/README.osx deleted file mode 100644 index c71544ff..00000000 --- a/freedv/tags/1.2.2/README.osx +++ /dev/null @@ -1,107 +0,0 @@ -Building under OSX is similar to building under linux, but there are some additional steps that need to be performed to produce a working app-bundle. - -For the following instructions, I'm assuming you will be placing everything in: -/Users//Dev/ - -1/ DEPENDENCIES -Using Macports, most of the appropriate dependencies can be installed by: - -$ sudo port install subversion git libtool libsamplerate sox portaudio dylibbundler cmake - -It should be fairly similar using HomeBrew, but you will need to replace all the /opt/ paths in the following instructions. - -1.1/ HAMLIB -First, we will need to build hamlib from source, as we need hamlib to be statically compiled (Macports won't do this..) - -$ git clone git://git.code.sf.net/p/hamlib/code hamlib-code -$ cd hamlib-code - -You will now need to edit line 12 of autogen.sh, to change "libtoolize" to "glibtoolize" - -$ ./autogen.sh -$ ./configure --disable-shared --prefix /Users//Dev/hamlib -$ make -$ make install - -You should now have an installation of hamlib in ~/Dev/hamlib - -Just in case you have hamlib installed via Macports, it may be a good idea to run -$ sudo port deactivate hamlib - -1.2/ WXWIDGETS -To be able to produce an appbundle, we need wxWidgets to be build statically. Again, Macports won't do this out of the box. - -Edit the wxWidgets-3.0 port file using: -$ sudo port edit wxWidgets-3.0 - -and add the following to the bottom of the file: - -variant static description { build a static version of the libraries with some other options... } { - configure.args-append --enable-std_iostreams - configure.args-append --disable-shared - configure.args-delete --with-sdl - configure.args-delete --with-opengl - set installtype release-static -} - -Now you can build and install a static variant of wxWidgets with: -$ sudo port install wxWidgets-3.0 +static - -Note: This will probably break anything else which is using wxWidgets. Once you have finished building FreeDV, you may -want to go back to the dynamically compiled version using: -$ sudo port install wxWidgets-3.0 - -HomeBrew Users: Anyone know how to do the above? - -1.3/ CODEC2 LIBRARIES -The FreeDV CMake procedure will automatically checkout and compile Codec2. -If you want to build and install your own copy (i.e. for access to the command-line tools), you can do so: - -$ wget http://files.freedv.org/codec2/codec2-0.4.tar.gz -or -$ svn checkout https://svn.code.sf.net/p/freetel/code/codec2-dev/ - -$ cd codec2-0.4 -or -cd codec2-dev -$ mkdir build_osx && cd build_osx -$ cmake ../ && make -$ sudo make install - -3/ BUILDING FREEDV -Get the FreeDV source by either: - -Getting the current 'stable' release (1.0): -$ wget http://files.freedv.org/freedv/freedv-1.0.tar.gz -$ tar -xzf freedv-1.0.tar.gz - -or - -Checking the latest revision out from SVN: -$ svn checkout https://svn.code.sf.net/p/freetel/code/freedv-dev/ - -$ cd freedv-1.0 -or -$ cd freedv-dev - -$ mkdir build_osx && cd build_osx - -Assuming you are intending on building Codec2 as part of the build process, run: - -$ cmake -DWXCONFIG=/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.0/lib/wx/config/osx_cocoa-unicode-static-3.0 -DCMAKE_EXE_LINKER_FLAGS="-L/opt/local/lib" -DHAMLIB_INCLUDE_DIR=../../hamlib/include -DHAMLIB_LIBRARY=../../hamlib/lib/libhamlib.a ../ - -Then, build FreeDV: -$ make - -The build process will create an appbundle (FreeDV.app) and a compressed disk image (FreeDV.dmg) in ./build_osx/src -Move these to wherever you want, and run! - -Happy DVing! - -Acknowledgements: -A big thank you to Mooneer Salem, K6AQ, for walking me through this process, and figuring out how to solve the wxWidgets and Hamlib issues. - -Please e-mail any corrections to either the digitalvoice google group list, or myself, at: -vk5qi(at)rfhead.net -Mark Jessop VK5QI - diff --git a/freedv/tags/1.2.2/README.txt b/freedv/tags/1.2.2/README.txt deleted file mode 100644 index 26e731d1..00000000 --- a/freedv/tags/1.2.2/README.txt +++ /dev/null @@ -1,233 +0,0 @@ -================================== - FreeDV GUI README.txt -================================== - -This document describes how to build the FreeDV GUI program for -various operating systems. See also: - - http://freedv.org - introduction, documentation, downloads - RELEASE_NOTES.txt - changes made to each version - USER_MANUAL.txt - FreeDV GUI Manual - -================================== - Building and installing on Linux -================================== - -First the basics: - - $ sudo apt-get install build-essential cmake subversion - -To install the required development libraries instead of building them -statically: - -Debian/Ubuntu: - - $ sudo apt-get install libwxgtk3.0-dev portaudio19-dev \ - libhamlib-dev libsamplerate-dev libasound2-dev libao-dev libgsm1-dev \ - libsndfile-dev - -Fedora: - - $ sudo dnf install wxGTK3-devel portaudio-devel libsamplerate-devel \ - libsndfile-devel speexdsp-devel hamlib-devel alsa-lib-devel libao-devel \ - gsm-devel - - -RHEL/CentOS and derivitves (requires Fedora EPEL repository) - - $ sudo yum install wxGTK3-devel portaudio-devel libsamplerate-devel \ - libsndfile-devel speexdsp-devel hamlib-devel alsa-lib-devel libao-devel \ - gsm-devel - - -Quickstart 1 ------------- - -1/ Using a modern Linux, and the development library packages - installed above: - - $ cd /path/to/freedv - $ mkdir build_linux - $ cd build_linux - $ cmake ../ - $ make - $ ./src/freedv - -Quickstart 2 ------------- - -Builds static versions of wxWidgets, portaudio, codec2-dev, which are commonly -missing on older Linux systems. - -1/ Assumes static build of wxWidgets and the freedv-dev source is checked out into ~/freedv-dev: - - $ cd ~/freedv-dev - $ mkdir build_linux - $ cd build_linux - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE ../ - $ make - -2/ Then you can configure FreeDV using your local codec-dev, something like: - - $ cmake -DCMAKE_BUILD_TYPE=Debug -DBOOTSTRAP_WXWIDGETS=TRUE -DCODEC2_INCLUDE_DIRS=/path/to/codec2-dev/src -DCODEC2_LIBRARY=/path/to/codec2-dev/build_linux/src/libcodec2.so -DUSE_STATIC_CODEC2=FALSE -DUSE_STATIC_PORTAUDIO=TRUE ../ - -3/ OR build a local copy of codec2-dev: - - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE -DUSE_STATIC_CODEC2=TRUE -DUSE_STATIC_PORTAUDIO=TRUE ../ - -4/ Build and run FreeDV: - - $ make - $ ./src/freedv - -======================================================= - Building for Windows on Ubuntu Linux (Cross compiling) -======================================================= - -1/ Install the cross compiling toolchain: - - $ sudo apt-get install mingw-w64 - -2/ Patch cmake using: http://www.cmake.org/gitweb?p=stage/cmake.git;a=patch;h=33286235048495ceafb636d549d9a4e8891967ae - -3/ Checkout a fresh copy of codec2-dev and build for Windows, pointing to the generate_codebook built by a linux build of generate_codebook, using this cmake line - - $ cmake .. -DCMAKE_TOOLCHAIN_FILE=/home/david/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake -DUNITTEST=FALSE -DGENERATE_CODEBOOK=/home/david/codec2-dev/build_linux/src/generate_codebook - -4/ Build WxWidgets - - $ cd /path/to/freedv-dev - $ mkdir build_windows - $ cd build_windows - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE .. -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-Ubuntu-mingw32.cmake -DCMAKE_BUILD_TYPE=Debug - $ make - -5/ Download and install the Windows version of Hamlib: - - $ wget http://internode.dl.sourceforge.net/project/hamlib/hamlib/1.2.15.3/hamlib-win32-1.2.15.3.zip - $ unzip hamlib-win32-1.2.15.3.zip - -6/ Build All the libraries and FreeDV: - - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-Ubuntu-mingw32.cmake -DUSE_STATIC_PORTAUDIO=TRUE -DUSE_STATIC_SNDFILE=TRUE -DUSE_STATIC_SAMPLERATE=TRUE -DUSE_STATIC_CODEC2=FALSE -DCODEC2_INCLUDE_DIRS=/home/david/tmp/codec2-dev/src -DCODEC2_LIBRARY=/home/david/tmp/codec2-dev/build_windows/src/libcodec2.dll.a -DHAMLIB_INCLUDE_DIR=hamlib-win32-1.2.15.3/include -DHAMLIB_LIBRARY=hamlib-win32-1.2.15.3/lib/gcc/libhamlib.dll.a -DCMAKE_BUILD_TYPE=Debug .. - $ make - -7/ Test on Linux with "wine", this will tell you if any DLLs are missing: - - $ wine src/freedv.exe - -8/ When moving to an actual Windows machine, I needed: - - /usr/lib/gcc/i686-w64-mingw32/4.8/libstdc++-6.dll - /usr/lib/gcc/i686-w64-mingw32/4.8/libgcc_s_sjlj-1.dll - /usr/i686-w64-mingw32/lib/libwinpthread-1.dll - - Wine seems to find these automagically, so I found them on my system by - looking at ~/.wine/system.reg for PATH: - - [System\\CurrentControlSet\\Control\\Session Manager\\Environment] 1423800803 - "PATH"=str(2):"C:\\windows\\system32;C:\\windows;C:\\windows\\system32\\wbem;Z:\\usr\\i686-w64-mingw32\\lib;Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\4.8" - - -==================================== - Building and installing on Windows -==================================== - -The windows build is similar to linux and follows the same basic workflow, -however, while codec2 and FreeDV (freedv) build well on windows, some of the -dependencies do not. For that reson current windows releases are cross-compiled -from linux. - -Only MinGW is supported. While it is likely possible to perform a pure MinGW -build, installing MSYS2 will make your life easier. - -CMake may not automatically detect that you're in the MSYS environment. If this -occurs you need to pass cmake the proper generator: - -cmake -G"MSYS Makefiles" [other options] - -=============================== - Bootstrapping wxWidgets build -=============================== - -If wxWidgets (>= 3.0) is not available then one option is to have CMake boot- -strap the build for FreeDV. - -This is required because the tool wx-config is used to get the correct compiler -and linker flags of the wxWidgets components needed by FreeDV. Since this is -normally done at configure time, not during "make", it is not possible for CMake -or have this information prior to building wxWidgets. - -In order to work around this issue you can "bootstrap" the wxWidgets build using -the CMake option, "BOOTSTRAP_WXWIDGETS". wxWidgets will be built using static -libraries. - -NOTE: This forces "USE_STATIC_WXWIDGETS" to be true internally regarless of the -value set manually. - -(from any directory, but empty directory outside of the source is prefered.) -$ cmake -DBOOTSTRAP_WXWIDGETS=TRUE /path/to/freedv -$ make -(wxWidgets is downloaded and built) -$ cmake . -(wxWidgets build should be detected) -$ make -(if all goes well, as root) -$ make install - -==================================== - Building and installing on OSX -==================================== - -Pls see README.osx - -==================================== - Building and installing on FreeBSD -==================================== - -As per "Quickstart 2" above but change build_linux to build_freebsd - -======= -Editing -======= - -Please make sure your text editor does not insert tabs, and -used indents of 4 spaces. The following .emacs code was used to -configure emacs: - -(setq-default indent-tabs-mode nil) - -(add-hook 'c-mode-common-hook - (function (lambda () - (setq c-basic-offset 4) - ))) - -FreeDV GUI TODO List --------------------- - -[ ] Ubuntu packaging -[ ] default sound card in/out setting for rx out of the box -[ ] When application close on windows while "Start" down sometimes crashes - + Also on Linux it reports an unterminated thread when exiting -[ ] Tool-Audio Config Dialog sound device names truncated on Windows -[ ] Serialport::closeport() on Linux takes about 1 second - + delays 'Stop' on main window test on Tools-PTT Test -[ ] Voice keyer file name at bottom on main screen truncated - + need a bigger field -[ ] Start/Stop file rec/playback, work out a better UI, - maybe buttons on front page -[ ] feature for evaluating yr own sound quality - + trap bad mic response/levels - + zero in on different sound quality from different users -[ ] feeding audio over UDP say from from gqrx - + could also be used to netcat stored files -[ ] refactoring - [ ] fdmdv2_main.cpp is way too long - [ ] rename fdmdv2*.cpp -> freedv*.cpp - [ ] dlg_ptt uses ComPortsDlg name internally, rename PttDlg or similar -[ ] Add RSID - + use case, when would it be used? -[ ] clean up dialogs - + were based on auto generation code - + must be an easier/clearer way to write them - diff --git a/freedv/tags/1.2.2/RELEASE_NOTES.txt b/freedv/tags/1.2.2/RELEASE_NOTES.txt deleted file mode 100644 index 79eaeca0..00000000 --- a/freedv/tags/1.2.2/RELEASE_NOTES.txt +++ /dev/null @@ -1,7 +0,0 @@ -V1.2.2 July 2017 ----------------- - -1/ Improvements to Hamlib support, error message reporting, serial rate box. - -2/ Disabled unused UDP comms/egexp processing to clean up Options dialog. - diff --git a/freedv/tags/1.2.2/USER_MANUAL.txt b/freedv/tags/1.2.2/USER_MANUAL.txt deleted file mode 100644 index 2cc44945..00000000 --- a/freedv/tags/1.2.2/USER_MANUAL.txt +++ /dev/null @@ -1,100 +0,0 @@ -====================== -FREEDV GUI USER MANUAL -====================== - -Introduction ------------- - -This document describes additional features in the latest FreeDV -releases that haven't been documented in other sources. See also -freedv.org - -PTT Configuration ------------------ - -Tools-PTT Dialog - -Hamlib comes with a default serial rate for each radio. If your radio -has a different serial rate change the Serial Rate drop down box to -match your radio. - -When "Test" is pressed, the "Serial Params" field is populated and -displayed. This will help track down any mis-matches between Hamlib -and your radio. - -Voice Keyer ------------ - -Voice Keyer Button on Front Page -Options-PTT Dialog - -Puts FreeDV and your radio into transmit, reads a wave file of your -voice to call CQ, then switches to receive to see if anyone is -replying. If you press space bar the voice keyer stops. If a signal -with a valid sync is received for a few seconds the voice keyer stops. - -Options-PTT dialog can be used to select the wave file, set the Rx -delay, and number of times the tx/rx cycle repeats. - -The wave file for the voice keyer should be in 8kHz mono 16 bit sample -form. Use a free application such as Audacity to convert a file you -have recorded to this format. - -Test Frame Histogram --------------------- - -Test Frame Histogram tab on Front Page - -Displays BER of each carrier when in "test frame" mode. As each QPSK -carrier has 2 bits there are 2*Nc histogram points. - -Ideally all carriers will have about the same BER (+/- 20% after 5000 -total bit errors). However problems can occur with filtering in the -tx path. If one carrier has less power, then it will have a higher -BER. The errors in this carrier will tend to dominate overall -BER. For example if one carrier is attenuated due to SSB filter ripple -in the tx path then the BER on that carrier will be higher. This is -bad news for DV. - -Suggested usage: - -i) Transmit FreeDV in test frame mode. Use a 2nd rx (or -get a friend) to monitor your rx signal with FreeDV in test frame -mode. - -ii) Adjust your rx SNR to get a BER of a few % (e.g. reduce tx -power, use a short antenna for the rx, point your beam away, adjust rx -RF gain). - -iii) Monitor the error histogram for a few minutes, until you -have say 5000 total bit errors. You have a problem if the BER of any -carrier is more than 20% different from the rest. - -A typical issue will be one carrier at 1.0, the others at 0.5, -indicating the poorer carrier BER is twice the larger. - -Full Duplex Testing with loopback ---------------------------------- - -Options - Half Duplex check box - -FreeDV GUI can operate in full duplex mode which is useful for -development of listening to your own FreeDV signal as only one PC is -required. Normal operation is half duplex. - -Tx and Rx signals can be looped back via an analog connection between -the sound cards. - -On Linux, using the Alsa loopback module: - - $ sudo modprobe snd-aloop - $ ./freedv - - In Tools - Audio Config - Receive Tab - From Radio select -> Loopback: Loopback PCM (hw:1,0) - - Transmit Tab - To Radio select -> Loopback: Loopback PCM (hw:1,1) - -TODO ----- - -[ ] Merge this information into existing start up guides - diff --git a/freedv/tags/1.2.2/cmake/BuildCodec2.cmake b/freedv/tags/1.2.2/cmake/BuildCodec2.cmake deleted file mode 100644 index a02b74f3..00000000 --- a/freedv/tags/1.2.2/cmake/BuildCodec2.cmake +++ /dev/null @@ -1,25 +0,0 @@ -set(SPEEXDSP_CMAKE_ARGS -DBUILD_SHARED_LIBS=FALSE -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external/dist) - -if(USE_STATIC_SPEEXDSP) - list(APPEND SPEEXDSP_CMAKE_ARGS - -DSPEEXDSP_LIBRARIES=${CMAKE_BINARY_DIR}/external/dist/lib/libspeexdsp.a - -DSPEEXDSP_INCLUDE_DIR=${CMAKE_BINARY_DIR}/external/dist/include) -endif() - -set(CODEC2_CMAKE_ARGS -DUNITTEST=FALSE) - -if(CMAKE_CROSSCOMPILING) - set(CODEC2_CMAKE_ARGS ${CODEC2_CMAKE_ARGS} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) -endif() - -include(ExternalProject) -ExternalProject_Add(codec2 - SVN_REPOSITORY https://svn.code.sf.net/p/freetel/code/codec2/tags/0.7 - CMAKE_ARGS ${CODEC2_CMAKE_ARGS} ${SPEEXDSP_CMAKE_ARGS} - INSTALL_COMMAND "" -) -set(CODEC2_LIBRARIES - ${CMAKE_BINARY_DIR}/codec2-prefix/src/codec2-build/src/libcodec2.a) -include_directories(${CMAKE_BINARY_DIR}/codec2-prefix/src/codec2/src) -list(APPEND FREEDV_LINK_LIBS ${CODEC2_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS codec2) diff --git a/freedv/tags/1.2.2/cmake/BuildHamlib.cmake b/freedv/tags/1.2.2/cmake/BuildHamlib.cmake deleted file mode 100644 index 4166f5ae..00000000 --- a/freedv/tags/1.2.2/cmake/BuildHamlib.cmake +++ /dev/null @@ -1,20 +0,0 @@ -set(HAMLIB_TARBALL "hamlib-1.2.15.3") - -include(ExternalProject) -ExternalProject_Add(hamlib - URL http://downloads.sourceforge.net/hamlib/${HAMLIB_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(HAMLIB_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/portaudio.lib) -else(WIN32) - set(HAMLIB_LIBRARIES - ) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${HAMLIB_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS hamlib) diff --git a/freedv/tags/1.2.2/cmake/BuildPortaudio.cmake b/freedv/tags/1.2.2/cmake/BuildPortaudio.cmake deleted file mode 100644 index cc33d061..00000000 --- a/freedv/tags/1.2.2/cmake/BuildPortaudio.cmake +++ /dev/null @@ -1,52 +0,0 @@ -set(PORTAUDIO_TARBALL "pa_stable_v19_20111121") - -# required linking libraries on linux. Not sure about windows. -find_library(ALSA_LIBRARIES asound) - -if(UNIX AND NOT ALSA_LIBRARIES) - message(ERROR "Could not find alsa library which is required for portaudio. -On Linux systems try installing: - alsa-lib-devel (RPM based systems) - libasound2-dev (DEB based systems)" - ) -endif() - -# Make sure that configure knows what system we're using when cross-compiling. -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --enable-cxx --without-jack --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) -else() - set(CONFIGURE_COMMAND ./configure --enable-cxx --without-jack --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) -endif() - -include(ExternalProject) -ExternalProject_Add(portaudio - URL http://www.portaudio.com/archives/${PORTAUDIO_TARBALL}.tgz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(PORTAUDIO_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudio.a - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudiocpp.a -) -else(WIN32) - find_library(RT rt) - find_library(ASOUND asound) - set(PORTAUDIO_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudio.a - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudiocpp.a - ${RT} - ${ASOUND} - ) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) - -# Add the portaudio library to the list of libraries that must be linked. -list(APPEND FREEDV_LINK_LIBS ${PORTAUDIO_LIBRARIES}) - -# Setup a dependency so that this gets built before linking to freedv. -list(APPEND FREEDV_STATIC_DEPS portaudio) diff --git a/freedv/tags/1.2.2/cmake/BuildSamplerate.cmake b/freedv/tags/1.2.2/cmake/BuildSamplerate.cmake deleted file mode 100644 index 8b6b7a36..00000000 --- a/freedv/tags/1.2.2/cmake/BuildSamplerate.cmake +++ /dev/null @@ -1,27 +0,0 @@ -set(SAMPLERATE_TARBALL "libsamplerate-0.1.8") - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-sndfile --disable-fftw) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist) -endif() - -include(ExternalProject) -ExternalProject_Add(samplerate - URL http://www.mega-nerd.com/SRC/${SAMPLERATE_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(SAMPLERATE_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libsamplerate.a) -else(WIN32) - set(SAMPLERATE_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libsamplerate.a) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SAMPLERATE_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS samplerate) diff --git a/freedv/tags/1.2.2/cmake/BuildSndfile.cmake b/freedv/tags/1.2.2/cmake/BuildSndfile.cmake deleted file mode 100644 index c49b6388..00000000 --- a/freedv/tags/1.2.2/cmake/BuildSndfile.cmake +++ /dev/null @@ -1,26 +0,0 @@ -set(SNDFILE_TARBALL "libsndfile-1.0.25") - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-external-libs --disable-shared --disable-sqlite) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-external-libs --disable-shared --disable-external-libs) -endif() - -include(ExternalProject) -ExternalProject_Add(sndfile - URL http://www.mega-nerd.com/libsndfile/files/${SNDFILE_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) V=1 - INSTALL_COMMAND $(MAKE) install -) -if(MINGW) - set(SNDFILE_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libsndfile.a) -else() - set(SNDFILE_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libsndfile.a) -endif() - -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SNDFILE_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS sndfile) diff --git a/freedv/tags/1.2.2/cmake/BuildSpeex.cmake b/freedv/tags/1.2.2/cmake/BuildSpeex.cmake deleted file mode 100644 index 262d558f..00000000 --- a/freedv/tags/1.2.2/cmake/BuildSpeex.cmake +++ /dev/null @@ -1,26 +0,0 @@ -set(SPEEXDSP_TARBALL "speexdsp-1.2rc3.tar.gz") - -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-examples) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-examples) -endif() - -include(ExternalProject) -ExternalProject_Add(speex - URL http://downloads.xiph.org/releases/speex/${SPEEXDSP_TARBALL} - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) - -set(SPEEXDSP_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libspeexdsp.a) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SPEEXDSP_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS speex) -if(USE_STATIC_CODEC2) - add_dependencies(codec2 speex) -endif() diff --git a/freedv/tags/1.2.2/cmake/BuildWxWidgets.cmake b/freedv/tags/1.2.2/cmake/BuildWxWidgets.cmake deleted file mode 100644 index 901d8062..00000000 --- a/freedv/tags/1.2.2/cmake/BuildWxWidgets.cmake +++ /dev/null @@ -1,43 +0,0 @@ -set(WXWIDGETS_TARBALL "wxWidgets-3.0.2") - -# If we're cross-compiling then we need to set the target host manually. -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) -endif() - -# If not cross-compiling then use the built-in makefile, otherwise use standard configure. -if(MINGW AND NOT CMAKE_CROSSCOMPILING) -# set(CONFIGURE_COMMAND "true") -# set(MAKE_COMMAND $(MAKE) -C build/msw -f makefile.gcc SHARED=0 UNICODE=1 BUILD=release PREFIX=${CMAKE_BINARY_DIR}/external/dist) - set(CONFIGURE_COMMAND ./configure --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -if(NOT MINGW) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --target=${HOST} --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -include(ExternalProject) -ExternalProject_Add(wxWidgets - URL http://downloads.sourceforge.net/wxwindows/${WXWIDGETS_TARBALL}.tar.bz2 - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND ${MAKE_COMMAND} - INSTALL_COMMAND $(MAKE) install -) - -ExternalProject_Get_Property(wxWidgets install_dir) -message(STATUS "wxWidgets install dir: ${install_dir}") -if(NOT WXCONFIG) - set(WXCONFIG "${install_dir}/bin/wx-config") -endif() -if(EXISTS ${WXCONFIG}) - set(BS_WX_DONE TRUE) -endif() diff --git a/freedv/tags/1.2.2/cmake/FindPortaudio.cmake b/freedv/tags/1.2.2/cmake/FindPortaudio.cmake deleted file mode 100644 index 158e20ee..00000000 --- a/freedv/tags/1.2.2/cmake/FindPortaudio.cmake +++ /dev/null @@ -1,107 +0,0 @@ -# - Try to find Portaudio -# Once done this will define -# -# PORTAUDIO_FOUND - system has Portaudio -# PORTAUDIO_INCLUDE_DIRS - the Portaudio include directory -# PORTAUDIO_LIBRARIES - Link these to use Portaudio -# PORTAUDIO_DEFINITIONS - Compiler switches required for using Portaudio -# PORTAUDIO_VERSION - Portaudio version -# -# Copyright (c) 2006 Andreas Schneider -# -# Redistribution and use is allowed according to the terms of the New BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - - -if (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - # in cache already - set(PORTAUDIO_FOUND TRUE) -else (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - if (NOT WIN32) - include(FindPkgConfig) - pkg_check_modules(PORTAUDIO2 portaudio-2.0) - endif (NOT WIN32) - - if (PORTAUDIO2_FOUND) - set(PORTAUDIO_INCLUDE_DIRS - ${PORTAUDIO2_INCLUDE_DIRS} - ) - if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_LIBRARIES "${PORTAUDIO2_LIBRARY_DIRS}/lib${PORTAUDIO2_LIBRARIES}.dylib") - else (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_LIBRARIES - ${PORTAUDIO2_LIBRARIES} - ) - endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_VERSION - 19 - ) - set(PORTAUDIO_FOUND TRUE) - else (PORTAUDIO2_FOUND) - find_path(PORTAUDIO_INCLUDE_DIR - NAMES - portaudio.h - PATHS - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ) - - find_library(PORTAUDIO_LIBRARY - NAMES - portaudio - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - find_path(PORTAUDIO_LIBRARY_DIR - NAMES - portaudio - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - set(PORTAUDIO_INCLUDE_DIRS - ${PORTAUDIO_INCLUDE_DIR} - ) - set(PORTAUDIO_LIBRARIES - ${PORTAUDIO_LIBRARY} - ) - - set(PORTAUDIO_LIBRARY_DIRS - ${PORTAUDIO_LIBRARY_DIR} - ) - - set(PORTAUDIO_VERSION - 18 - ) - - if (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) - set(PORTAUDIO_FOUND TRUE) - endif (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) - - if (PORTAUDIO_FOUND) - if (NOT Portaudio_FIND_QUIETLY) - message(STATUS "Found Portaudio: ${PORTAUDIO_LIBRARIES}") - endif (NOT Portaudio_FIND_QUIETLY) - else (PORTAUDIO_FOUND) - if (Portaudio_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Portaudio") - endif (Portaudio_FIND_REQUIRED) - endif (PORTAUDIO_FOUND) - endif (PORTAUDIO2_FOUND) - - - # show the PORTAUDIO_INCLUDE_DIRS and PORTAUDIO_LIBRARIES variables only in the advanced view - mark_as_advanced(PORTAUDIO_INCLUDE_DIRS PORTAUDIO_LIBRARIES) - -endif (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - diff --git a/freedv/tags/1.2.2/cmake/GetDependencies.cmake.in b/freedv/tags/1.2.2/cmake/GetDependencies.cmake.in deleted file mode 100644 index 7470aa6d..00000000 --- a/freedv/tags/1.2.2/cmake/GetDependencies.cmake.in +++ /dev/null @@ -1,37 +0,0 @@ -# As this script is run in a new cmake instance, it does not have access to -# the existing cache variables. Pass them in via the configure_file command. -set(CMAKE_BINARY_DIR @CMAKE_BINARY_DIR@) -set(CMAKE_SOURCE_DIR @CMAKE_SOURCE_DIR@) -set(UNIX @UNIX@) -set(WIN32 @WIN32@) -set(CMAKE_CROSSCOMPILING @CMAKE_CROSSCOMPILING@) -set(CMAKE_FIND_LIBRARY_SUFFIXES @CMAKE_FIND_LIBRARY_SUFFIXES@) -set(CMAKE_FIND_LIBRARY_PREFIXES @CMAKE_FIND_LIBRARY_PREFIXES@) -set(CMAKE_SYSTEM_LIBRARY_PATH @CMAKE_SYSTEM_LIBRARY_PATH@) -set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) - -set(FREEDV_EXE ${CMAKE_BINARY_DIR}/src/freedv.exe) - -include(GetPrerequisites) -get_prerequisites("${FREEDV_EXE}" _deps 1 0 "" "") -foreach(_runtime ${_deps}) - message("Looking for ${_runtime}") - find_library(RUNTIME_${_runtime} ${_runtime}) - message("${RUNTIME_${_runtime}}") - if(RUNTIME_${_runtime}) - message("Looking for dependencies of ${_runtime}") - get_prerequisites("${RUNTIME_${_runtime}}" _deps2 1 0 "" "") - foreach(_runtime2 ${_deps2}) - find_library(RUNTIME_${_runtime2} ${_runtime2}) - message("${RUNTIME_${_runtime2}}") - if(RUNTIME_${_runtime2}) - file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" - TYPE EXECUTABLE FILES "${RUNTIME_${_runtime2}}") - endif() - endforeach() - endif() - if(RUNTIME_${_runtime}) - file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" - TYPE EXECUTABLE FILES "${RUNTIME_${_runtime}}") - endif() -endforeach() diff --git a/freedv/tags/1.2.2/cmake/MinGW.cmake b/freedv/tags/1.2.2/cmake/MinGW.cmake deleted file mode 100644 index 333c1dc0..00000000 --- a/freedv/tags/1.2.2/cmake/MinGW.cmake +++ /dev/null @@ -1,8 +0,0 @@ -# If we're cross-compiling then we need to set the target host manually. -if(MINGW AND CMAKE_CROSSCOMPILING) - if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(HOST x86_64-w64-mingw32) - else() - set(HOST i686-w64-mingw32) - endif() -endif() diff --git a/freedv/tags/1.2.2/cmake/Toolchain-Ubuntu-mingw32.cmake b/freedv/tags/1.2.2/cmake/Toolchain-Ubuntu-mingw32.cmake deleted file mode 100644 index 3507d720..00000000 --- a/freedv/tags/1.2.2/cmake/Toolchain-Ubuntu-mingw32.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# Sample toolchain file for building for Windows from an Ubuntu Linux system. -# -# Typical usage: -# *) install cross compiler: `sudo apt-get install mingw-w64 g++-mingw-w64` -# *) cd build -# *) cmake -DCMAKE_TOOLCHAIN_FILE=~/Toolchain-Ubuntu-mingw32.cmake .. - -set(CMAKE_SYSTEM_NAME Windows) -set(TOOLCHAIN_PREFIX i686-w64-mingw32) - -# cross compilers to use for C and C++ -set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) -set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) -set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) - -# target environment on the build host system -# set 1st to dir with the cross compiler's C/C++ headers/libs -set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) - -# modify default behavior of FIND_XXX() commands to -# search for headers/libs in the target environment and -# search for programs in the build host environment -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/freedv/tags/1.2.2/cmake/config.h.in b/freedv/tags/1.2.2/cmake/config.h.in deleted file mode 100644 index 8e3ab76b..00000000 --- a/freedv/tags/1.2.2/cmake/config.h.in +++ /dev/null @@ -1,19 +0,0 @@ -/*-------------------------------------------------------------------------- - ** This file is autogenerated from config.h.in - ** during the cmake configuration of your project. If you need to make changes - ** edit the original file NOT THIS FILE. - ** --------------------------------------------------------------------------*/ -#ifndef _CONFIGURATION_HEADER_GUARD_H_ -#define _CONFIGURATION_HEADER_GUARD_H_ - -#define SIZEOF_INT @SIZEOF_INT@ -#cmakedefine HAVE_LIMITS_H @HAVE_LIMITS_H@ -#cmakedefine HAVE_STDINT_H @HAVE_STDINT_H@ -#cmakedefine HAVE_STDDEF_H @HAVE_STDDEF_H@ -#cmakedefine HAVE_STDLIB_H @HAVE_STDLIB_H@ -#cmakedefine HAVE_STRING_H @HAVE_STRING_H@ -#cmakedefine HAVE_FLOOR @HAVE_FLOOR@ -#cmakedefine HAVE_MEMSET @HAVE_MEMSET@ -#cmakedefine HAVE_POW @HAVE_POW@ -#cmakedefine HAVE_SQRT @HAVE_SQRT@ -#endif diff --git a/freedv/tags/1.2.2/cmake/soxconfig.h.in b/freedv/tags/1.2.2/cmake/soxconfig.h.in deleted file mode 100644 index fb38608e..00000000 --- a/freedv/tags/1.2.2/cmake/soxconfig.h.in +++ /dev/null @@ -1,16 +0,0 @@ -#define PACKAGE_VERSION "14.4.2" - -#cmakedefine HAVE_BYTESWAP_H 1 -#cmakedefine HAVE_FMEMOPEN 1 -#cmakedefine HAVE_FSEEKO 1 -#cmakedefine HAVE__FSEEKOI64 1 -#cmakedefine HAVE_LTDL_H 1 -#cmakedefine HAVE_MAGIC 1 -#cmakedefine HAVE_POPEN 1 -#cmakedefine HAVE_STDINT_H 1 -#cmakedefine HAVE_INTTYPES_H 1 -#cmakedefine HAVE_STRCASECMP 1 -#cmakedefine HAVE_STRINGS_H 1 -#cmakedefine HAVE_SYS_STAT_H 1 -#cmakedefine HAVE_SYS_TYPES_H 1 -#cmakedefine HAVE_VSNPRINTF 1 diff --git a/freedv/tags/1.2.2/cmake/version.h.in b/freedv/tags/1.2.2/cmake/version.h.in deleted file mode 100644 index 43b3b7a8..00000000 --- a/freedv/tags/1.2.2/cmake/version.h.in +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef FREEDV_VER_DOT_H -#define FREEDV_VER_DOT_H 1 - -#define FREEDV_VERSION_MAJOR @FREEDV_VERSION_MAJOR@ -#define FREEDV_VERSION_MINOR @FREEDV_VERSION_MINOR@ -#define FREEDV_VERSION_PATCH @FREEDV_VERSION_PATCH@ -#define FREEDV_VERSION_SUFFIX "@FREEDV_VERSION_SUFFIX@" - -#define FREEDV_VERSION "@FREEDV_VERSION_STRING@" - -#endif //FREEDV_VER_DOT_H diff --git a/freedv/tags/1.2.2/contrib/CMakeLists.txt b/freedv/tags/1.2.2/contrib/CMakeLists.txt deleted file mode 100644 index 3f4b7e02..00000000 --- a/freedv/tags/1.2.2/contrib/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Install icons if we're on most *nix systems. -if(UNIX AND NOT APPLE) - set(ICON_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor - CACHE PATH "Prefix to use for installing icons.") - install(FILES freedv48x48.png - DESTINATION ${ICON_INSTALL_PREFIX}/48x48/apps - RENAME freedv.png) - install(FILES freedv64x64.png - DESTINATION ${ICON_INSTALL_PREFIX}/64x64/apps - RENAME freedv.png) - install(FILES freedv128x128.png - DESTINATION ${ICON_INSTALL_PREFIX}/128x128/apps - RENAME freedv.png) - install(FILES freedv256x256.png - DESTINATION ${ICON_INSTALL_PREFIX}/256x256/apps - RENAME freedv.png) - - set(DESKTOP_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/applications - CACHE PATH "Location to install desktop files.") - install(FILES freedv.desktop - DESTINATION ${DESKTOP_INSTALL_DIR}) -endif(UNIX AND NOT APPLE) diff --git a/freedv/tags/1.2.2/contrib/LICENSE b/freedv/tags/1.2.2/contrib/LICENSE deleted file mode 100644 index dc8853a7..00000000 --- a/freedv/tags/1.2.2/contrib/LICENSE +++ /dev/null @@ -1,393 +0,0 @@ -Attribution 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - d. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - g. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - i. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's - License You apply must not prevent recipients of the Adapted - Material from complying with this Public License. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public licenses. -Notwithstanding, Creative Commons may elect to apply one of its public -licenses to material it publishes and in those instances will be -considered the "Licensor." Except for the limited purpose of indicating -that material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the public -licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/freedv/tags/1.2.2/contrib/freedv.desktop b/freedv/tags/1.2.2/contrib/freedv.desktop deleted file mode 100644 index 96e82931..00000000 --- a/freedv/tags/1.2.2/contrib/freedv.desktop +++ /dev/null @@ -1,8 +0,0 @@ -[Desktop Entry] -Version=1.0 -Name=FreeDV -Exec=freedv -Icon=freedv -Type=Application -Terminal=false -Categories=GTK;GNOME;AudioVideo;Audio;HamRadio; diff --git a/freedv/tags/1.2.2/contrib/freedv.ico b/freedv/tags/1.2.2/contrib/freedv.ico deleted file mode 100644 index e6b9a2087ddae6e530734e7293da08b1d12d4dd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364646 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 B>KOn4 diff --git a/freedv/tags/1.2.2/contrib/freedv64x64.png b/freedv/tags/1.2.2/contrib/freedv64x64.png deleted file mode 100644 index eb89773bbcd93a4ed507271bd728734d0a5d7f4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6289 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

dh=(PosX@Z;=^IUxT@l?9i6IK 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 X%Xb{{VgDe5fxZ-ERb@)0jeP$Hyj%aW diff --git a/freedv/tags/1.2.2/credits.txt b/freedv/tags/1.2.2/credits.txt deleted file mode 100644 index 431e399c..00000000 --- a/freedv/tags/1.2.2/credits.txt +++ /dev/null @@ -1,13 +0,0 @@ -Credits (code or ideas borrowed from): -============================================== -Dave Witten and David Rowe (obviously) -Mel Whitten K0PFX (material and moral support) -Bruce Perens (cheerleader, promotion and publicity) -Mooneer Salem KG6AOV(Mac OSX Patch) -Soeren Straarup OZ2DAK (FreeBSD Port) -Don Mak -Steve Nance (K5FR) -Joel Stanley (Hamlib prototyping) and Mark Jessop (Mac OSX) -James Ahlstrom (Quisk) -FLDIGI -All the folks on the digital voice google group... diff --git a/freedv/tags/1.2.2/db/current b/freedv/tags/1.2.2/db/current deleted file mode 100644 index d00491fd..00000000 --- a/freedv/tags/1.2.2/db/current +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/freedv/tags/1.2.2/db/format b/freedv/tags/1.2.2/db/format deleted file mode 100644 index db06890e..00000000 --- a/freedv/tags/1.2.2/db/format +++ /dev/null @@ -1,2 +0,0 @@ -4 -layout sharded 1000 diff --git a/freedv/tags/1.2.2/db/fs-type b/freedv/tags/1.2.2/db/fs-type deleted file mode 100644 index 4fdd9531..00000000 --- a/freedv/tags/1.2.2/db/fs-type +++ /dev/null @@ -1 +0,0 @@ -fsfs diff --git a/freedv/tags/1.2.2/db/fsfs.conf b/freedv/tags/1.2.2/db/fsfs.conf deleted file mode 100644 index cc08cebb..00000000 --- a/freedv/tags/1.2.2/db/fsfs.conf +++ /dev/null @@ -1,38 +0,0 @@ -### This file controls the configuration of the FSFS filesystem. - -[memcached-servers] -### These options name memcached servers used to cache internal FSFS -### data. See http://www.danga.com/memcached/ for more information on -### memcached. To use memcached with FSFS, run one or more memcached -### servers, and specify each of them as an option like so: -# first-server = 127.0.0.1:11211 -# remote-memcached = mymemcached.corp.example.com:11212 -### The option name is ignored; the value is of the form HOST:PORT. -### memcached servers can be shared between multiple repositories; -### however, if you do this, you *must* ensure that repositories have -### distinct UUIDs and paths, or else cached data from one repository -### might be used by another accidentally. Note also that memcached has -### no authentication for reads or writes, so you must ensure that your -### memcached servers are only accessible by trusted users. - -[caches] -### When a cache-related error occurs, normally Subversion ignores it -### and continues, logging an error if the server is appropriately -### configured (and ignoring it with file:// access). To make -### Subversion never ignore cache errors, uncomment this line. -# fail-stop = true - -[rep-sharing] -### To conserve space, the filesystem can optionally avoid storing -### duplicate representations. This comes at a slight cost in -### performance, as maintaining a database of shared representations can -### increase commit times. The space savings are dependent upon the size -### of the repository, the number of objects it contains and the amount of -### duplication between them, usually a function of the branching and -### merging process. -### -### The following parameter enables rep-sharing in the repository. It can -### be switched on and off at will, but for best space-saving results -### should be enabled consistently over the life of the repository. -### rep-sharing is enabled by default. -# enable-rep-sharing = true diff --git a/freedv/tags/1.2.2/db/min-unpacked-rev b/freedv/tags/1.2.2/db/min-unpacked-rev deleted file mode 100644 index 573541ac..00000000 --- a/freedv/tags/1.2.2/db/min-unpacked-rev +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/freedv/tags/1.2.2/db/rep-cache.db b/freedv/tags/1.2.2/db/rep-cache.db deleted file mode 100644 index 63c6f0b8a5181c3954b89f8a6fb505c09e81c4e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmWFz^vNtqRY=P(%1ta$FlJz3U}R))P*7lCU|#F<%m)i%A($C- zAIbAF|6yQa`p&@go%tjdHKRhKAwcgCX!K`f7nhf3Y|1T3Ov*_uN-c;_PE5`~FqoZ# zTpdGP6+#@Hd|Vaa@(LOX3JMvC#Tg1At`Q*$e*Qol>f@sj5aj9W7!;}C?HZ{AR8f># zmRX#cpQqsI7vk#f8U$AelFUy_D^4xJDpj0Wm5Nm&wW1&~FC{f49;*tVp_+zFY~rr+ zj0~ATWfjGRIlx>UpIBOw59Y_iJrHjQXM*xD3phi=ay7kUVbs3S5Eu=C0Sy6OknPB| j{D8V<)bh~~7!3h>h5#4HveEoc&mbSQYcvD~O$Y!0OWInE diff --git a/freedv/tags/1.2.2/db/revprops/0/0 b/freedv/tags/1.2.2/db/revprops/0/0 deleted file mode 100644 index d0b90dee..00000000 --- a/freedv/tags/1.2.2/db/revprops/0/0 +++ /dev/null @@ -1,5 +0,0 @@ -K 8 -svn:date -V 27 -2012-08-21T18:27:59.389906Z -END diff --git a/freedv/tags/1.2.2/db/revprops/0/1 b/freedv/tags/1.2.2/db/revprops/0/1 deleted file mode 100644 index 0af71a2e..00000000 --- a/freedv/tags/1.2.2/db/revprops/0/1 +++ /dev/null @@ -1,13 +0,0 @@ -K 10 -svn:author -V 9 -OFA-Staff -K 8 -svn:date -V 27 -2012-08-21T18:28:08.741468Z -K 7 -svn:log -V 25 -Imported folder structure -END diff --git a/freedv/tags/1.2.2/db/revs/0/0 b/freedv/tags/1.2.2/db/revs/0/0 deleted file mode 100644 index 10f5c45f..00000000 --- a/freedv/tags/1.2.2/db/revs/0/0 +++ /dev/null @@ -1,11 +0,0 @@ -PLAIN -END -ENDREP -id: 0.0.r0/17 -type: dir -count: 0 -text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e -cpath: / - - -17 107 diff --git a/freedv/tags/1.2.2/db/revs/0/1 b/freedv/tags/1.2.2/db/revs/0/1 deleted file mode 100644 index fd802a9f..00000000 --- a/freedv/tags/1.2.2/db/revs/0/1 +++ /dev/null @@ -1,49 +0,0 @@ -id: 3-1.0.r1/0 -type: dir -count: 0 -cpath: /tags -copyroot: 0 / - -id: 0-1.0.r1/62 -type: dir -count: 0 -cpath: /trunk -copyroot: 0 / - -id: 2-1.0.r1/126 -type: dir -count: 0 -cpath: /branches -copyroot: 0 / - -PLAIN -K 8 -branches -V 16 -dir 2-1.0.r1/126 -K 4 -tags -V 14 -dir 3-1.0.r1/0 -K 5 -trunk -V 15 -dir 0-1.0.r1/62 -END -ENDREP -id: 0.0.r1/306 -type: dir -pred: 0.0.r0/17 -count: 1 -text: 1 194 99 99 7b6cc14dddba4e09be5255b475d1a0a8 -cpath: / -copyroot: 0 / - -_0.0.t0-0 add-dir false false /trunk - -_2.0.t0-0 add-dir false false /branches - -_3.0.t0-0 add-dir false false /tags - - -306 431 diff --git a/freedv/tags/1.2.2/db/transactions/.gitignore b/freedv/tags/1.2.2/db/transactions/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/db/txn-current b/freedv/tags/1.2.2/db/txn-current deleted file mode 100644 index d00491fd..00000000 --- a/freedv/tags/1.2.2/db/txn-current +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/freedv/tags/1.2.2/db/txn-current-lock b/freedv/tags/1.2.2/db/txn-current-lock deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/db/txn-protorevs/.gitignore b/freedv/tags/1.2.2/db/txn-protorevs/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/db/uuid b/freedv/tags/1.2.2/db/uuid deleted file mode 100644 index 0f362976..00000000 --- a/freedv/tags/1.2.2/db/uuid +++ /dev/null @@ -1 +0,0 @@ -a56d66ce-6468-4744-9be7-52ce95ca47a4 diff --git a/freedv/tags/1.2.2/db/write-lock b/freedv/tags/1.2.2/db/write-lock deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/debian/changelog b/freedv/tags/1.2.2/debian/changelog deleted file mode 100644 index ddfe80b5..00000000 --- a/freedv/tags/1.2.2/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -freedv (1.0-150830) unstable; urgency=low - - * Subversion snapshot of tag 1.0. - - -- Stuart Longland Sun, 30 Aug 2015 09:01:13 +1000 diff --git a/freedv/tags/1.2.2/debian/compat b/freedv/tags/1.2.2/debian/compat deleted file mode 100644 index ec635144..00000000 --- a/freedv/tags/1.2.2/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/freedv/tags/1.2.2/debian/control b/freedv/tags/1.2.2/debian/control deleted file mode 100644 index d1472b25..00000000 --- a/freedv/tags/1.2.2/debian/control +++ /dev/null @@ -1,19 +0,0 @@ -Source: fdmdv2 -Section: main -Priority: optional -Maintainer: Stuart Longland -Build-Depends: debhelper (>= 9), cmake, libcodec2-dev, libgtk2.0-dev, - libhamlib-dev, libsamplerate-dev, libasound2-dev, libao-dev, libgsm1-dev, - portaudio19-dev, libsox-dev, libsndfile1-dev, libwxgtk3.0-dev -Standards-Version: 3.9.5 -Homepage: http://www.freedv.org -#Vcs-Git: git://anonscm.debian.org/collab-maint/freedv.git -#Vcs-Browser: http://anonscm.debian.org/?p=collab-maint/freedv.git;a=summary - -Package: freedv -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libcodec2 -Description: FreeDV: Open-Source Digital Voice modem - FreeDV is a digital voice modem that can transmit voice-quality - audio digitally over HF radio links in as little as 1.25kHz - bandwidth in varying conditions. diff --git a/freedv/tags/1.2.2/debian/copyright b/freedv/tags/1.2.2/debian/copyright deleted file mode 100644 index b55a293b..00000000 --- a/freedv/tags/1.2.2/debian/copyright +++ /dev/null @@ -1,38 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: freedv -Source: - -Files: * -Copyright: - -License: - - - . - - -# If you want to use GPL v2 or later for the /debian/* files use -# the following clauses, or change it to suit. Delete these two lines -Files: debian/* -Copyright: 2015 unknown -License: GPL-2+ - This package 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 package 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, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". - -# Please also look if there are files or directories which have a -# different copyright/license attached and list them here. -# Please avoid to pick license terms that are more restrictive than the -# packaged work, as it may make Debian's contributions unacceptable upstream. diff --git a/freedv/tags/1.2.2/debian/docs b/freedv/tags/1.2.2/debian/docs deleted file mode 100644 index acfbcb33..00000000 --- a/freedv/tags/1.2.2/debian/docs +++ /dev/null @@ -1,3 +0,0 @@ -credits.txt -README.txt -README.txt diff --git a/freedv/tags/1.2.2/debian/format b/freedv/tags/1.2.2/debian/format deleted file mode 100644 index 163aaf8d..00000000 --- a/freedv/tags/1.2.2/debian/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/freedv/tags/1.2.2/debian/rules b/freedv/tags/1.2.2/debian/rules deleted file mode 100755 index ad892150..00000000 --- a/freedv/tags/1.2.2/debian/rules +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/make -f -# See debhelper(7) (uncomment to enable) -# output every command that modifies files on the build system. -#DH_VERBOSE = 1 - -# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* -DPKG_EXPORT_BUILDFLAGS = 1 -include /usr/share/dpkg/default.mk - -# see FEATURE AREAS in dpkg-buildflags(1) -#export DEB_BUILD_MAINT_OPTIONS = hardening=+all - -# see ENVIRONMENT in dpkg-buildflags(1) -# package maintainers to append CFLAGS -#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic -# package maintainers to append LDFLAGS -#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed - - -# main packaging script based on dh7 syntax -%: - dh $@ - -# debmake generated override targets -# This is example for Cmake (See http://bugs.debian.org/641051 ) -override_dh_auto_configure: - dh_auto_configure -- \ - -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) \ - -DUSE_STATIC_CODEC2=FALSE \ - -DUSE_STATIC_SPEEXDSP=FALSE diff --git a/freedv/tags/1.2.2/freedv-dev/.clang/.gitignore b/freedv/tags/1.2.2/freedv-dev/.clang/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/freedv-dev/CMakeLists.txt b/freedv/tags/1.2.2/freedv-dev/CMakeLists.txt deleted file mode 100644 index fdc1c01e..00000000 --- a/freedv/tags/1.2.2/freedv-dev/CMakeLists.txt +++ /dev/null @@ -1,463 +0,0 @@ -# -# FreeDV - HF Digital Voice for Radio Amateurs -# -# CMake configuration contributed by Richard Shaw (KF5OIM) -# Please report questions, comments, problems, or patches to the freetel -# mailing list: https://lists.sourceforge.net/lists/listinfo/freetel-codec2 -# - -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7" CACHE STRING "Minimum OS X deployment version") - -cmake_minimum_required(VERSION 2.8) - -# Prevent in-source builds to protect automake/autoconf config. -# If an in-source build is attempted, you will still need to clean up a few -# files manually. -set(CMAKE_DISABLE_SOURCE_CHANGES ON) -set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) -if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") - message(FATAL_ERROR "In-source builds in ${CMAKE_BINARY_DIR} are not " - "allowed, please remove ./CMakeCache.txt and ./CMakeFiles/, create a " - "separate build directory and run cmake from there.") -endif("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") - -# Set local module path. -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") - -project(FreeDV) - -# -# Set FreeDV version and generate src/version.h -# -set(FREEDV_VERSION_MAJOR 1) -set(FREEDV_VERSION_MINOR 2) -set(FREEDV_VERSION_PATCH 2) -set(FREEDV_VERSION ${FREEDV_VERSION_MAJOR}.${FREEDV_VERSION_MINOR}) -if(FREEDV_VERSION_PATCH) - set(FREEDV_VERSION ${FREEDV_VERSION}.${FREEDV_VERSION_PATCH}) -endif() -set(FREEDV_VERSION_SUFFIX "devel") -if(FREEDV_VERSION_SUFFIX) - set(FREEDV_VERSION_STRING "${FREEDV_VERSION} ${FREEDV_VERSION_SUFFIX}") -else() - set(FREEDV_VERSION_STRING "${FREEDV_VERSION}") -endif() -message(STATUS "FreeDV version: ${FREEDV_VERSION_STRING}") -configure_file(cmake/version.h.in src/version.h @ONLY) - -# Set default build type -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug") - message(STATUS "Build type not specified, defaulting to ${CMAKE_BUILD_TYPE}") -endif(NOT CMAKE_BUILD_TYPE) - -# Work around for not using a svn working copy. -add_definitions(-D_NO_AUTOTOOLS_) -find_program(SVN_PATH svn) -if(SVN_PATH) - execute_process(COMMAND ${SVN_PATH} info --show-item revision - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE SVN_REVISION_RESULT - OUTPUT_VARIABLE SVN_CURRENT_REVISION - ERROR_QUIET - ) -else() - set(SVN_REVISION_RESULT 1) -endif() - -if(SVN_REVISION_RESULT EQUAL 0) - string(STRIP ${SVN_CURRENT_REVISION} SVN_REVISION) - add_definitions(-DSVN_REVISION="${SVN_REVISION}") -else() - add_definitions(-DSVN_REVISION="None") -endif() - - -# Set default build flags. -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") -if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11") -endif(APPLE) - -# -# Setup cmake options -# -set(CMAKE_VERBOSE_MAKEFILE TRUE CACHE BOOL "Verbose makefile.") -set(USE_STATIC_DEPS FALSE CACHE BOOL - "Download and build static libraries instead of system libraries.") -set(USE_STATIC_PORTAUDIO FALSE CACHE BOOL - "Download and build static portaudio instead of the system library.") -set(USE_STATIC_SNDFILE FALSE CACHE BOOL - "Download and build static sndfile instead of the system library.") -set(USE_STATIC_SAMPLERATE FALSE CACHE BOOL - "Download and build static samplerate instead of the system library.") -set(USE_STATIC_CODEC2 TRUE CACHE BOOL - "Download and build static codec2 instead of the system library.") -set(USE_STATIC_SPEEXDSP TRUE CACHE BOOL - "Download and build static speex instead of the system library.") -set(BOOTSTRAP_WXWIDGETS FALSE CACHE BOOL - "Download and build static wxWidgets instead of the system library.") - -if(USE_STATIC_DEPS) - set(USE_STATIC_PORTAUDIO TRUE FORCE) - set(USE_STATIC_SNDFILE TRUE FORCE) - set(USE_STATIC_SAMPLERATE TRUE FORCE) - set(USE_STATIC_CODEC2 TRUE FORCE) -endif(USE_STATIC_DEPS) - -# -# Pull in external wxWidgets target if performing static build. -# -if(BOOTSTRAP_WXWIDGETS) - message(STATUS "Adding wxWidgets build target...") - include(cmake/BuildWxWidgets.cmake) -endif(BOOTSTRAP_WXWIDGETS) - -# -# Perform bootstrap build of wxWidgets -# -if(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) - message(STATUS "Will perform bootstrap build of wxWidgets. - After make step completes, re-run cmake and make again to perform FreeDV build.") -# -# Continue normal build if not bootstrapping wxWidgets or is already built. -# -else(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) - - -# -# Various hacks and work arounds for building under MinGW. -# -if(MINGW) - message(STATUS "System is MinGW.") - # Setup HOST variable. - include(cmake/MinGW.cmake) - # This sets up the exe icon for windows under mingw. - set(RES_FILES "") - set(RES_FILES "${CMAKE_SOURCE_DIR}/contrib/freedv.rc") - set(CMAKE_RC_COMPILER_INIT windres) - enable_language(RC) - set(CMAKE_RC_COMPILE_OBJECT - " -O coff -i -o ") - include(InstallRequiredSystemLibraries) -endif(MINGW) - -# Math library is automatic on MinGW -if(UNIX) - set(CMAKE_REQUIRED_INCLUDES math.h) - set(CMAKE_REQUIRED_LIBRARIES m) -endif(UNIX) - -# Find some standard headers and functions. -include(CheckIncludeFiles) -check_include_files("byteswap.h" HAVE_BYTESWAP_H) -check_include_files("limits.h" HAVE_LIMITS_H) -check_include_files("stddef.h" HAVE_STDDEF_H) -check_include_files("stdlib.h" HAVE_STDLIB_H) -check_include_files("string.h" HAVE_STRING_H) -check_include_files("strings.h" HAVE_STRINGS_H) -check_include_files("ltdl.h" HAVE_LTDL_H) -check_include_files("inttypes.h" HAVE_INTTYPES_H) -check_include_files("sys/stat.h" HAVE_SYS_STAT_H) -check_include_files("sys/types.h" HAVE_SYS_TYPES_H) - -include(CheckTypeSize) -check_type_size("int" SIZEOF_INT) - -include(CheckFunctionExists) -check_function_exists(floor HAVE_FLOOR) -check_function_exists(memset HAVE_MEMSET) -check_function_exists(pow HAVE_POW) -check_function_exists(sqrt HAVE_SQRT) -check_function_exists(fseeko HAVE_FSEEKO) -check_function_exists(fmemopen HAVE_FMEMOPEN) -check_function_exists(strcasecmp HAVE_STRCASECMP) -check_function_exists(vsnprintf HAVE_VSNPRINTF) - -include(CheckSymbolExists) -check_symbol_exists("_fseeki64" "stdio.h" HAVE__FSEEKI64) - -# fdmdv2_main.h requires patching to find config.h as it current looks in the -# source directory and the generated file goes in the binary directory. -configure_file ("${PROJECT_SOURCE_DIR}/cmake/config.h.in" - "${PROJECT_BINARY_DIR}/config.h" ) -include_directories(${PROJECT_BINARY_DIR}) -add_definitions(-DHAVE_CONFIG_H) - -# Config file for bundled sox sources -configure_file("${PROJECT_SOURCE_DIR}/cmake/soxconfig.h.in" - "${PROJECT_BINARY_DIR}/soxconfig.h") - -# Pthread Library -find_package(Threads REQUIRED) -message(STATUS "Threads library flags: ${CMAKE_THREAD_LIBS_INIT}") - -# -# Find codec2 -# -if(NOT USE_STATIC_CODEC2) - message(STATUS "Looking for codec2...") - # 'CONFIG' removed due to incompatibility with cmake version - # in Ubuntu 12.04 (Precise) -- Stuart Longland - find_package(codec2 QUIET) - if(codec2_FOUND) - get_target_property(CODEC2_LIBRARY codec2 LOCATION) - message(STATUS " codec2 library: ${CODEC2_LIBRARY}") - message(STATUS " codec2 headers: ${codec2_INCLUDE_DIRS}") - else() - # Try to find manually - find_path(CODEC2_INCLUDE_DIRS codec2.h - PATH_SUFFIXES codec2) - find_library(CODEC2_LIBRARY NAMES codec2) - if(CODEC2_LIBRARY AND CODEC2_INCLUDE_DIRS) - message(STATUS " codec2 library: ${CODEC2_LIBRARY}") - message(STATUS " codec2 headers: ${CODEC2_INCLUDE_DIRS}") - list(APPEND FREEDV_LINK_LIBS ${CODEC2_LIBRARY}) - include_directories(${CODEC2_INCLUDE_DIRS}) - else() - message(FATAL_ERROR "codec2 library not found. -Linux: -Codec2 may not be in your distribution so build yourself or use the cmake option to build statically into FreeDV. -Windws: -It's easiest to use the cmake option: USE_STATIC_CODEC2" - ) - endif() - endif() -else(NOT USE_STATIC_CODEC2) - message(STATUS "Will attempt static build of codec2.") - include(cmake/BuildCodec2.cmake) -endif(NOT USE_STATIC_CODEC2) - -# -# Find or build portaudio Library -# -if(NOT USE_STATIC_PORTAUDIO) - message(STATUS "Looking for portaudio...") - find_package(Portaudio REQUIRED) - if(PORTAUDIO_FOUND) - message(STATUS " portaudio library: ${PORTAUDIO_LIBRARIES}") - message(STATUS " portaudio headers: ${PORTAUDIO_INCLUDE_DIRS}") - list(APPEND FREEDV_LINK_LIBS ${PORTAUDIO_LIBRARIES}) - include_directories(${PORTAUDIO_INCLUDE_DIRS}) - else() - message(FATAL_ERROR "portaudio library not found. -On Linux systems try installing: - portaudio-devel (RPM based systems) - libportaudio-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_PORTAUDIO" - ) - endif() - if(NOT ${PORTAUDIO_VERSION} EQUAL 19) - message(WARNING "Portaudio versions other than 19 are known to have issues. You have been warned!") - endif() -else(NOT USE_STATIC_PORTAUDIO) - message(STATUS "Will attempt static build of portaudio.") - include(cmake/BuildPortaudio.cmake) -endif(NOT USE_STATIC_PORTAUDIO) - -# -# Hamlib library -# -message(STATUS "Looking for hamlib...") -find_path(HAMLIB_INCLUDE_DIR hamlib/rig.h) -find_library(HAMLIB_LIBRARY hamlib PATH_SUFFIXES hamlib) -message(STATUS "Hamlib library: ${HAMLIB_LIBRARY}") -message(STATUS "Hamlib headers: ${HAMLIB_INCLUDE_DIR}") -if(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - message(STATUS "Hamlib library found.") - include_directories(${HAMLIB_INCLUDE_DIR}) - list(APPEND FREEDV_LINK_LIBS ${HAMLIB_LIBRARY}) -else(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - message(FATAL_ERROR "hamlib not found. -On Linux systems try installing: - hamlib-devel (RPM based systems) - libhamlib-dev (DEB based systems)" - ) -endif(HAMLIB_LIBRARY AND HAMLIB_INCLUDE_DIR) - - -# -# Samplerate Library -# -if(NOT USE_STATIC_SAMPLERATE) - message(STATUS "Looking for samplerate...") - find_library(LIBSAMPLERATE samplerate) - find_path(LIBSAMPLERATE_INCLUDE_DIR samplerate.h) - message(STATUS " samplerate library: ${LIBSAMPLERATE}") - message(STATUS " samplerate headers: ${LIBSAMPLERATE_INCLUDE_DIR}") - if(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) - list(APPEND FREEDV_LINK_LIBS ${LIBSAMPLERATE}) - include_directories(${LIBSAMPLERATE_INCLUDE_DIR}) - else(LIBSTAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) - message(FATAL_ERROR "samplerate library not found. -On Linux systems try installing: - samplerate-devel (RPM based systems) - libsamplerate-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_SAMPLERATE" - ) - endif(LIBSAMPLERATE AND LIBSAMPLERATE_INCLUDE_DIR) -else(NOT USE_STATIC_SAMPLERATE) - message(STATUS "Will attempt static build of samplerate.") - include(cmake/BuildSamplerate.cmake) -endif(NOT USE_STATIC_SAMPLERATE) - -# -# sndfile Library -# -if(NOT USE_STATIC_SNDFILE) - message(STATUS "Looking for sndfile...") - find_library(LIBSNDFILE sndfile) - find_path(LIBSNDFILE_INCLUDE_DIR sndfile.h) - message(STATUS " sndfile library: ${LIBSNDFILE}") - message(STATUS " sndfile headers: ${LIBSNDFILE_INCLUDE_DIR}") - if(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) - list(APPEND FREEDV_LINK_LIBS ${LIBSNDFILE}) - else(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) - message(FATAL_ERROR "sndfile library not found. -On Linux systems try installing: - libsndfile-devel (RPM based systems) - libsndfile-dev (DEB based systems) -On Windows it's easiest to use the cmake option: USE_STATIC_SNDFILE" - ) - endif(LIBSNDFILE AND LIBSNDFILE_INCLUDE_DIR) -else(NOT USE_STATIC_SNDFILE) - message(STATUS "Will attempt static build of sndfile.") - include(cmake/BuildSndfile.cmake) -endif(NOT USE_STATIC_SNDFILE) - -# -# Find wxWidgets -# -if(NOT BOOTSTRAP_WXWIDGETS) - set(WXCONFIG "" CACHE FILEPATH "Location of wx-config binary.") - set(WXRC "" CACHE FILEPATH "Location of wxrc binary.") -endif(NOT BOOTSTRAP_WXWIDGETS) -#if(BOOTSTRAP_WXWIDGETS) -# set(WXCONFIG "${CMAKE_BINARY_DIR}/external/dist/bin/wx-config") -# set(WXRC "${CMAKE_BINARY_DIR}/external/dist/bin/wxrc") -# list(APPEND FREEDV_STATIC_DEPS wxWidgets) -#endif(BOOTSTRAP_WXWIDGETS) -message(STATUS "Looking for wxWidgets...") -if(WXCONFIG) - message(STATUS "wx-config: ${WXCONFIG}") - set(wxWidgets_CONFIG_EXECUTABLE ${WXCONFIG}) -endif(WXCONFIG) -if(WXRC) - message(STATUS "wxrc: ${WXRC}") - set(wxWidgets_wxrc_EXECUTABLE ${WXRC}) -endif(WXRC) -set(WX_VERSION_MIN 3.0.0) -find_package(wxWidgets REQUIRED core base aui html net adv) -execute_process(COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" --version - OUTPUT_VARIABLE WX_VERSION) -string(STRIP ${WX_VERSION} WX_VERSION) -if(WX_VERSION VERSION_EQUAL ${WX_VERSION_MIN} - OR WX_VERSION VERSION_GREATER ${WX_VERSION_MIN}) - message(STATUS "wxWidgets version: ${WX_VERSION}") -else() - message(FATAL_ERROR "wxWidgets must be installed on your system. -Please check that wx-config is in path, the directory -where wxWidgets libraries are installed (returned by -'wx-config --libs' or 'wx-config --static --libs' command) -is in LD_LIBRARY_PATH or equivalent variable and -wxWidgets version is ${WX_VERSION_MIN} or above.") -endif() -if(wxWidgets_FOUND) - include("${wxWidgets_USE_FILE}") - list(APPEND FREEDV_LINK_LIBS ${wxWidgets_LIBRARIES}) -endif(wxWidgets_FOUND) - -# -# Find speex library -# -if(NOT USE_STATIC_SPEEXDSP) - message(STATUS "Looking for Speex DSP library.") - find_path(SPEEXDSP_INCLUDE_DIR NAMES speex/speex.h speex/speexdsp_types.h) - find_library(SPEEXDSP_LIBRARY speexdsp) - message(STATUS " Speex DSP headers: ${SPEEXDSP_INCLUDE_DIR}") - message(STATUS " Speex DSP library: ${SPEEXDSP_LIBRARY}") - if(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) - include_directories(${SPEEXDSP_INCLUDE_DIR}) - list(APPEND FREEDV_LINK_LIBS ${SPEEXDSP_LIBRARY}) - else(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) - message(FATAL_ERROR "Speex DSP library not found!") - endif(SPEEXDSP_INCLUDE_DIR AND SPEEXDSP_LIBRARY) -else() - message(STATUS "Will attempt static build of speex.") - include(cmake/BuildSpeex.cmake) -endif() - -# -# Find libdl for dlopen/dlclose -# -if(UNIX) - message(STATUS "Looking for dl library.") - find_library(DL_LIBRARY dl) - if(DL_LIBRARY) - message(STATUS " dl library: ${DL_LIBRARY}") - list(APPEND FREEDV_LINK_LIBS ${DL_LIBRARY}) - else() - message(FATAL_ERROR "dl library not found. -On Linux systems try installing: - glibc-devel (RPM based systems) - glibc-dev (DEB based systems)" - ) - endif() -endif(UNIX) - - -#Freedv -add_subdirectory(src) - -# Icons and desktop file -add_subdirectory(contrib) - -message(STATUS "Build type will be: ${CMAKE_BUILD_TYPE}") - -# -# Cpack NSIS configuration for Windows. -# -if(WIN32) - # Detect if we're doing a 32-bit or 64-bit windows build. - if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(CMAKE_CL_64 TRUE) - set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") - endif() - if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - set(CPACK_STRIP_FILES TRUE) - endif() - configure_file(cmake/GetDependencies.cmake.in cmake/GetDependencies.cmake - @ONLY - ) - install(SCRIPT ${CMAKE_BINARY_DIR}/cmake/GetDependencies.cmake) - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "HF Digital Voice for Radio Amateurs") - set(CPACK_PACKAGE_VENDOR "CMake") - #set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README") - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") - set(CPACK_PACKAGE_VERSION_MAJOR ${FREEDV_VERSION_MAJOR}) - set(CPACK_PACKAGE_VERSION_MINOR ${FREEDV_VERSION_MINOR}) - # CPack expects a patch level version so set it here and override if we - # are actually setting one. - set(CPACK_PACKAGE_VERSION_PATCH 0) - if(FREEDV_VERSION_PATCH) - set(CPACK_PACKAGE_VERSION_PATCH ${FREEDV_VERSION_PATCH}) - endif() - if(FREEDV_VERSION_SUFFIX) - set(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${FREEDV_VERSION_SUFFIX}") - endif() - # There is a bug in NSI that does not handle full unix paths properly. Make - # sure there is at least one set of four (4) backlasshes. - #set(CPACK_PACKAGE_ICON "${CMake_SOURCE_DIR}/Utilities/Release\\\\InstallIcon.bmp") - set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\freedv.exe") - set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY}") - set(CPACK_NSIS_PACKAGE_NAME "FreeDV") - set(CPACK_PACKAGE_EXECUTABLES freedv;FreeDV) - set(CPACK_NSIS_URL_INFO_ABOUT "http://freedv.org") - set(CPACK_NSIS_MODIFY_PATH OFF) - set(CPACK_NSIS_MENU_LINKS - "http://freedv.org" "FreeDV Homepage") - include(CPack) -endif(WIN32) - -endif(BOOTSTRAP_WXWIDGETS AND NOT EXISTS ${WXCONFIG}) diff --git a/freedv/tags/1.2.2/freedv-dev/COPYING b/freedv/tags/1.2.2/freedv-dev/COPYING deleted file mode 100644 index cfd4e991..00000000 --- a/freedv/tags/1.2.2/freedv-dev/COPYING +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see - . - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/freedv/tags/1.2.2/freedv-dev/README.osx b/freedv/tags/1.2.2/freedv-dev/README.osx deleted file mode 100644 index c71544ff..00000000 --- a/freedv/tags/1.2.2/freedv-dev/README.osx +++ /dev/null @@ -1,107 +0,0 @@ -Building under OSX is similar to building under linux, but there are some additional steps that need to be performed to produce a working app-bundle. - -For the following instructions, I'm assuming you will be placing everything in: -/Users//Dev/ - -1/ DEPENDENCIES -Using Macports, most of the appropriate dependencies can be installed by: - -$ sudo port install subversion git libtool libsamplerate sox portaudio dylibbundler cmake - -It should be fairly similar using HomeBrew, but you will need to replace all the /opt/ paths in the following instructions. - -1.1/ HAMLIB -First, we will need to build hamlib from source, as we need hamlib to be statically compiled (Macports won't do this..) - -$ git clone git://git.code.sf.net/p/hamlib/code hamlib-code -$ cd hamlib-code - -You will now need to edit line 12 of autogen.sh, to change "libtoolize" to "glibtoolize" - -$ ./autogen.sh -$ ./configure --disable-shared --prefix /Users//Dev/hamlib -$ make -$ make install - -You should now have an installation of hamlib in ~/Dev/hamlib - -Just in case you have hamlib installed via Macports, it may be a good idea to run -$ sudo port deactivate hamlib - -1.2/ WXWIDGETS -To be able to produce an appbundle, we need wxWidgets to be build statically. Again, Macports won't do this out of the box. - -Edit the wxWidgets-3.0 port file using: -$ sudo port edit wxWidgets-3.0 - -and add the following to the bottom of the file: - -variant static description { build a static version of the libraries with some other options... } { - configure.args-append --enable-std_iostreams - configure.args-append --disable-shared - configure.args-delete --with-sdl - configure.args-delete --with-opengl - set installtype release-static -} - -Now you can build and install a static variant of wxWidgets with: -$ sudo port install wxWidgets-3.0 +static - -Note: This will probably break anything else which is using wxWidgets. Once you have finished building FreeDV, you may -want to go back to the dynamically compiled version using: -$ sudo port install wxWidgets-3.0 - -HomeBrew Users: Anyone know how to do the above? - -1.3/ CODEC2 LIBRARIES -The FreeDV CMake procedure will automatically checkout and compile Codec2. -If you want to build and install your own copy (i.e. for access to the command-line tools), you can do so: - -$ wget http://files.freedv.org/codec2/codec2-0.4.tar.gz -or -$ svn checkout https://svn.code.sf.net/p/freetel/code/codec2-dev/ - -$ cd codec2-0.4 -or -cd codec2-dev -$ mkdir build_osx && cd build_osx -$ cmake ../ && make -$ sudo make install - -3/ BUILDING FREEDV -Get the FreeDV source by either: - -Getting the current 'stable' release (1.0): -$ wget http://files.freedv.org/freedv/freedv-1.0.tar.gz -$ tar -xzf freedv-1.0.tar.gz - -or - -Checking the latest revision out from SVN: -$ svn checkout https://svn.code.sf.net/p/freetel/code/freedv-dev/ - -$ cd freedv-1.0 -or -$ cd freedv-dev - -$ mkdir build_osx && cd build_osx - -Assuming you are intending on building Codec2 as part of the build process, run: - -$ cmake -DWXCONFIG=/opt/local/Library/Frameworks/wxWidgets.framework/Versions/wxWidgets/3.0/lib/wx/config/osx_cocoa-unicode-static-3.0 -DCMAKE_EXE_LINKER_FLAGS="-L/opt/local/lib" -DHAMLIB_INCLUDE_DIR=../../hamlib/include -DHAMLIB_LIBRARY=../../hamlib/lib/libhamlib.a ../ - -Then, build FreeDV: -$ make - -The build process will create an appbundle (FreeDV.app) and a compressed disk image (FreeDV.dmg) in ./build_osx/src -Move these to wherever you want, and run! - -Happy DVing! - -Acknowledgements: -A big thank you to Mooneer Salem, K6AQ, for walking me through this process, and figuring out how to solve the wxWidgets and Hamlib issues. - -Please e-mail any corrections to either the digitalvoice google group list, or myself, at: -vk5qi(at)rfhead.net -Mark Jessop VK5QI - diff --git a/freedv/tags/1.2.2/freedv-dev/README.txt b/freedv/tags/1.2.2/freedv-dev/README.txt deleted file mode 100644 index 26e731d1..00000000 --- a/freedv/tags/1.2.2/freedv-dev/README.txt +++ /dev/null @@ -1,233 +0,0 @@ -================================== - FreeDV GUI README.txt -================================== - -This document describes how to build the FreeDV GUI program for -various operating systems. See also: - - http://freedv.org - introduction, documentation, downloads - RELEASE_NOTES.txt - changes made to each version - USER_MANUAL.txt - FreeDV GUI Manual - -================================== - Building and installing on Linux -================================== - -First the basics: - - $ sudo apt-get install build-essential cmake subversion - -To install the required development libraries instead of building them -statically: - -Debian/Ubuntu: - - $ sudo apt-get install libwxgtk3.0-dev portaudio19-dev \ - libhamlib-dev libsamplerate-dev libasound2-dev libao-dev libgsm1-dev \ - libsndfile-dev - -Fedora: - - $ sudo dnf install wxGTK3-devel portaudio-devel libsamplerate-devel \ - libsndfile-devel speexdsp-devel hamlib-devel alsa-lib-devel libao-devel \ - gsm-devel - - -RHEL/CentOS and derivitves (requires Fedora EPEL repository) - - $ sudo yum install wxGTK3-devel portaudio-devel libsamplerate-devel \ - libsndfile-devel speexdsp-devel hamlib-devel alsa-lib-devel libao-devel \ - gsm-devel - - -Quickstart 1 ------------- - -1/ Using a modern Linux, and the development library packages - installed above: - - $ cd /path/to/freedv - $ mkdir build_linux - $ cd build_linux - $ cmake ../ - $ make - $ ./src/freedv - -Quickstart 2 ------------- - -Builds static versions of wxWidgets, portaudio, codec2-dev, which are commonly -missing on older Linux systems. - -1/ Assumes static build of wxWidgets and the freedv-dev source is checked out into ~/freedv-dev: - - $ cd ~/freedv-dev - $ mkdir build_linux - $ cd build_linux - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE ../ - $ make - -2/ Then you can configure FreeDV using your local codec-dev, something like: - - $ cmake -DCMAKE_BUILD_TYPE=Debug -DBOOTSTRAP_WXWIDGETS=TRUE -DCODEC2_INCLUDE_DIRS=/path/to/codec2-dev/src -DCODEC2_LIBRARY=/path/to/codec2-dev/build_linux/src/libcodec2.so -DUSE_STATIC_CODEC2=FALSE -DUSE_STATIC_PORTAUDIO=TRUE ../ - -3/ OR build a local copy of codec2-dev: - - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE -DUSE_STATIC_CODEC2=TRUE -DUSE_STATIC_PORTAUDIO=TRUE ../ - -4/ Build and run FreeDV: - - $ make - $ ./src/freedv - -======================================================= - Building for Windows on Ubuntu Linux (Cross compiling) -======================================================= - -1/ Install the cross compiling toolchain: - - $ sudo apt-get install mingw-w64 - -2/ Patch cmake using: http://www.cmake.org/gitweb?p=stage/cmake.git;a=patch;h=33286235048495ceafb636d549d9a4e8891967ae - -3/ Checkout a fresh copy of codec2-dev and build for Windows, pointing to the generate_codebook built by a linux build of generate_codebook, using this cmake line - - $ cmake .. -DCMAKE_TOOLCHAIN_FILE=/home/david/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake -DUNITTEST=FALSE -DGENERATE_CODEBOOK=/home/david/codec2-dev/build_linux/src/generate_codebook - -4/ Build WxWidgets - - $ cd /path/to/freedv-dev - $ mkdir build_windows - $ cd build_windows - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE .. -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-Ubuntu-mingw32.cmake -DCMAKE_BUILD_TYPE=Debug - $ make - -5/ Download and install the Windows version of Hamlib: - - $ wget http://internode.dl.sourceforge.net/project/hamlib/hamlib/1.2.15.3/hamlib-win32-1.2.15.3.zip - $ unzip hamlib-win32-1.2.15.3.zip - -6/ Build All the libraries and FreeDV: - - $ cmake -DBOOTSTRAP_WXWIDGETS=TRUE -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-Ubuntu-mingw32.cmake -DUSE_STATIC_PORTAUDIO=TRUE -DUSE_STATIC_SNDFILE=TRUE -DUSE_STATIC_SAMPLERATE=TRUE -DUSE_STATIC_CODEC2=FALSE -DCODEC2_INCLUDE_DIRS=/home/david/tmp/codec2-dev/src -DCODEC2_LIBRARY=/home/david/tmp/codec2-dev/build_windows/src/libcodec2.dll.a -DHAMLIB_INCLUDE_DIR=hamlib-win32-1.2.15.3/include -DHAMLIB_LIBRARY=hamlib-win32-1.2.15.3/lib/gcc/libhamlib.dll.a -DCMAKE_BUILD_TYPE=Debug .. - $ make - -7/ Test on Linux with "wine", this will tell you if any DLLs are missing: - - $ wine src/freedv.exe - -8/ When moving to an actual Windows machine, I needed: - - /usr/lib/gcc/i686-w64-mingw32/4.8/libstdc++-6.dll - /usr/lib/gcc/i686-w64-mingw32/4.8/libgcc_s_sjlj-1.dll - /usr/i686-w64-mingw32/lib/libwinpthread-1.dll - - Wine seems to find these automagically, so I found them on my system by - looking at ~/.wine/system.reg for PATH: - - [System\\CurrentControlSet\\Control\\Session Manager\\Environment] 1423800803 - "PATH"=str(2):"C:\\windows\\system32;C:\\windows;C:\\windows\\system32\\wbem;Z:\\usr\\i686-w64-mingw32\\lib;Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\4.8" - - -==================================== - Building and installing on Windows -==================================== - -The windows build is similar to linux and follows the same basic workflow, -however, while codec2 and FreeDV (freedv) build well on windows, some of the -dependencies do not. For that reson current windows releases are cross-compiled -from linux. - -Only MinGW is supported. While it is likely possible to perform a pure MinGW -build, installing MSYS2 will make your life easier. - -CMake may not automatically detect that you're in the MSYS environment. If this -occurs you need to pass cmake the proper generator: - -cmake -G"MSYS Makefiles" [other options] - -=============================== - Bootstrapping wxWidgets build -=============================== - -If wxWidgets (>= 3.0) is not available then one option is to have CMake boot- -strap the build for FreeDV. - -This is required because the tool wx-config is used to get the correct compiler -and linker flags of the wxWidgets components needed by FreeDV. Since this is -normally done at configure time, not during "make", it is not possible for CMake -or have this information prior to building wxWidgets. - -In order to work around this issue you can "bootstrap" the wxWidgets build using -the CMake option, "BOOTSTRAP_WXWIDGETS". wxWidgets will be built using static -libraries. - -NOTE: This forces "USE_STATIC_WXWIDGETS" to be true internally regarless of the -value set manually. - -(from any directory, but empty directory outside of the source is prefered.) -$ cmake -DBOOTSTRAP_WXWIDGETS=TRUE /path/to/freedv -$ make -(wxWidgets is downloaded and built) -$ cmake . -(wxWidgets build should be detected) -$ make -(if all goes well, as root) -$ make install - -==================================== - Building and installing on OSX -==================================== - -Pls see README.osx - -==================================== - Building and installing on FreeBSD -==================================== - -As per "Quickstart 2" above but change build_linux to build_freebsd - -======= -Editing -======= - -Please make sure your text editor does not insert tabs, and -used indents of 4 spaces. The following .emacs code was used to -configure emacs: - -(setq-default indent-tabs-mode nil) - -(add-hook 'c-mode-common-hook - (function (lambda () - (setq c-basic-offset 4) - ))) - -FreeDV GUI TODO List --------------------- - -[ ] Ubuntu packaging -[ ] default sound card in/out setting for rx out of the box -[ ] When application close on windows while "Start" down sometimes crashes - + Also on Linux it reports an unterminated thread when exiting -[ ] Tool-Audio Config Dialog sound device names truncated on Windows -[ ] Serialport::closeport() on Linux takes about 1 second - + delays 'Stop' on main window test on Tools-PTT Test -[ ] Voice keyer file name at bottom on main screen truncated - + need a bigger field -[ ] Start/Stop file rec/playback, work out a better UI, - maybe buttons on front page -[ ] feature for evaluating yr own sound quality - + trap bad mic response/levels - + zero in on different sound quality from different users -[ ] feeding audio over UDP say from from gqrx - + could also be used to netcat stored files -[ ] refactoring - [ ] fdmdv2_main.cpp is way too long - [ ] rename fdmdv2*.cpp -> freedv*.cpp - [ ] dlg_ptt uses ComPortsDlg name internally, rename PttDlg or similar -[ ] Add RSID - + use case, when would it be used? -[ ] clean up dialogs - + were based on auto generation code - + must be an easier/clearer way to write them - diff --git a/freedv/tags/1.2.2/freedv-dev/RELEASE_NOTES.txt b/freedv/tags/1.2.2/freedv-dev/RELEASE_NOTES.txt deleted file mode 100644 index 79eaeca0..00000000 --- a/freedv/tags/1.2.2/freedv-dev/RELEASE_NOTES.txt +++ /dev/null @@ -1,7 +0,0 @@ -V1.2.2 July 2017 ----------------- - -1/ Improvements to Hamlib support, error message reporting, serial rate box. - -2/ Disabled unused UDP comms/egexp processing to clean up Options dialog. - diff --git a/freedv/tags/1.2.2/freedv-dev/USER_MANUAL.txt b/freedv/tags/1.2.2/freedv-dev/USER_MANUAL.txt deleted file mode 100644 index 2cc44945..00000000 --- a/freedv/tags/1.2.2/freedv-dev/USER_MANUAL.txt +++ /dev/null @@ -1,100 +0,0 @@ -====================== -FREEDV GUI USER MANUAL -====================== - -Introduction ------------- - -This document describes additional features in the latest FreeDV -releases that haven't been documented in other sources. See also -freedv.org - -PTT Configuration ------------------ - -Tools-PTT Dialog - -Hamlib comes with a default serial rate for each radio. If your radio -has a different serial rate change the Serial Rate drop down box to -match your radio. - -When "Test" is pressed, the "Serial Params" field is populated and -displayed. This will help track down any mis-matches between Hamlib -and your radio. - -Voice Keyer ------------ - -Voice Keyer Button on Front Page -Options-PTT Dialog - -Puts FreeDV and your radio into transmit, reads a wave file of your -voice to call CQ, then switches to receive to see if anyone is -replying. If you press space bar the voice keyer stops. If a signal -with a valid sync is received for a few seconds the voice keyer stops. - -Options-PTT dialog can be used to select the wave file, set the Rx -delay, and number of times the tx/rx cycle repeats. - -The wave file for the voice keyer should be in 8kHz mono 16 bit sample -form. Use a free application such as Audacity to convert a file you -have recorded to this format. - -Test Frame Histogram --------------------- - -Test Frame Histogram tab on Front Page - -Displays BER of each carrier when in "test frame" mode. As each QPSK -carrier has 2 bits there are 2*Nc histogram points. - -Ideally all carriers will have about the same BER (+/- 20% after 5000 -total bit errors). However problems can occur with filtering in the -tx path. If one carrier has less power, then it will have a higher -BER. The errors in this carrier will tend to dominate overall -BER. For example if one carrier is attenuated due to SSB filter ripple -in the tx path then the BER on that carrier will be higher. This is -bad news for DV. - -Suggested usage: - -i) Transmit FreeDV in test frame mode. Use a 2nd rx (or -get a friend) to monitor your rx signal with FreeDV in test frame -mode. - -ii) Adjust your rx SNR to get a BER of a few % (e.g. reduce tx -power, use a short antenna for the rx, point your beam away, adjust rx -RF gain). - -iii) Monitor the error histogram for a few minutes, until you -have say 5000 total bit errors. You have a problem if the BER of any -carrier is more than 20% different from the rest. - -A typical issue will be one carrier at 1.0, the others at 0.5, -indicating the poorer carrier BER is twice the larger. - -Full Duplex Testing with loopback ---------------------------------- - -Options - Half Duplex check box - -FreeDV GUI can operate in full duplex mode which is useful for -development of listening to your own FreeDV signal as only one PC is -required. Normal operation is half duplex. - -Tx and Rx signals can be looped back via an analog connection between -the sound cards. - -On Linux, using the Alsa loopback module: - - $ sudo modprobe snd-aloop - $ ./freedv - - In Tools - Audio Config - Receive Tab - From Radio select -> Loopback: Loopback PCM (hw:1,0) - - Transmit Tab - To Radio select -> Loopback: Loopback PCM (hw:1,1) - -TODO ----- - -[ ] Merge this information into existing start up guides - diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/BuildCodec2.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/BuildCodec2.cmake deleted file mode 100644 index 038826d5..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/BuildCodec2.cmake +++ /dev/null @@ -1,26 +0,0 @@ -set(SPEEXDSP_CMAKE_ARGS -DBUILD_SHARED_LIBS=FALSE -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external/dist) - -if(USE_STATIC_SPEEXDSP) - list(APPEND SPEEXDSP_CMAKE_ARGS - -DSPEEXDSP_LIBRARIES=${CMAKE_BINARY_DIR}/external/dist/lib/libspeexdsp.a - -DSPEEXDSP_INCLUDE_DIR=${CMAKE_BINARY_DIR}/external/dist/include) -endif() - -set(CODEC2_CMAKE_ARGS -DUNITTEST=FALSE) - -if(CMAKE_CROSSCOMPILING) - set(CODEC2_CMAKE_ARGS ${CODEC2_CMAKE_ARGS} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) -endif() - -include(ExternalProject) -ExternalProject_Add(codec2 - SVN_REPOSITORY https://svn.code.sf.net/p/freetel/code/codec2-dev - CMAKE_ARGS ${CODEC2_CMAKE_ARGS} ${SPEEXDSP_CMAKE_ARGS} - CMAKE_CACHE_ARGS -DCMAKE_OSX_DEPLOYMENT_TARGET:string=10.7 - INSTALL_COMMAND "" -) -set(CODEC2_LIBRARIES - ${CMAKE_BINARY_DIR}/codec2-prefix/src/codec2-build/src/libcodec2.a) -include_directories(${CMAKE_BINARY_DIR}/codec2-prefix/src/codec2/src) -list(APPEND FREEDV_LINK_LIBS ${CODEC2_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS codec2) diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/BuildHamlib.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/BuildHamlib.cmake deleted file mode 100644 index 4166f5ae..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/BuildHamlib.cmake +++ /dev/null @@ -1,20 +0,0 @@ -set(HAMLIB_TARBALL "hamlib-1.2.15.3") - -include(ExternalProject) -ExternalProject_Add(hamlib - URL http://downloads.sourceforge.net/hamlib/${HAMLIB_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(HAMLIB_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/portaudio.lib) -else(WIN32) - set(HAMLIB_LIBRARIES - ) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${HAMLIB_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS hamlib) diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/BuildPortaudio.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/BuildPortaudio.cmake deleted file mode 100644 index cc33d061..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/BuildPortaudio.cmake +++ /dev/null @@ -1,52 +0,0 @@ -set(PORTAUDIO_TARBALL "pa_stable_v19_20111121") - -# required linking libraries on linux. Not sure about windows. -find_library(ALSA_LIBRARIES asound) - -if(UNIX AND NOT ALSA_LIBRARIES) - message(ERROR "Could not find alsa library which is required for portaudio. -On Linux systems try installing: - alsa-lib-devel (RPM based systems) - libasound2-dev (DEB based systems)" - ) -endif() - -# Make sure that configure knows what system we're using when cross-compiling. -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --enable-cxx --without-jack --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) -else() - set(CONFIGURE_COMMAND ./configure --enable-cxx --without-jack --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) -endif() - -include(ExternalProject) -ExternalProject_Add(portaudio - URL http://www.portaudio.com/archives/${PORTAUDIO_TARBALL}.tgz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(PORTAUDIO_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudio.a - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudiocpp.a -) -else(WIN32) - find_library(RT rt) - find_library(ASOUND asound) - set(PORTAUDIO_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudio.a - ${CMAKE_BINARY_DIR}/external/dist/lib/libportaudiocpp.a - ${RT} - ${ASOUND} - ) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) - -# Add the portaudio library to the list of libraries that must be linked. -list(APPEND FREEDV_LINK_LIBS ${PORTAUDIO_LIBRARIES}) - -# Setup a dependency so that this gets built before linking to freedv. -list(APPEND FREEDV_STATIC_DEPS portaudio) diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/BuildSamplerate.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/BuildSamplerate.cmake deleted file mode 100644 index 8b6b7a36..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/BuildSamplerate.cmake +++ /dev/null @@ -1,27 +0,0 @@ -set(SAMPLERATE_TARBALL "libsamplerate-0.1.8") - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-sndfile --disable-fftw) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist) -endif() - -include(ExternalProject) -ExternalProject_Add(samplerate - URL http://www.mega-nerd.com/SRC/${SAMPLERATE_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) -if(WIN32) - set(SAMPLERATE_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libsamplerate.a) -else(WIN32) - set(SAMPLERATE_LIBRARIES - ${CMAKE_BINARY_DIR}/external/dist/lib/libsamplerate.a) -endif(WIN32) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SAMPLERATE_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS samplerate) diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/BuildSndfile.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/BuildSndfile.cmake deleted file mode 100644 index c49b6388..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/BuildSndfile.cmake +++ /dev/null @@ -1,26 +0,0 @@ -set(SNDFILE_TARBALL "libsndfile-1.0.25") - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-external-libs --disable-shared --disable-sqlite) -else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-external-libs --disable-shared --disable-external-libs) -endif() - -include(ExternalProject) -ExternalProject_Add(sndfile - URL http://www.mega-nerd.com/libsndfile/files/${SNDFILE_TARBALL}.tar.gz - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) V=1 - INSTALL_COMMAND $(MAKE) install -) -if(MINGW) - set(SNDFILE_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libsndfile.a) -else() - set(SNDFILE_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libsndfile.a) -endif() - -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SNDFILE_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS sndfile) diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/BuildSpeex.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/BuildSpeex.cmake deleted file mode 100644 index 8d287ead..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/BuildSpeex.cmake +++ /dev/null @@ -1,30 +0,0 @@ -set(SPEEXDSP_TARBALL "speexdsp-1.2rc3.tar.gz") - -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-examples) -else() - if(APPLE) - set(CONFIGURE_COMMAND ${CMAKE_BINARY_DIR}/../configure_speexdsp_osx.sh ${CMAKE_BINARY_DIR}/external/dist) - else() - set(CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/external/dist --disable-examples) - endif() -endif() - -include(ExternalProject) -ExternalProject_Add(speex - URL http://downloads.xiph.org/releases/speex/${SPEEXDSP_TARBALL} - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND $(MAKE) - INSTALL_COMMAND $(MAKE) install -) - -set(SPEEXDSP_LIBRARIES ${CMAKE_BINARY_DIR}/external/dist/lib/libspeexdsp.a) -include_directories(${CMAKE_BINARY_DIR}/external/dist/include) -list(APPEND FREEDV_LINK_LIBS ${SPEEXDSP_LIBRARIES}) -list(APPEND FREEDV_STATIC_DEPS speex) -if(USE_STATIC_CODEC2) - add_dependencies(codec2 speex) -endif() diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/BuildWxWidgets.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/BuildWxWidgets.cmake deleted file mode 100644 index 901d8062..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/BuildWxWidgets.cmake +++ /dev/null @@ -1,43 +0,0 @@ -set(WXWIDGETS_TARBALL "wxWidgets-3.0.2") - -# If we're cross-compiling then we need to set the target host manually. -if(MINGW AND CMAKE_CROSSCOMPILING) - include(cmake/MinGW.cmake) -endif() - -# If not cross-compiling then use the built-in makefile, otherwise use standard configure. -if(MINGW AND NOT CMAKE_CROSSCOMPILING) -# set(CONFIGURE_COMMAND "true") -# set(MAKE_COMMAND $(MAKE) -C build/msw -f makefile.gcc SHARED=0 UNICODE=1 BUILD=release PREFIX=${CMAKE_BINARY_DIR}/external/dist) - set(CONFIGURE_COMMAND ./configure --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -if(MINGW AND CMAKE_CROSSCOMPILING) - set(CONFIGURE_COMMAND ./configure --build=${HOST} --host=${HOST} --target=${HOST} --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -if(NOT MINGW) - set(CONFIGURE_COMMAND ./configure --host=${HOST} --target=${HOST} --disable-shared --prefix=${CMAKE_BINARY_DIR}/external/dist) - set(MAKE_COMMAND $(MAKE)) -endif() - -include(ExternalProject) -ExternalProject_Add(wxWidgets - URL http://downloads.sourceforge.net/wxwindows/${WXWIDGETS_TARBALL}.tar.bz2 - BUILD_IN_SOURCE 1 - INSTALL_DIR external/dist - CONFIGURE_COMMAND ${CONFIGURE_COMMAND} - BUILD_COMMAND ${MAKE_COMMAND} - INSTALL_COMMAND $(MAKE) install -) - -ExternalProject_Get_Property(wxWidgets install_dir) -message(STATUS "wxWidgets install dir: ${install_dir}") -if(NOT WXCONFIG) - set(WXCONFIG "${install_dir}/bin/wx-config") -endif() -if(EXISTS ${WXCONFIG}) - set(BS_WX_DONE TRUE) -endif() diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/FindPortaudio.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/FindPortaudio.cmake deleted file mode 100644 index 158e20ee..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/FindPortaudio.cmake +++ /dev/null @@ -1,107 +0,0 @@ -# - Try to find Portaudio -# Once done this will define -# -# PORTAUDIO_FOUND - system has Portaudio -# PORTAUDIO_INCLUDE_DIRS - the Portaudio include directory -# PORTAUDIO_LIBRARIES - Link these to use Portaudio -# PORTAUDIO_DEFINITIONS - Compiler switches required for using Portaudio -# PORTAUDIO_VERSION - Portaudio version -# -# Copyright (c) 2006 Andreas Schneider -# -# Redistribution and use is allowed according to the terms of the New BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - - -if (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - # in cache already - set(PORTAUDIO_FOUND TRUE) -else (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - if (NOT WIN32) - include(FindPkgConfig) - pkg_check_modules(PORTAUDIO2 portaudio-2.0) - endif (NOT WIN32) - - if (PORTAUDIO2_FOUND) - set(PORTAUDIO_INCLUDE_DIRS - ${PORTAUDIO2_INCLUDE_DIRS} - ) - if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_LIBRARIES "${PORTAUDIO2_LIBRARY_DIRS}/lib${PORTAUDIO2_LIBRARIES}.dylib") - else (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_LIBRARIES - ${PORTAUDIO2_LIBRARIES} - ) - endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(PORTAUDIO_VERSION - 19 - ) - set(PORTAUDIO_FOUND TRUE) - else (PORTAUDIO2_FOUND) - find_path(PORTAUDIO_INCLUDE_DIR - NAMES - portaudio.h - PATHS - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ) - - find_library(PORTAUDIO_LIBRARY - NAMES - portaudio - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - find_path(PORTAUDIO_LIBRARY_DIR - NAMES - portaudio - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - set(PORTAUDIO_INCLUDE_DIRS - ${PORTAUDIO_INCLUDE_DIR} - ) - set(PORTAUDIO_LIBRARIES - ${PORTAUDIO_LIBRARY} - ) - - set(PORTAUDIO_LIBRARY_DIRS - ${PORTAUDIO_LIBRARY_DIR} - ) - - set(PORTAUDIO_VERSION - 18 - ) - - if (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) - set(PORTAUDIO_FOUND TRUE) - endif (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) - - if (PORTAUDIO_FOUND) - if (NOT Portaudio_FIND_QUIETLY) - message(STATUS "Found Portaudio: ${PORTAUDIO_LIBRARIES}") - endif (NOT Portaudio_FIND_QUIETLY) - else (PORTAUDIO_FOUND) - if (Portaudio_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Portaudio") - endif (Portaudio_FIND_REQUIRED) - endif (PORTAUDIO_FOUND) - endif (PORTAUDIO2_FOUND) - - - # show the PORTAUDIO_INCLUDE_DIRS and PORTAUDIO_LIBRARIES variables only in the advanced view - mark_as_advanced(PORTAUDIO_INCLUDE_DIRS PORTAUDIO_LIBRARIES) - -endif (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) - diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/GetDependencies.cmake.in b/freedv/tags/1.2.2/freedv-dev/cmake/GetDependencies.cmake.in deleted file mode 100644 index 7470aa6d..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/GetDependencies.cmake.in +++ /dev/null @@ -1,37 +0,0 @@ -# As this script is run in a new cmake instance, it does not have access to -# the existing cache variables. Pass them in via the configure_file command. -set(CMAKE_BINARY_DIR @CMAKE_BINARY_DIR@) -set(CMAKE_SOURCE_DIR @CMAKE_SOURCE_DIR@) -set(UNIX @UNIX@) -set(WIN32 @WIN32@) -set(CMAKE_CROSSCOMPILING @CMAKE_CROSSCOMPILING@) -set(CMAKE_FIND_LIBRARY_SUFFIXES @CMAKE_FIND_LIBRARY_SUFFIXES@) -set(CMAKE_FIND_LIBRARY_PREFIXES @CMAKE_FIND_LIBRARY_PREFIXES@) -set(CMAKE_SYSTEM_LIBRARY_PATH @CMAKE_SYSTEM_LIBRARY_PATH@) -set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) - -set(FREEDV_EXE ${CMAKE_BINARY_DIR}/src/freedv.exe) - -include(GetPrerequisites) -get_prerequisites("${FREEDV_EXE}" _deps 1 0 "" "") -foreach(_runtime ${_deps}) - message("Looking for ${_runtime}") - find_library(RUNTIME_${_runtime} ${_runtime}) - message("${RUNTIME_${_runtime}}") - if(RUNTIME_${_runtime}) - message("Looking for dependencies of ${_runtime}") - get_prerequisites("${RUNTIME_${_runtime}}" _deps2 1 0 "" "") - foreach(_runtime2 ${_deps2}) - find_library(RUNTIME_${_runtime2} ${_runtime2}) - message("${RUNTIME_${_runtime2}}") - if(RUNTIME_${_runtime2}) - file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" - TYPE EXECUTABLE FILES "${RUNTIME_${_runtime2}}") - endif() - endforeach() - endif() - if(RUNTIME_${_runtime}) - file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" - TYPE EXECUTABLE FILES "${RUNTIME_${_runtime}}") - endif() -endforeach() diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/MinGW.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/MinGW.cmake deleted file mode 100644 index 333c1dc0..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/MinGW.cmake +++ /dev/null @@ -1,8 +0,0 @@ -# If we're cross-compiling then we need to set the target host manually. -if(MINGW AND CMAKE_CROSSCOMPILING) - if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) - set(HOST x86_64-w64-mingw32) - else() - set(HOST i686-w64-mingw32) - endif() -endif() diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake b/freedv/tags/1.2.2/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake deleted file mode 100644 index 3507d720..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/Toolchain-Ubuntu-mingw32.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# Sample toolchain file for building for Windows from an Ubuntu Linux system. -# -# Typical usage: -# *) install cross compiler: `sudo apt-get install mingw-w64 g++-mingw-w64` -# *) cd build -# *) cmake -DCMAKE_TOOLCHAIN_FILE=~/Toolchain-Ubuntu-mingw32.cmake .. - -set(CMAKE_SYSTEM_NAME Windows) -set(TOOLCHAIN_PREFIX i686-w64-mingw32) - -# cross compilers to use for C and C++ -set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) -set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) -set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) - -# target environment on the build host system -# set 1st to dir with the cross compiler's C/C++ headers/libs -set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) - -# modify default behavior of FIND_XXX() commands to -# search for headers/libs in the target environment and -# search for programs in the build host environment -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/config.h.in b/freedv/tags/1.2.2/freedv-dev/cmake/config.h.in deleted file mode 100644 index 8e3ab76b..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/config.h.in +++ /dev/null @@ -1,19 +0,0 @@ -/*-------------------------------------------------------------------------- - ** This file is autogenerated from config.h.in - ** during the cmake configuration of your project. If you need to make changes - ** edit the original file NOT THIS FILE. - ** --------------------------------------------------------------------------*/ -#ifndef _CONFIGURATION_HEADER_GUARD_H_ -#define _CONFIGURATION_HEADER_GUARD_H_ - -#define SIZEOF_INT @SIZEOF_INT@ -#cmakedefine HAVE_LIMITS_H @HAVE_LIMITS_H@ -#cmakedefine HAVE_STDINT_H @HAVE_STDINT_H@ -#cmakedefine HAVE_STDDEF_H @HAVE_STDDEF_H@ -#cmakedefine HAVE_STDLIB_H @HAVE_STDLIB_H@ -#cmakedefine HAVE_STRING_H @HAVE_STRING_H@ -#cmakedefine HAVE_FLOOR @HAVE_FLOOR@ -#cmakedefine HAVE_MEMSET @HAVE_MEMSET@ -#cmakedefine HAVE_POW @HAVE_POW@ -#cmakedefine HAVE_SQRT @HAVE_SQRT@ -#endif diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/soxconfig.h.in b/freedv/tags/1.2.2/freedv-dev/cmake/soxconfig.h.in deleted file mode 100644 index fb38608e..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/soxconfig.h.in +++ /dev/null @@ -1,16 +0,0 @@ -#define PACKAGE_VERSION "14.4.2" - -#cmakedefine HAVE_BYTESWAP_H 1 -#cmakedefine HAVE_FMEMOPEN 1 -#cmakedefine HAVE_FSEEKO 1 -#cmakedefine HAVE__FSEEKOI64 1 -#cmakedefine HAVE_LTDL_H 1 -#cmakedefine HAVE_MAGIC 1 -#cmakedefine HAVE_POPEN 1 -#cmakedefine HAVE_STDINT_H 1 -#cmakedefine HAVE_INTTYPES_H 1 -#cmakedefine HAVE_STRCASECMP 1 -#cmakedefine HAVE_STRINGS_H 1 -#cmakedefine HAVE_SYS_STAT_H 1 -#cmakedefine HAVE_SYS_TYPES_H 1 -#cmakedefine HAVE_VSNPRINTF 1 diff --git a/freedv/tags/1.2.2/freedv-dev/cmake/version.h.in b/freedv/tags/1.2.2/freedv-dev/cmake/version.h.in deleted file mode 100644 index 43b3b7a8..00000000 --- a/freedv/tags/1.2.2/freedv-dev/cmake/version.h.in +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef FREEDV_VER_DOT_H -#define FREEDV_VER_DOT_H 1 - -#define FREEDV_VERSION_MAJOR @FREEDV_VERSION_MAJOR@ -#define FREEDV_VERSION_MINOR @FREEDV_VERSION_MINOR@ -#define FREEDV_VERSION_PATCH @FREEDV_VERSION_PATCH@ -#define FREEDV_VERSION_SUFFIX "@FREEDV_VERSION_SUFFIX@" - -#define FREEDV_VERSION "@FREEDV_VERSION_STRING@" - -#endif //FREEDV_VER_DOT_H diff --git a/freedv/tags/1.2.2/freedv-dev/contrib/CMakeLists.txt b/freedv/tags/1.2.2/freedv-dev/contrib/CMakeLists.txt deleted file mode 100644 index 3f4b7e02..00000000 --- a/freedv/tags/1.2.2/freedv-dev/contrib/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Install icons if we're on most *nix systems. -if(UNIX AND NOT APPLE) - set(ICON_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor - CACHE PATH "Prefix to use for installing icons.") - install(FILES freedv48x48.png - DESTINATION ${ICON_INSTALL_PREFIX}/48x48/apps - RENAME freedv.png) - install(FILES freedv64x64.png - DESTINATION ${ICON_INSTALL_PREFIX}/64x64/apps - RENAME freedv.png) - install(FILES freedv128x128.png - DESTINATION ${ICON_INSTALL_PREFIX}/128x128/apps - RENAME freedv.png) - install(FILES freedv256x256.png - DESTINATION ${ICON_INSTALL_PREFIX}/256x256/apps - RENAME freedv.png) - - set(DESKTOP_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/applications - CACHE PATH "Location to install desktop files.") - install(FILES freedv.desktop - DESTINATION ${DESKTOP_INSTALL_DIR}) -endif(UNIX AND NOT APPLE) diff --git a/freedv/tags/1.2.2/freedv-dev/contrib/LICENSE b/freedv/tags/1.2.2/freedv-dev/contrib/LICENSE deleted file mode 100644 index dc8853a7..00000000 --- a/freedv/tags/1.2.2/freedv-dev/contrib/LICENSE +++ /dev/null @@ -1,393 +0,0 @@ -Attribution 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - d. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - g. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - i. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's - License You apply must not prevent recipients of the Adapted - Material from complying with this Public License. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public licenses. -Notwithstanding, Creative Commons may elect to apply one of its public -licenses to material it publishes and in those instances will be -considered the "Licensor." Except for the limited purpose of indicating -that material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the public -licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/freedv/tags/1.2.2/freedv-dev/contrib/freedv.desktop b/freedv/tags/1.2.2/freedv-dev/contrib/freedv.desktop deleted file mode 100644 index 96e82931..00000000 --- a/freedv/tags/1.2.2/freedv-dev/contrib/freedv.desktop +++ /dev/null @@ -1,8 +0,0 @@ -[Desktop Entry] -Version=1.0 -Name=FreeDV -Exec=freedv -Icon=freedv -Type=Application -Terminal=false -Categories=GTK;GNOME;AudioVideo;Audio;HamRadio; diff --git a/freedv/tags/1.2.2/freedv-dev/contrib/freedv.ico b/freedv/tags/1.2.2/freedv-dev/contrib/freedv.ico deleted file mode 100644 index e6b9a2087ddae6e530734e7293da08b1d12d4dd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364646 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 B>KOn4 diff --git a/freedv/tags/1.2.2/freedv-dev/contrib/freedv64x64.png b/freedv/tags/1.2.2/freedv-dev/contrib/freedv64x64.png deleted file mode 100644 index eb89773bbcd93a4ed507271bd728734d0a5d7f4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6289 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

dh=(PosX@Z;=^IUxT@l?9i6IK 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 X%Xb{{VgDe5fxZ-ERb@)0jeP$Hyj%aW diff --git a/freedv/tags/1.2.2/freedv-dev/credits.txt b/freedv/tags/1.2.2/freedv-dev/credits.txt deleted file mode 100644 index 431e399c..00000000 --- a/freedv/tags/1.2.2/freedv-dev/credits.txt +++ /dev/null @@ -1,13 +0,0 @@ -Credits (code or ideas borrowed from): -============================================== -Dave Witten and David Rowe (obviously) -Mel Whitten K0PFX (material and moral support) -Bruce Perens (cheerleader, promotion and publicity) -Mooneer Salem KG6AOV(Mac OSX Patch) -Soeren Straarup OZ2DAK (FreeBSD Port) -Don Mak -Steve Nance (K5FR) -Joel Stanley (Hamlib prototyping) and Mark Jessop (Mac OSX) -James Ahlstrom (Quisk) -FLDIGI -All the folks on the digital voice google group... diff --git a/freedv/tags/1.2.2/freedv-dev/db/current b/freedv/tags/1.2.2/freedv-dev/db/current deleted file mode 100644 index d00491fd..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/current +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/freedv/tags/1.2.2/freedv-dev/db/format b/freedv/tags/1.2.2/freedv-dev/db/format deleted file mode 100644 index db06890e..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/format +++ /dev/null @@ -1,2 +0,0 @@ -4 -layout sharded 1000 diff --git a/freedv/tags/1.2.2/freedv-dev/db/fs-type b/freedv/tags/1.2.2/freedv-dev/db/fs-type deleted file mode 100644 index 4fdd9531..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/fs-type +++ /dev/null @@ -1 +0,0 @@ -fsfs diff --git a/freedv/tags/1.2.2/freedv-dev/db/fsfs.conf b/freedv/tags/1.2.2/freedv-dev/db/fsfs.conf deleted file mode 100644 index cc08cebb..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/fsfs.conf +++ /dev/null @@ -1,38 +0,0 @@ -### This file controls the configuration of the FSFS filesystem. - -[memcached-servers] -### These options name memcached servers used to cache internal FSFS -### data. See http://www.danga.com/memcached/ for more information on -### memcached. To use memcached with FSFS, run one or more memcached -### servers, and specify each of them as an option like so: -# first-server = 127.0.0.1:11211 -# remote-memcached = mymemcached.corp.example.com:11212 -### The option name is ignored; the value is of the form HOST:PORT. -### memcached servers can be shared between multiple repositories; -### however, if you do this, you *must* ensure that repositories have -### distinct UUIDs and paths, or else cached data from one repository -### might be used by another accidentally. Note also that memcached has -### no authentication for reads or writes, so you must ensure that your -### memcached servers are only accessible by trusted users. - -[caches] -### When a cache-related error occurs, normally Subversion ignores it -### and continues, logging an error if the server is appropriately -### configured (and ignoring it with file:// access). To make -### Subversion never ignore cache errors, uncomment this line. -# fail-stop = true - -[rep-sharing] -### To conserve space, the filesystem can optionally avoid storing -### duplicate representations. This comes at a slight cost in -### performance, as maintaining a database of shared representations can -### increase commit times. The space savings are dependent upon the size -### of the repository, the number of objects it contains and the amount of -### duplication between them, usually a function of the branching and -### merging process. -### -### The following parameter enables rep-sharing in the repository. It can -### be switched on and off at will, but for best space-saving results -### should be enabled consistently over the life of the repository. -### rep-sharing is enabled by default. -# enable-rep-sharing = true diff --git a/freedv/tags/1.2.2/freedv-dev/db/min-unpacked-rev b/freedv/tags/1.2.2/freedv-dev/db/min-unpacked-rev deleted file mode 100644 index 573541ac..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/min-unpacked-rev +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/freedv/tags/1.2.2/freedv-dev/db/rep-cache.db b/freedv/tags/1.2.2/freedv-dev/db/rep-cache.db deleted file mode 100644 index 63c6f0b8a5181c3954b89f8a6fb505c09e81c4e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmWFz^vNtqRY=P(%1ta$FlJz3U}R))P*7lCU|#F<%m)i%A($C- zAIbAF|6yQa`p&@go%tjdHKRhKAwcgCX!K`f7nhf3Y|1T3Ov*_uN-c;_PE5`~FqoZ# zTpdGP6+#@Hd|Vaa@(LOX3JMvC#Tg1At`Q*$e*Qol>f@sj5aj9W7!;}C?HZ{AR8f># zmRX#cpQqsI7vk#f8U$AelFUy_D^4xJDpj0Wm5Nm&wW1&~FC{f49;*tVp_+zFY~rr+ zj0~ATWfjGRIlx>UpIBOw59Y_iJrHjQXM*xD3phi=ay7kUVbs3S5Eu=C0Sy6OknPB| j{D8V<)bh~~7!3h>h5#4HveEoc&mbSQYcvD~O$Y!0OWInE diff --git a/freedv/tags/1.2.2/freedv-dev/db/revprops/0/0 b/freedv/tags/1.2.2/freedv-dev/db/revprops/0/0 deleted file mode 100644 index d0b90dee..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/revprops/0/0 +++ /dev/null @@ -1,5 +0,0 @@ -K 8 -svn:date -V 27 -2012-08-21T18:27:59.389906Z -END diff --git a/freedv/tags/1.2.2/freedv-dev/db/revprops/0/1 b/freedv/tags/1.2.2/freedv-dev/db/revprops/0/1 deleted file mode 100644 index 0af71a2e..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/revprops/0/1 +++ /dev/null @@ -1,13 +0,0 @@ -K 10 -svn:author -V 9 -OFA-Staff -K 8 -svn:date -V 27 -2012-08-21T18:28:08.741468Z -K 7 -svn:log -V 25 -Imported folder structure -END diff --git a/freedv/tags/1.2.2/freedv-dev/db/revs/0/0 b/freedv/tags/1.2.2/freedv-dev/db/revs/0/0 deleted file mode 100644 index 10f5c45f..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/revs/0/0 +++ /dev/null @@ -1,11 +0,0 @@ -PLAIN -END -ENDREP -id: 0.0.r0/17 -type: dir -count: 0 -text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e -cpath: / - - -17 107 diff --git a/freedv/tags/1.2.2/freedv-dev/db/revs/0/1 b/freedv/tags/1.2.2/freedv-dev/db/revs/0/1 deleted file mode 100644 index fd802a9f..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/revs/0/1 +++ /dev/null @@ -1,49 +0,0 @@ -id: 3-1.0.r1/0 -type: dir -count: 0 -cpath: /tags -copyroot: 0 / - -id: 0-1.0.r1/62 -type: dir -count: 0 -cpath: /trunk -copyroot: 0 / - -id: 2-1.0.r1/126 -type: dir -count: 0 -cpath: /branches -copyroot: 0 / - -PLAIN -K 8 -branches -V 16 -dir 2-1.0.r1/126 -K 4 -tags -V 14 -dir 3-1.0.r1/0 -K 5 -trunk -V 15 -dir 0-1.0.r1/62 -END -ENDREP -id: 0.0.r1/306 -type: dir -pred: 0.0.r0/17 -count: 1 -text: 1 194 99 99 7b6cc14dddba4e09be5255b475d1a0a8 -cpath: / -copyroot: 0 / - -_0.0.t0-0 add-dir false false /trunk - -_2.0.t0-0 add-dir false false /branches - -_3.0.t0-0 add-dir false false /tags - - -306 431 diff --git a/freedv/tags/1.2.2/freedv-dev/db/transactions/.gitignore b/freedv/tags/1.2.2/freedv-dev/db/transactions/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/freedv-dev/db/txn-current b/freedv/tags/1.2.2/freedv-dev/db/txn-current deleted file mode 100644 index d00491fd..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/txn-current +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/freedv/tags/1.2.2/freedv-dev/db/txn-current-lock b/freedv/tags/1.2.2/freedv-dev/db/txn-current-lock deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/freedv-dev/db/txn-protorevs/.gitignore b/freedv/tags/1.2.2/freedv-dev/db/txn-protorevs/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/freedv-dev/db/uuid b/freedv/tags/1.2.2/freedv-dev/db/uuid deleted file mode 100644 index 0f362976..00000000 --- a/freedv/tags/1.2.2/freedv-dev/db/uuid +++ /dev/null @@ -1 +0,0 @@ -a56d66ce-6468-4744-9be7-52ce95ca47a4 diff --git a/freedv/tags/1.2.2/freedv-dev/db/write-lock b/freedv/tags/1.2.2/freedv-dev/db/write-lock deleted file mode 100644 index e69de29b..00000000 diff --git a/freedv/tags/1.2.2/freedv-dev/debian/changelog b/freedv/tags/1.2.2/freedv-dev/debian/changelog deleted file mode 100644 index ddfe80b5..00000000 --- a/freedv/tags/1.2.2/freedv-dev/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -freedv (1.0-150830) unstable; urgency=low - - * Subversion snapshot of tag 1.0. - - -- Stuart Longland Sun, 30 Aug 2015 09:01:13 +1000 diff --git a/freedv/tags/1.2.2/freedv-dev/debian/compat b/freedv/tags/1.2.2/freedv-dev/debian/compat deleted file mode 100644 index ec635144..00000000 --- a/freedv/tags/1.2.2/freedv-dev/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/freedv/tags/1.2.2/freedv-dev/debian/control b/freedv/tags/1.2.2/freedv-dev/debian/control deleted file mode 100644 index d1472b25..00000000 --- a/freedv/tags/1.2.2/freedv-dev/debian/control +++ /dev/null @@ -1,19 +0,0 @@ -Source: fdmdv2 -Section: main -Priority: optional -Maintainer: Stuart Longland -Build-Depends: debhelper (>= 9), cmake, libcodec2-dev, libgtk2.0-dev, - libhamlib-dev, libsamplerate-dev, libasound2-dev, libao-dev, libgsm1-dev, - portaudio19-dev, libsox-dev, libsndfile1-dev, libwxgtk3.0-dev -Standards-Version: 3.9.5 -Homepage: http://www.freedv.org -#Vcs-Git: git://anonscm.debian.org/collab-maint/freedv.git -#Vcs-Browser: http://anonscm.debian.org/?p=collab-maint/freedv.git;a=summary - -Package: freedv -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libcodec2 -Description: FreeDV: Open-Source Digital Voice modem - FreeDV is a digital voice modem that can transmit voice-quality - audio digitally over HF radio links in as little as 1.25kHz - bandwidth in varying conditions. diff --git a/freedv/tags/1.2.2/freedv-dev/debian/copyright b/freedv/tags/1.2.2/freedv-dev/debian/copyright deleted file mode 100644 index b55a293b..00000000 --- a/freedv/tags/1.2.2/freedv-dev/debian/copyright +++ /dev/null @@ -1,38 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: freedv -Source: - -Files: * -Copyright: - -License: - - - . - - -# If you want to use GPL v2 or later for the /debian/* files use -# the following clauses, or change it to suit. Delete these two lines -Files: debian/* -Copyright: 2015 unknown -License: GPL-2+ - This package 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 package 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, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". - -# Please also look if there are files or directories which have a -# different copyright/license attached and list them here. -# Please avoid to pick license terms that are more restrictive than the -# packaged work, as it may make Debian's contributions unacceptable upstream. diff --git a/freedv/tags/1.2.2/freedv-dev/debian/docs b/freedv/tags/1.2.2/freedv-dev/debian/docs deleted file mode 100644 index acfbcb33..00000000 --- a/freedv/tags/1.2.2/freedv-dev/debian/docs +++ /dev/null @@ -1,3 +0,0 @@ -credits.txt -README.txt -README.txt diff --git a/freedv/tags/1.2.2/freedv-dev/debian/format b/freedv/tags/1.2.2/freedv-dev/debian/format deleted file mode 100644 index 163aaf8d..00000000 --- a/freedv/tags/1.2.2/freedv-dev/debian/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/freedv/tags/1.2.2/freedv-dev/debian/rules b/freedv/tags/1.2.2/freedv-dev/debian/rules deleted file mode 100755 index ad892150..00000000 --- a/freedv/tags/1.2.2/freedv-dev/debian/rules +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/make -f -# See debhelper(7) (uncomment to enable) -# output every command that modifies files on the build system. -#DH_VERBOSE = 1 - -# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* -DPKG_EXPORT_BUILDFLAGS = 1 -include /usr/share/dpkg/default.mk - -# see FEATURE AREAS in dpkg-buildflags(1) -#export DEB_BUILD_MAINT_OPTIONS = hardening=+all - -# see ENVIRONMENT in dpkg-buildflags(1) -# package maintainers to append CFLAGS -#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic -# package maintainers to append LDFLAGS -#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed - - -# main packaging script based on dh7 syntax -%: - dh $@ - -# debmake generated override targets -# This is example for Cmake (See http://bugs.debian.org/641051 ) -override_dh_auto_configure: - dh_auto_configure -- \ - -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) \ - -DUSE_STATIC_CODEC2=FALSE \ - -DUSE_STATIC_SPEEXDSP=FALSE diff --git a/freedv/tags/1.2.2/freedv-dev/script/spot.sh b/freedv/tags/1.2.2/freedv-dev/script/spot.sh deleted file mode 100644 index cb1309a2..00000000 --- a/freedv/tags/1.2.2/freedv-dev/script/spot.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# -# spot.sh -# David Rowe Sep 2015 -# - -# Demo script for "spotting" based on FreeDV txt string. Posts a -# date-stamped text file to a web server. Called from FreeDV GUI -# program when a callsign is received in the txt msg. - - -# Q: how to remove repeated spots, or those close in time? -# -# Set up automated lftp login: -# -# $ lftp ftp://username@server -# Password: -# lftp username@server:~> set bmk:save-passwords true -# lftp username@server:~> bookmark add yourserver -# lftp username@server:~> bookmark list -# lftp username@server:~> quit - -SPOTFILE=/home/david/tmp/freedvspot.html -FTPSERVER=ftp.rowetel.com - -echo `date -u` " " $1 "
" >> $SPOTFILE -tail -n 25 $SPOTFILE > /tmp/spot.tmp1 -mv /tmp/spot.tmp1 $SPOTFILE -lftp -e "cd www;put $SPOTFILE;quit" $FTPSERVER diff --git a/freedv/tags/1.2.2/freedv-dev/src/CMakeLists.txt b/freedv/tags/1.2.2/freedv-dev/src/CMakeLists.txt deleted file mode 100644 index ef0798b3..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -set(FREEDV_SOURCES - dlg_audiooptions.cpp - dlg_filter.cpp - dlg_options.cpp - dlg_ptt.cpp - dlg_plugin.cpp - fdmdv2_main.cpp - fdmdv2_pa_wrapper.cpp - fdmdv2_plot.cpp - fdmdv2_plot_scalar.cpp - fdmdv2_plot_scatter.cpp - fdmdv2_plot_spectrum.cpp - fdmdv2_plot_waterfall.cpp - hamlib.cpp - serialport.cpp - topFrame.cpp - sox_biquad.c - comp.h - dlg_audiooptions.h - dlg_filter.h - dlg_options.h - dlg_ptt.h - fdmdv2_defines.h - fdmdv2_main.h - fdmdv2_pa_wrapper.h - fdmdv2_plot.h - fdmdv2_plot_scalar.h - fdmdv2_plot_scatter.h - fdmdv2_plot_spectrum.h - fdmdv2_plot_waterfall.h - hamlib.h - sox_biquad.h - sox/band.h - sox/biquad.c - sox/biquads.c - sox/biquad.h - sox/effects.c - sox/effects.h - sox/effects_i.c - sox/formats_i.c - sox/libsox.c - sox/sox.h - sox/sox_i.h - sox/soxomp.h - sox/util.h - sox/xmalloc.h - sox/xmalloc.c - topFrame.h - version.h -) - -# WIN32 is needed for Windows GUI apps and is ignored for UNIX like systems. -add_executable(freedv WIN32 ${FREEDV_SOURCES} ${RES_FILES}) -target_link_libraries(freedv ${FREEDV_LINK_LIBS}) -include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) -if(FREEDV_STATIC_DEPS) - add_dependencies(freedv ${FREEDV_STATIC_DEPS}) -endif(FREEDV_STATIC_DEPS) -install(TARGETS freedv - RUNTIME DESTINATION bin) - -# Custom commands to build OSX images. -if(APPLE) - add_custom_command( - TARGET freedv - POST_BUILD - COMMAND mkdir ARGS -p FreeDV.app/Contents/MacOS - COMMAND mkdir ARGS -p FreeDV.app/Contents/Resources/English.lproj - COMMAND cp ARGS ${CMAKE_CURRENT_SOURCE_DIR}/info.plist FreeDV.app/Contents - COMMAND cp ARGS ${CMAKE_CURRENT_SOURCE_DIR}/freedv.icns FreeDV.app/Contents/Resources - COMMAND echo ARGS -n "APPL????" > FreeDV.app/Contents/PkgInfo - COMMAND cp ARGS freedv FreeDV.app/Contents/MacOS/FreeDV - COMMAND dylibbundler ARGS -od -b -x FreeDV.app/Contents/MacOS/FreeDV -d FreeDV.app/Contents/libs -p @executable_path/../libs/ - COMMAND mkdir dist_tmp - COMMAND cp -r FreeDV.app dist_tmp - COMMAND hdiutil create -srcfolder dist_tmp/ -volname FreeDV -format UDZO ./FreeDV.dmg - COMMAND rm -rf dist_tmp - ) -endif(APPLE) diff --git a/freedv/tags/1.2.2/freedv-dev/src/Makefile.win32 b/freedv/tags/1.2.2/freedv-dev/src/Makefile.win32 deleted file mode 100644 index 932e8518..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/Makefile.win32 +++ /dev/null @@ -1,52 +0,0 @@ -# src/Makefile.win32 -# David Rowe 26 Oct 2012 -# -# Makefile for Win32 on msys/Mingw to help David R get up to speed -# -# $ make -f Makefile.Win32 - -CODEC2_PATH=$(HOME)/codec2-dev -INCLUDE_PATH=/usr/local/include - -WX_CONFIG=wx-config -WX_CPPFLAGS = $(shell $(WX_CONFIG) --cxxflags) -D__WXDEBUG__ -WX_LIBS = $(shell $(WX_CONFIG) --libs core, base, aui, adv, net) -SVN_REVISION=$(shell svnversion) -CODEC2_INC=$(CODEC2_PATH)/src -CODEC2_LIB=$(CODEC2_PATH)/build_win32/src/ - -CPP_FLAGS = -D_NO_AUTOTOOLS_ -I$(INCLUDE_PATH) $(WX_CPPFLAGS) -I$(CODEC2_INC) -I../extern/include -I. -g -Wall -DSVN_REVISION=\"$(SVN_REVISION)\" -LIBS = $(WX_LIBS) -L$(CODEC2_LIB) -lcodec2 -lm -lportaudiocpp -lportaudio -lpthread -lsndfile -lsamplerate -lhamlib -lsox -lspeexdsp - -OBJS = topFrame.o \ -fdmdv2_main.o \ -fdmdv2_plot.o \ -fdmdv2_plot_scalar.o \ -fdmdv2_plot_scatter.o \ -fdmdv2_plot_spectrum.o \ -fdmdv2_plot_waterfall.o \ -fdmdv2_pa_wrapper.o \ -dlg_audiooptions.o \ -dlg_ptt.o \ -dlg_options.o \ -dlg_filter.o \ -sox_biquad.o \ -hamlib.o \ -../../codec2-dev/src/golay23.o - -HDRS = version.h dlg_audiooptions.h dlg_ptt.h dlg_filter.h fdmdv2_main.h fdmdv2_defines.h fdmdv2_plot.h fdmdv2_plot_scalar.h fdmdv2_plot_waterfall.h fdmdv2_plot_scatter.h fdmdv2_plot_spectrum.h fdmdv2_pa_wrapper.h topFrame.h dlg_audiooptions.h topFrame.h varicode.h ../../codec2-dev/src/golay23.h hamlib.h - -all: freedv - -freedv: $(OBJS) - g++ -o freedv $(OBJS) $(CPP_FLAGS) $(LIBS) - -%.o: %.cpp $(HDRS) Makefile.win32 - g++ $(CPP_FLAGS) -c $< -o $@ - -%.o: %.c $(HDRS) Makefile.win32 - gcc $(CPP_FLAGS) -c $< -o $@ - -clean: - rm -f *.o fdmdv2 - diff --git a/freedv/tags/1.2.2/freedv-dev/src/afreedvplugin.c b/freedv/tags/1.2.2/freedv-dev/src/afreedvplugin.c deleted file mode 100644 index 5fdc424e..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/afreedvplugin.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - afreedvplugin.c - David Rowe Feb 2016 - - Sample FreeDV plugin - - TODO: - [ ] plugin to call back to functions - [ ] ability to list .so's/DLLs and scan - [ ] where do we put plugins? - [ ] Windows build and test environment - - linux .so: - $ gcc -Wall -fPIC -c afreedvplugin.c - $ gcc -shared -Wl,-soname,afreedvplugin.so -o afreedvplugin.so afreedvplugin.o - win32 .dll: - $ i686-w64-mingw32-gcc -c afreedvplugin.c - $ i686-w64-mingw32-gcc -shared -o afreedvplugin.dll afreedvplugin.o -Wl,--out-implib,afreedvplugin_dll.a - -*/ - -#include -#include -#include - -#ifdef _WIN32_ -#define DLL __declspec(dllexport) -#else -#define DLL -#endif - - -#ifdef LATER -/* functions plugin can call - not sure how to link to these */ - -int plugin_alert(char string[]); -int plugin_get_persistant(char name[], char value[]); -int plugin_set_persistant(char name[], char value[]); -#endif -static int (*plugin_get_persistant)(char name[], char value[]); - -struct APLUGIN_STATES { - int symbol_rate; - int num_tones; - int counter; -}; - -/* plugin functions called by host, we need to write these */ - -void DLL plugin_name(char name[]) { - - sprintf(name, "aFreeDVplugIn"); -} - -/* - Text fields will be created for nparams, using the names - in *param_names[]. These fields we be saved to persistent - storage as name/param_names[0], name/param_names[1] .... -*/ - -void DLL *plugin_open(char *param_names[], - int *nparams, - int (*aplugin_get_persistant)(char *, char *)) -{ - struct APLUGIN_STATES *states; - - /* set up function ptrs */ - - plugin_get_persistant = aplugin_get_persistant; - - /* tell host how many persistent parameters we have and their names */ - - strcpy(param_names[0], "SymbolRate"); - strcpy(param_names[1], "NumTones"); - *nparams = 2; - - /* init local states */ - - states = (struct APLUGIN_STATES *)malloc(sizeof(struct APLUGIN_STATES)); - if (states == NULL) { - // TODO: plugin_alert("Problem starting plugin!"); - return NULL; - } - states->counter = 0; - - return (void*)states; -} - -void DLL plugin_close(void *states) { - free(states); -} - -void DLL plugin_start(void *s) { - struct APLUGIN_STATES *states = (struct APLUGIN_STATES*)s; - char txt[80]; - - fprintf(stderr, "\nplugin_start\n"); - - (plugin_get_persistant)("SymbolRate",txt); - states->symbol_rate = atoi(txt); - - (plugin_get_persistant)("NumTones",txt); - states->num_tones = atoi(txt); - - fprintf(stderr, "symbol_rate: %d num_tones: %d\n", states->symbol_rate, states->num_tones); -} - -void DLL plugin_stop(void *states) { - fprintf(stderr, "\nplugin_stop\n"); -} - -void DLL plugin_rx_samples(void *s, short samples[], int n) { - struct APLUGIN_STATES *states = (struct APLUGIN_STATES*)s; - //fprintf(stderr, "Got n=%d samples!\n", n); - //fprintf(stderr, "samples[0] = %d samples[%d-1] = %d counter = %d\n", samples[0], n, samples[n-1], states->counter++); -} - diff --git a/freedv/tags/1.2.2/freedv-dev/src/comp.h b/freedv/tags/1.2.2/freedv-dev/src/comp.h deleted file mode 100644 index a3a1bd9b..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/comp.h +++ /dev/null @@ -1,39 +0,0 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: comp.h - AUTHOR......: David Rowe - DATE CREATED: 24/08/09 - - Complex number definition. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2009 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 . -*/ - -#ifndef __COMP__ -#define __COMP__ - -/* Complex number */ - -typedef struct -{ - float real; - float imag; -} COMP; - -#endif diff --git a/freedv/tags/1.2.2/freedv-dev/src/dlg_audiooptions.cpp b/freedv/tags/1.2.2/freedv-dev/src/dlg_audiooptions.cpp deleted file mode 100644 index 4e8cd981..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/dlg_audiooptions.cpp +++ /dev/null @@ -1,1264 +0,0 @@ -//========================================================================= -// Name: AudioOptsDialog.cpp -// Purpose: Implements an Audio options selection dialog. -// -// Authors: David Rowe, David Witten -// License: -// -// All rights reserved. -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================= -#include "fdmdv2_main.h" -#include "dlg_audiooptions.h" - -// constants for test waveform plots - -#define TEST_WAVEFORM_X 180 -#define TEST_WAVEFORM_Y 180 -#define TEST_WAVEFORM_PLOT_TIME 2.0 -#define TEST_WAVEFORM_PLOT_FS 400 -#define TEST_BUF_SIZE 1024 -#define TEST_FS 48000.0 -#define TEST_DT 0.1 // time between plot updates in seconds -#define TEST_WAVEFORM_PLOT_BUF ((int)(DT*400)) - -void AudioOptsDialog::Pa_Init(void) -{ - m_isPaInitialized = false; - - if((pa_err = Pa_Initialize()) == paNoError) - { - m_isPaInitialized = true; - } - else - { - wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK); - return; - } -} - - -void AudioOptsDialog::buildTestControls(PlotScalar **plotScalar, wxButton **btnTest, - wxPanel *parentPanel, wxBoxSizer *bSizer, wxString buttonLabel) -{ - wxBoxSizer* bSizer1 = new wxBoxSizer(wxVERTICAL); - - wxPanel *panel = new wxPanel(parentPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); - *plotScalar = new PlotScalar((wxFrame*) panel, 1, TEST_WAVEFORM_PLOT_TIME, 1.0/TEST_WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "", 1); - (*plotScalar)->SetClientSize(wxSize(TEST_WAVEFORM_X,TEST_WAVEFORM_Y)); - bSizer1->Add(panel, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 8); - - *btnTest = new wxButton(parentPanel, wxID_ANY, buttonLabel, wxDefaultPosition, wxDefaultSize); - bSizer1->Add(*btnTest, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 0); - - bSizer->Add(bSizer1, 0, wxALIGN_CENTER_HORIZONTAL |wxALIGN_CENTER_VERTICAL ); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// AudioOptsDialog() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -AudioOptsDialog::AudioOptsDialog(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - //this->SetSizeHints(wxSize(850, 600), wxDefaultSize); - fprintf(stderr, "pos %d %d\n", pos.x, pos.y); - Pa_Init(); - - wxBoxSizer* mainSizer; - mainSizer = new wxBoxSizer(wxVERTICAL); - m_panel1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer4; - bSizer4 = new wxBoxSizer(wxVERTICAL); - m_notebook1 = new wxNotebook(m_panel1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM); - m_panelRx = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer20; - bSizer20 = new wxBoxSizer(wxVERTICAL); - wxGridSizer* gSizer4; - gSizer4 = new wxGridSizer(2, 1, 0, 0); - - // Rx In ----------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer2; - sbSizer2 = new wxStaticBoxSizer(new wxStaticBox(m_panelRx, wxID_ANY, _("From Radio")), wxHORIZONTAL); - - wxBoxSizer* bSizer811a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlRxInDevices = new wxListCtrl(m_panelRx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer811a->Add(m_listCtrlRxInDevices, 1, wxALL|wxEXPAND, 1); - - wxBoxSizer* bSizer811; - bSizer811 = new wxBoxSizer(wxHORIZONTAL); - m_staticText51 = new wxStaticText(m_panelRx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText51->Wrap(-1); - bSizer811->Add(m_staticText51, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_textCtrlRxIn = new wxTextCtrl(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer811->Add(m_textCtrlRxIn, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText6 = new wxStaticText(m_panelRx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText6->Wrap(-1); - bSizer811->Add(m_staticText6, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateRxIn = new wxComboBox(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer811->Add(m_cbSampleRateRxIn, 0, wxALIGN_CENTER_VERTICAL|wxALL, 1); - - bSizer811a->Add(bSizer811, 0, wxEXPAND, 5); - - sbSizer2->Add(bSizer811a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarRxIn, &m_btnRxInTest, m_panelRx, sbSizer2, _("Rec 2s")); - - gSizer4->Add(sbSizer2, 1, wxEXPAND, 5); - - // Rx Out ----------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer3; - sbSizer3 = new wxStaticBoxSizer(new wxStaticBox(m_panelRx, wxID_ANY, _("To Speaker/Headphones")), wxHORIZONTAL); - - wxBoxSizer* bSizer81a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlRxOutDevices = new wxListCtrl(m_panelRx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer81a->Add(m_listCtrlRxOutDevices, 1, wxALL|wxEXPAND, 1); - - wxBoxSizer* bSizer81; - bSizer81 = new wxBoxSizer(wxHORIZONTAL); - m_staticText9 = new wxStaticText(m_panelRx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText9->Wrap(-1); - bSizer81->Add(m_staticText9, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_textCtrlRxOut = new wxTextCtrl(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer81->Add(m_textCtrlRxOut, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText10 = new wxStaticText(m_panelRx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText10->Wrap(-1); - bSizer81->Add(m_staticText10, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateRxOut = new wxComboBox(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer81->Add(m_cbSampleRateRxOut, 0, wxALIGN_CENTER_VERTICAL|wxALL, 1); - - bSizer81a->Add(bSizer81, 0, wxEXPAND, 5); - - sbSizer3->Add(bSizer81a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarRxOut, &m_btnRxOutTest, m_panelRx, sbSizer3, _("Play 2s")); - - gSizer4->Add(sbSizer3, 1, wxEXPAND, 2); - bSizer20->Add(gSizer4, 1, wxEXPAND, 1); - m_panelRx->SetSizer(bSizer20); - m_panelRx->Layout(); - bSizer20->Fit(m_panelRx); - m_notebook1->AddPage(m_panelRx, _("Receive"), true); - - // Tx Tab ------------------------------------------------------------------------------- - - m_panelTx = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer18; - bSizer18 = new wxBoxSizer(wxVERTICAL); - wxGridSizer* gSizer2; - gSizer2 = new wxGridSizer(2, 1, 0, 0); - - // Tx In ---------------------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer22; - sbSizer22 = new wxStaticBoxSizer(new wxStaticBox(m_panelTx, wxID_ANY, _("From Microphone")), wxHORIZONTAL); - - wxBoxSizer* bSizer83a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlTxInDevices = new wxListCtrl(m_panelTx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer83a->Add(m_listCtrlTxInDevices, 1, wxALL|wxEXPAND, 1); - wxBoxSizer* bSizer83; - bSizer83 = new wxBoxSizer(wxHORIZONTAL); - m_staticText12 = new wxStaticText(m_panelTx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText12->Wrap(-1); - bSizer83->Add(m_staticText12, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_textCtrlTxIn = new wxTextCtrl(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer83->Add(m_textCtrlTxIn, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText11 = new wxStaticText(m_panelTx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText11->Wrap(-1); - bSizer83->Add(m_staticText11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateTxIn = new wxComboBox(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer83->Add(m_cbSampleRateTxIn, 0, wxALL, 1); - - bSizer83a->Add(bSizer83, 0, wxEXPAND, 5); - - sbSizer22->Add(bSizer83a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarTxIn, &m_btnTxInTest, m_panelTx, sbSizer22, _("Rec 2s")); - - gSizer2->Add(sbSizer22, 1, wxEXPAND, 5); - - // Tx Out ---------------------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer21; - sbSizer21 = new wxStaticBoxSizer(new wxStaticBox(m_panelTx, wxID_ANY, _("To Radio")), wxHORIZONTAL); - - wxBoxSizer* bSizer82a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlTxOutDevices = new wxListCtrl(m_panelTx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer82a->Add(m_listCtrlTxOutDevices, 1, wxALL|wxEXPAND, 2); - wxBoxSizer* bSizer82; - bSizer82 = new wxBoxSizer(wxHORIZONTAL); - m_staticText81 = new wxStaticText(m_panelTx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText81->Wrap(-1); - bSizer82->Add(m_staticText81, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_textCtrlTxOut = new wxTextCtrl(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer82->Add(m_textCtrlTxOut, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText71 = new wxStaticText(m_panelTx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText71->Wrap(-1); - bSizer82->Add(m_staticText71, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateTxOut = new wxComboBox(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer82->Add(m_cbSampleRateTxOut, 0, wxALL, 1); - - bSizer82a->Add(bSizer82, 0, wxEXPAND, 5); - - sbSizer21->Add(bSizer82a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarTxOut, &m_btnTxOutTest, m_panelTx, sbSizer21, _("Play 2s")); - - gSizer2->Add(sbSizer21, 1, wxEXPAND, 5); - bSizer18->Add(gSizer2, 1, wxEXPAND, 1); - m_panelTx->SetSizer(bSizer18); - m_panelTx->Layout(); - bSizer18->Fit(m_panelTx); - m_notebook1->AddPage(m_panelTx, _("Transmit"), false); - - // API Tab ------------------------------------------------------------------- - - m_panelAPI = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer12; - bSizer12 = new wxBoxSizer(wxHORIZONTAL); - wxGridSizer* gSizer31; - gSizer31 = new wxGridSizer(2, 1, 0, 0); - wxStaticBoxSizer* sbSizer1; - sbSizer1 = new wxStaticBoxSizer(new wxStaticBox(m_panelAPI, wxID_ANY, _("PortAudio")), wxVERTICAL); - - wxGridSizer* gSizer3; - gSizer3 = new wxGridSizer(4, 2, 0, 0); - - m_staticText7 = new wxStaticText(m_panelAPI, wxID_ANY, _("PortAudio Version String:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText7->Wrap(-1); - gSizer3->Add(m_staticText7, 1, wxALIGN_RIGHT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - m_textStringVer = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - gSizer3->Add(m_textStringVer, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText8 = new wxStaticText(m_panelAPI, wxID_ANY, _("PortAudio Int Version:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText8->Wrap(-1); - gSizer3->Add(m_staticText8, 1, wxALIGN_RIGHT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - m_textIntVer = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - gSizer3->Add(m_textIntVer, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText5 = new wxStaticText(m_panelAPI, wxID_ANY, _("Device Count:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText5->Wrap(-1); - gSizer3->Add(m_staticText5, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 10); - m_textCDevCount = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - gSizer3->Add(m_textCDevCount, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText4 = new wxStaticText(m_panelAPI, wxID_ANY, _("API Count:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText4->Wrap(-1); - gSizer3->Add(m_staticText4, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 10); - m_textAPICount = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - m_textAPICount->SetMaxSize(wxSize(45,-1)); - gSizer3->Add(m_textAPICount, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - sbSizer1->Add(gSizer3, 1, wxEXPAND, 2); - gSizer31->Add(sbSizer1, 1, wxEXPAND, 2); - wxStaticBoxSizer* sbSizer6; - sbSizer6 = new wxStaticBoxSizer(new wxStaticBox(m_panelAPI, wxID_ANY, _("Other")), wxVERTICAL); - gSizer31->Add(sbSizer6, 1, wxEXPAND, 5); - bSizer12->Add(gSizer31, 1, wxEXPAND, 5); - m_panelAPI->SetSizer(bSizer12); - m_panelAPI->Layout(); - bSizer12->Fit(m_panelAPI); - m_notebook1->AddPage(m_panelAPI, _("API Info"), false); - bSizer4->Add(m_notebook1, 1, wxEXPAND | wxALL, 0); - m_panel1->SetSizer(bSizer4); - m_panel1->Layout(); - bSizer4->Fit(m_panel1); - mainSizer->Add(m_panel1, 1, wxEXPAND | wxALL, 1); - - wxBoxSizer* bSizer6; - bSizer6 = new wxBoxSizer(wxHORIZONTAL); - m_btnRefresh = new wxButton(this, wxID_ANY, _("Refresh"), wxDefaultPosition, wxDefaultSize, 0); - bSizer6->Add(m_btnRefresh, 0, wxALIGN_CENTER|wxALL, 2); - - m_sdbSizer1 = new wxStdDialogButtonSizer(); - - m_sdbSizer1OK = new wxButton(this, wxID_OK); - m_sdbSizer1->AddButton(m_sdbSizer1OK); - - m_sdbSizer1Cancel = new wxButton(this, wxID_CANCEL); - m_sdbSizer1->AddButton(m_sdbSizer1Cancel); - - m_sdbSizer1Apply = new wxButton(this, wxID_APPLY); - m_sdbSizer1->AddButton(m_sdbSizer1Apply); - - m_sdbSizer1->Realize(); - - bSizer6->Add(m_sdbSizer1, 1, wxALIGN_CENTER_VERTICAL, 2); - mainSizer->Add(bSizer6, 0, wxEXPAND, 2); - this->SetSizer(mainSizer); - this->Layout(); - this->Centre(wxBOTH); -// this->Centre(wxBOTH); - - m_notebook1->SetSelection(0); - - showAPIInfo(); - m_RxInDevices.m_listDevices = m_listCtrlRxInDevices; - m_RxInDevices.direction = AUDIO_IN; - m_RxInDevices.m_textDevice = m_textCtrlRxIn; - m_RxInDevices.m_cbSampleRate = m_cbSampleRateRxIn; - - m_RxOutDevices.m_listDevices = m_listCtrlRxOutDevices; - m_RxOutDevices.direction = AUDIO_OUT; - m_RxOutDevices.m_textDevice = m_textCtrlRxOut; - m_RxOutDevices.m_cbSampleRate = m_cbSampleRateRxOut; - - m_TxInDevices.m_listDevices = m_listCtrlTxInDevices; - m_TxInDevices.direction = AUDIO_IN; - m_TxInDevices.m_textDevice = m_textCtrlTxIn; - m_TxInDevices.m_cbSampleRate = m_cbSampleRateTxIn; - - m_TxOutDevices.m_listDevices = m_listCtrlTxOutDevices; - m_TxOutDevices.direction = AUDIO_OUT; - m_TxOutDevices.m_textDevice = m_textCtrlTxOut; - m_TxOutDevices.m_cbSampleRate = m_cbSampleRateTxOut; - - populateParams(m_RxInDevices); - populateParams(m_RxOutDevices); - populateParams(m_TxInDevices); - populateParams(m_TxOutDevices); - - m_listCtrlRxInDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnRxInDeviceSelect ), NULL, this ); - m_listCtrlRxOutDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnRxOutDeviceSelect ), NULL, this ); - m_listCtrlTxInDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnTxInDeviceSelect ), NULL, this ); - m_listCtrlTxOutDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnTxOutDeviceSelect ), NULL, this ); - - // wire up test buttons - m_btnRxInTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxInTest ), NULL, this ); - m_btnRxOutTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxOutTest ), NULL, this ); - m_btnTxInTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxInTest ), NULL, this ); - m_btnTxOutTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxOutTest ), NULL, this ); - - m_btnRefresh->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRefreshClick ), NULL, this ); - m_sdbSizer1Apply->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnApplyAudioParameters ), NULL, this ); - m_sdbSizer1Cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnCancelAudioParameters ), NULL, this ); - m_sdbSizer1OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnOkAudioParameters ), NULL, this ); -/* - void OnClose( wxCloseEvent& event ) { event.Skip(); } - void OnHibernate( wxActivateEvent& event ) { event.Skip(); } - void OnIconize( wxIconizeEvent& event ) { event.Skip(); } - void OnInitDialog( wxInitDialogEvent& event ) { event.Skip(); } -*/ -// this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(AudioOptsDialog::OnClose)); - this->Connect(wxEVT_HIBERNATE, wxActivateEventHandler(AudioOptsDialog::OnHibernate)); - this->Connect(wxEVT_ICONIZE, wxIconizeEventHandler(AudioOptsDialog::OnIconize)); - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(AudioOptsDialog::OnInitDialog)); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// ~AudioOptsDialog() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -AudioOptsDialog::~AudioOptsDialog() -{ - Pa_Terminate(); - - // Disconnect Events - this->Disconnect(wxEVT_HIBERNATE, wxActivateEventHandler(AudioOptsDialog::OnHibernate)); - this->Disconnect(wxEVT_ICONIZE, wxIconizeEventHandler(AudioOptsDialog::OnIconize)); - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(AudioOptsDialog::OnInitDialog)); - - m_listCtrlRxInDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnRxInDeviceSelect), NULL, this); - m_listCtrlRxOutDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnRxOutDeviceSelect), NULL, this); - m_listCtrlTxInDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnTxInDeviceSelect), NULL, this); - m_listCtrlTxOutDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnTxOutDeviceSelect), NULL, this); - - m_btnRxInTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxInTest ), NULL, this ); - m_btnRxOutTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxOutTest ), NULL, this ); - m_btnTxInTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxInTest ), NULL, this ); - m_btnTxOutTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxOutTest ), NULL, this ); - - m_btnRefresh->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnRefreshClick), NULL, this); - m_sdbSizer1Apply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnApplyAudioParameters), NULL, this); - m_sdbSizer1Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnCancelAudioParameters), NULL, this); - m_sdbSizer1OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnOkAudioParameters), NULL, this); - -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnInitDialog( wxInitDialogEvent& event ) -{ - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -// -// helper function to look up name of devNum, and if it exists write -// name to textCtrl. Used to trap dissapearing devices. -//------------------------------------------------------------------------- -int AudioOptsDialog::setTextCtrlIfDevNumValid(wxTextCtrl *textCtrl, wxListCtrl *listCtrl, int devNum) -{ - int i, aDevNum, found_devNum; - - // ignore last list entry as it is the "none" entry - - found_devNum = 0; - for(i=0; iGetItemCount()-1; i++) { - aDevNum = wxAtoi(listCtrl->GetItemText(i, 1)); - //printf("aDevNum: %d devNum: %d\n", aDevNum, devNum); - if (aDevNum == devNum) { - found_devNum = 1; - textCtrl->SetValue(listCtrl->GetItemText(i, 0) + " (" + wxString::Format(wxT("%i"),devNum) + ")"); - printf("setting focus of %d\n", i); - listCtrl->SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED); - } - } - - if (found_devNum) - return devNum; - else { - textCtrl->SetValue("none"); - return -1; - } -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -int AudioOptsDialog::ExchangeData(int inout) -{ - if(inout == EXCHANGE_DATA_IN) - { - // Map sound card device numbers to tx/rx device numbers depending - // on number of sound cards in use - - printf("EXCHANGE_DATA_IN:\n"); - printf(" g_nSoundCards: %d\n", g_nSoundCards); - printf(" g_soundCard1InDeviceNum: %d\n", g_soundCard1InDeviceNum); - printf(" g_soundCard1OutDeviceNum: %d\n", g_soundCard1OutDeviceNum); - printf(" g_soundCard1SampleRate: %d\n", g_soundCard1SampleRate); - printf(" g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - printf(" g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - printf(" g_soundCard2SampleRate: %d\n", g_soundCard2SampleRate); - - if (g_nSoundCards == 0) { - m_textCtrlRxIn ->SetValue("none"); rxInAudioDeviceNum = -1; - m_textCtrlRxOut->SetValue("none"); rxOutAudioDeviceNum = -1; - m_textCtrlTxIn ->SetValue("none"); txInAudioDeviceNum = -1; - m_textCtrlTxOut->SetValue("none"); txOutAudioDeviceNum = -1; - } - - if (g_nSoundCards == 1) { - rxInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxIn, - m_listCtrlRxInDevices, - g_soundCard1InDeviceNum); - - rxOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxOut, - m_listCtrlRxOutDevices, - g_soundCard1OutDeviceNum); - - if ((rxInAudioDeviceNum != -1) && (rxInAudioDeviceNum != -1)) { - m_cbSampleRateRxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - m_cbSampleRateRxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - } - - m_textCtrlTxIn ->SetValue("none"); txInAudioDeviceNum = -1; - m_textCtrlTxOut->SetValue("none"); txOutAudioDeviceNum = -1; - } - - if (g_nSoundCards == 2) { - - rxInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxIn, - m_listCtrlRxInDevices, - g_soundCard1InDeviceNum); - - rxOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxOut, - m_listCtrlRxOutDevices, - g_soundCard2OutDeviceNum); - - txInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlTxIn, - m_listCtrlTxInDevices, - g_soundCard2InDeviceNum); - - txOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlTxOut, - m_listCtrlTxOutDevices, - g_soundCard1OutDeviceNum); - - if ((rxInAudioDeviceNum != -1) && (txOutAudioDeviceNum != -1)) { - m_cbSampleRateRxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - m_cbSampleRateTxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - } - - if ((txInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1)) { - m_cbSampleRateTxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard2SampleRate)); - m_cbSampleRateRxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard2SampleRate)); - } - } - printf(" rxInAudioDeviceNum: %d\n rxOutAudioDeviceNum: %d\n txInAudioDeviceNum: %d\n txOutAudioDeviceNum: %d\n", - rxInAudioDeviceNum, rxOutAudioDeviceNum, txInAudioDeviceNum, txOutAudioDeviceNum); - } - - if(inout == EXCHANGE_DATA_OUT) - { - int valid_one_card_config = 0; - int valid_two_card_config = 0; - wxString sampleRate1, sampleRate2; - - printf("EXCHANGE_DATA_OUT:\n"); - printf(" rxInAudioDeviceNum: %d\n rxOutAudioDeviceNum: %d\n txInAudioDeviceNum: %d\n txOutAudioDeviceNum: %d\n", - rxInAudioDeviceNum, rxOutAudioDeviceNum, txInAudioDeviceNum, txOutAudioDeviceNum); - - // --------------------------------------------------------------- - // check we have a valid 1 or 2 sound card configuration - // --------------------------------------------------------------- - - // one sound card config, tx device numbers should be set to -1 - - if ((rxInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1) && - (txInAudioDeviceNum == -1) && (txOutAudioDeviceNum == -1)) { - - valid_one_card_config = 1; - - // in and out sample rate must be the same, as there is one callback - - sampleRate1 = m_cbSampleRateRxIn->GetValue(); - if (!sampleRate1.IsSameAs(m_cbSampleRateRxOut->GetValue())) { - wxMessageBox(wxT("With a single sound card the Sample Rate of " - "From Radio and To Speaker/Headphones must be the same."), wxT(""), wxOK); - return -1; - } - } - - // two card configuration - - if ((rxInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1) && - (txInAudioDeviceNum != -1) && (txOutAudioDeviceNum != -1)) { - - valid_two_card_config = 1; - - // Check we haven't doubled up on sound devices - - if (rxInAudioDeviceNum == txInAudioDeviceNum) { - wxMessageBox(wxT("You must use different devices for From Radio and From Microphone"), wxT(""), wxOK); - return -1; - } - - if (rxOutAudioDeviceNum == txOutAudioDeviceNum) { - wxMessageBox(wxT("You must use different devices for To Radio and To Speaker/Headphones"), wxT(""), wxOK); - return -1; - } - - // Check sample rates for callback 1 devices are the same, - // as input and output are handled synchronously by one - // portaudio callback - - sampleRate1 = m_cbSampleRateRxIn->GetValue(); - if (!sampleRate1.IsSameAs(m_cbSampleRateTxOut->GetValue())) { - wxMessageBox(wxT("With two sound cards the Sample Rate " - "of From Radio and To Radio must be the same."), wxT(""), wxOK); - return -1; - } - - // check sample rate for callback 2 devices is the same - - sampleRate2 = m_cbSampleRateTxIn->GetValue(); - if (!sampleRate2.IsSameAs(m_cbSampleRateRxOut->GetValue())) { - wxMessageBox(wxT("With two sound cards the Sample Rate of " - "From Microphone and To Speaker/Headphones must be the same."), wxT(""), wxOK); - return -1; - } - - } - - printf(" valid_one_card_config: %d valid_two_card_config: %d\n", valid_one_card_config, valid_two_card_config); - - if (!valid_one_card_config && !valid_two_card_config) { - wxMessageBox(wxT("Invalid one or two sound card configuration"), wxT(""), wxOK); - return -1; - } - - // --------------------------------------------------------------- - // Map Rx/TX device numbers to sound card device numbers used - // in callbacks. Portaudio uses one callback per sound card so - // we have to be soundcard oriented at run time rather than - // Tx/Rx oriented as in this dialog. - // --------------------------------------------------------------- - g_nSoundCards = 0; - g_soundCard1InDeviceNum = g_soundCard1OutDeviceNum = g_soundCard2InDeviceNum = g_soundCard2OutDeviceNum = -1; - - if (valid_one_card_config) { - - // Only callback 1 used - - g_nSoundCards = 1; - g_soundCard1InDeviceNum = rxInAudioDeviceNum; - g_soundCard1OutDeviceNum = rxOutAudioDeviceNum; - g_soundCard1SampleRate = wxAtoi(sampleRate1); - } - - if (valid_two_card_config) { - g_nSoundCards = 2; - g_soundCard1InDeviceNum = rxInAudioDeviceNum; - g_soundCard1OutDeviceNum = txOutAudioDeviceNum; - g_soundCard1SampleRate = wxAtoi(sampleRate1); - g_soundCard2InDeviceNum = txInAudioDeviceNum; - g_soundCard2OutDeviceNum = rxOutAudioDeviceNum; - g_soundCard2SampleRate = wxAtoi(sampleRate2); - } - - printf(" g_nSoundCards: %d\n", g_nSoundCards); - printf(" g_soundCard1InDeviceNum: %d\n", g_soundCard1InDeviceNum); - printf(" g_soundCard1OutDeviceNum: %d\n", g_soundCard1OutDeviceNum); - printf(" g_soundCard1SampleRate: %d\n", g_soundCard1SampleRate); - printf(" g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - printf(" g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - printf(" g_soundCard2SampleRate: %d\n", g_soundCard2SampleRate); - - wxConfigBase *pConfig = wxConfigBase::Get(); - pConfig->Write(wxT("/Audio/soundCard1InDeviceNum"), g_soundCard1InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1OutDeviceNum"), g_soundCard1OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1SampleRate"), g_soundCard1SampleRate ); - - pConfig->Write(wxT("/Audio/soundCard2InDeviceNum"), g_soundCard2InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2OutDeviceNum"), g_soundCard2OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2SampleRate"), g_soundCard2SampleRate ); - - pConfig->Flush(); - delete wxConfigBase::Set((wxConfigBase *) NULL); - } - - return 0; -} - -//------------------------------------------------------------------------- -// buildListOfSupportedSampleRates() -//------------------------------------------------------------------------- -int AudioOptsDialog:: buildListOfSupportedSampleRates(wxComboBox *cbSampleRate, int devNum, int in_out) -{ - // every sound device has a different list of supported sample rates, so - // we work out which ones are supported and populate the list ctrl - - static double standardSampleRates[] = - { - 8000.0, 9600.0, - 11025.0, 12000.0, - 16000.0, 22050.0, - 24000.0, 32000.0, - 44100.0, 48000.0, - 88200.0, 96000.0, - 192000.0, -1 // negative terminated list - }; - - const PaDeviceInfo *deviceInfo; - PaStreamParameters inputParameters, outputParameters; - PaError err; - wxString str; - int i, numSampleRates; - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - printf("Pa_GetDeviceInfo(%d) failed!\n", devNum); - cbSampleRate->Clear(); - return 0; - } - - inputParameters.device = devNum; - inputParameters.channelCount = deviceInfo->maxInputChannels; - inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = 0; - inputParameters.hostApiSpecificStreamInfo = NULL; - - outputParameters.device = devNum; - outputParameters.channelCount = deviceInfo->maxOutputChannels; - outputParameters.sampleFormat = paInt16; - outputParameters.suggestedLatency = 0; - outputParameters.hostApiSpecificStreamInfo = NULL; - - cbSampleRate->Clear(); - //printf("devNum %d supports: ", devNum); - numSampleRates = 0; - for(i = 0; standardSampleRates[i] > 0; i++) - { - if (in_out == AUDIO_IN) - err = Pa_IsFormatSupported(&inputParameters, NULL, standardSampleRates[i]); - else - err = Pa_IsFormatSupported(NULL, &outputParameters, standardSampleRates[i]); - - if( err == paFormatIsSupported ) { - str.Printf("%i", (int)standardSampleRates[i]); - cbSampleRate->AppendString(str); - printf("%i ", (int)standardSampleRates[i]); - numSampleRates++; - } - } - printf("\n"); - - return numSampleRates; -} - -//------------------------------------------------------------------------- -// showAPIInfo() -//------------------------------------------------------------------------- -void AudioOptsDialog::showAPIInfo() -{ - wxString strval; - int apiVersion; - int apiCount = 0; - int numDevices = 0; - - strval = Pa_GetVersionText(); - m_textStringVer->SetLabel(strval); - - apiVersion = Pa_GetVersion(); - strval.Printf(wxT("%d"), apiVersion); - m_textIntVer->SetLabel(strval); - - apiCount = Pa_GetHostApiCount(); - strval.Printf(wxT("%d"), apiCount); - m_textAPICount->SetLabel(strval); - - numDevices = Pa_GetDeviceCount(); - strval.Printf(wxT("%d"), numDevices); - m_textCDevCount->SetLabel(strval); -} - -//------------------------------------------------------------------------- -// populateParams() -//------------------------------------------------------------------------- -void AudioOptsDialog::populateParams(AudioInfoDisplay ai) -{ - const PaDeviceInfo *deviceInfo = NULL; - wxListCtrl* ctrl = ai.m_listDevices; - int in_out = ai.direction; - long idx; - int numDevices; - wxListItem listItem; - wxString buf; - int devn; - int col = 0; - - numDevices = Pa_GetDeviceCount(); - - if(ctrl->GetColumnCount() > 0) - { - ctrl->ClearAll(); - } - - listItem.SetAlign(wxLIST_FORMAT_LEFT); - listItem.SetText(wxT("Device")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 300); - - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("ID")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 45); - - listItem.SetAlign(wxLIST_FORMAT_LEFT); - listItem.SetText(wxT("API")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - - if(in_out == AUDIO_IN) - { - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Default Sample Rate")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 160); - } - else if(in_out == AUDIO_OUT) - { - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Default Sample Rate")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 160); - } - - #ifdef LATENCY - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Min Latency")); - ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Max Latency")); - ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - #endif - - for(devn = 0; devn < numDevices; devn++) - { - buf.Printf(wxT("")); - deviceInfo = Pa_GetDeviceInfo(devn); - if( ((in_out == AUDIO_IN) && (deviceInfo->maxInputChannels > 0)) || - ((in_out == AUDIO_OUT) && (deviceInfo->maxOutputChannels > 0))) - { - col = 0; - buf.Printf(wxT("%s"), deviceInfo->name); - idx = ctrl->InsertItem(ctrl->GetItemCount(), buf); - col++; - - buf.Printf(wxT("%d"), devn); - ctrl->SetItem(idx, col++, buf); - - buf.Printf(wxT("%s"), Pa_GetHostApiInfo(deviceInfo->hostApi)->name); - ctrl->SetItem(idx, col++, buf); - - buf.Printf(wxT("%i"), (int)deviceInfo->defaultSampleRate); - ctrl->SetItem(idx, col++, buf); - - #ifdef LATENCY - if (in_out == AUDIO_IN) - buf.Printf(wxT("%8.4f"), deviceInfo->defaultLowInputLatency); - else - buf.Printf(wxT("%8.4f"), deviceInfo->defaultLowOutputLatency); - ctrl->SetItem(idx, col++, buf); - - if (in_out == AUDIO_IN) - buf.Printf(wxT("%8.4f"), deviceInfo->defaultHighInputLatency); - else - buf.Printf(wxT("%8.4f"), deviceInfo->defaultHighOutputLatency); - ctrl->SetItem(idx, col++, buf); - #endif - } - } - - // add "none" option at end - - buf.Printf(wxT("%s"), "none"); - idx = ctrl->InsertItem(ctrl->GetItemCount(), buf); -} - -//------------------------------------------------------------------------- -// OnDeviceSelect() -// -// helper function to set up "Device:" and "Sample Rate:" fields when -// we click on a line in the list of devices box -//------------------------------------------------------------------------- -void AudioOptsDialog::OnDeviceSelect(wxComboBox *cbSampleRate, - wxTextCtrl *textCtrl, - int *devNum, - wxListCtrl *listCtrlDevices, - int index, - int in_out) -{ - - wxString devName = listCtrlDevices->GetItemText(index, 0); - if (devName.IsSameAs("none")) { - *devNum = -1; - textCtrl->SetValue("none"); - } - else { - *devNum = wxAtoi(listCtrlDevices->GetItemText(index, 1)); - textCtrl->SetValue(devName + " (" + wxString::Format(wxT("%i"),*devNum) + ")"); - - int numSampleRates = buildListOfSupportedSampleRates(cbSampleRate, *devNum, in_out); - if (numSampleRates) { - wxString defSampleRate = listCtrlDevices->GetItemText(index, 3); - cbSampleRate->SetValue(defSampleRate); - } - else { - cbSampleRate->SetValue("None"); - } - } -} - -//------------------------------------------------------------------------- -// OnRxInDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxInDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateRxIn, - m_textCtrlRxIn, - &rxInAudioDeviceNum, - m_listCtrlRxInDevices, - evt.GetIndex(), - AUDIO_IN); -} - -//------------------------------------------------------------------------- -// OnRxOutDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxOutDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateRxOut, - m_textCtrlRxOut, - &rxOutAudioDeviceNum, - m_listCtrlRxOutDevices, - evt.GetIndex(), - AUDIO_OUT); -} - -//------------------------------------------------------------------------- -// OnTxInDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxInDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateTxIn, - m_textCtrlTxIn, - &txInAudioDeviceNum, - m_listCtrlTxInDevices, - evt.GetIndex(), - AUDIO_IN); -} - -//------------------------------------------------------------------------- -// OnTxOutDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxOutDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateTxOut, - m_textCtrlTxOut, - &txOutAudioDeviceNum, - m_listCtrlTxOutDevices, - evt.GetIndex(), - AUDIO_OUT); -} - -//------------------------------------------------------------------------- -// plotDeviceInputForAFewSecs() -// -// opens a record device and plots the input speech for a few seconds. This is "modal" using -// synchronous portaudio functions, so the GUI will not respond until after test sample has been -// taken -//------------------------------------------------------------------------- -void AudioOptsDialog::plotDeviceInputForAFewSecs(int devNum, PlotScalar *plotScalar) { - PaStreamParameters inputParameters; - const PaDeviceInfo *deviceInfo = NULL; - PaStream *stream = NULL; - PaError err; - short in48k_stereo_short[2*TEST_BUF_SIZE]; - short in48k_short[TEST_BUF_SIZE]; - short in8k_short[TEST_BUF_SIZE]; - int numDevices, nBufs, i, j, src_error,inputChannels; - float t; - SRC_STATE *src; - FIFO *fifo; - - // a basic sanity check - numDevices = Pa_GetDeviceCount(); - if (devNum >= numDevices) - return; - if (devNum < 0) - return; - printf("devNum %d\n", devNum); - - fifo = fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL); - src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL); - - // work out how many input channels this device supports. - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card "), wxT("Error"), wxOK); - return; - } - if (deviceInfo->maxInputChannels == 1) - inputChannels = 1; - else - inputChannels = 2; - - // open device - - inputParameters.device = devNum; - inputParameters.channelCount = inputChannels; - inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - nBufs = TEST_WAVEFORM_PLOT_TIME*TEST_FS/TEST_BUF_SIZE; - printf("inputChannels: %d nBufs %d\n", inputChannels, nBufs); - - err = Pa_OpenStream( - &stream, - &inputParameters, - NULL, - TEST_FS, - TEST_BUF_SIZE, - paClipOff, - NULL, // no callback, use blocking API - NULL ); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't initialise sound device."), wxT("Error"), wxOK); - return; - } - - err = Pa_StartStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't start sound device."), wxT("Error"), wxOK); - return; - } - - for(i=0, t=0.0; i TEST_DT) { - t -= TEST_DT; - short plotSamples[TEST_WAVEFORM_PLOT_BUF]; - if (fifo_read(fifo, plotSamples, TEST_WAVEFORM_PLOT_BUF)) - memset(plotSamples, 0, TEST_WAVEFORM_PLOT_BUF*sizeof(short)); - plotScalar->add_new_short_samples(0, plotSamples, TEST_WAVEFORM_PLOT_BUF, 32767); - plotScalar->Refresh(); - } - } - - err = Pa_StopStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't stop sound device."), wxT("Error"), wxOK); - return; - } - Pa_CloseStream(stream); - - fifo_destroy(fifo); - src_delete(src); -} - -//------------------------------------------------------------------------- -// plotDeviceOutputForAFewSecs() -// -// opens a play device and plays a tone for a few seconds. This is "modal" using -// synchronous portaudio functions, so the GUI will not respond until after test sample has been -// taken. Also plots a pretty picture like the record versions -//------------------------------------------------------------------------- -void AudioOptsDialog::plotDeviceOutputForAFewSecs(int devNum, PlotScalar *plotScalar) { - PaStreamParameters outputParameters; - const PaDeviceInfo *deviceInfo = NULL; - PaStream *stream = NULL; - PaError err; - short out48k_stereo_short[2*TEST_BUF_SIZE]; - short out48k_short[TEST_BUF_SIZE]; - short out8k_short[TEST_BUF_SIZE]; - int numDevices, nBufs, i, j, src_error, n, outputChannels; - float t; - SRC_STATE *src; - FIFO *fifo; - - // a basic sanity check - numDevices = Pa_GetDeviceCount(); - if (devNum >= numDevices) - return; - if (devNum < 0) - return; - - fifo = fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL); - src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL); - - // work out how many output channels this device supports. - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card "), wxT("Error"), wxOK); - return; - } - if (deviceInfo->maxOutputChannels == 1) - outputChannels = 1; - else - outputChannels = 2; - - printf("outputChannels: %d\n", outputChannels); - - outputParameters.device = devNum; - outputParameters.channelCount = outputChannels; - outputParameters.sampleFormat = paInt16; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - nBufs = TEST_WAVEFORM_PLOT_TIME*TEST_FS/TEST_BUF_SIZE; - - err = Pa_OpenStream( - &stream, - NULL, - &outputParameters, - TEST_FS, - TEST_BUF_SIZE, - paClipOff, - NULL, // no callback, use blocking API - NULL ); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't initialise sound device."), wxT("Error"), wxOK); - return; - } - - err = Pa_StartStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't start sound device."), wxT("Error"), wxOK); - return; - } - - for(i=0, t=0.0, n=0; i TEST_DT) { - t -= TEST_DT; - short plotSamples[TEST_WAVEFORM_PLOT_BUF]; - if (fifo_read(fifo, plotSamples, TEST_WAVEFORM_PLOT_BUF)) - memset(plotSamples, 0, TEST_WAVEFORM_PLOT_BUF*sizeof(short)); - plotScalar->add_new_short_samples(0, plotSamples, TEST_WAVEFORM_PLOT_BUF, 32767); - plotScalar->Refresh(); - } - } - - err = Pa_StopStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't stop sound device."), wxT("Error"), wxOK); - return; - } - Pa_CloseStream(stream); - - fifo_destroy(fifo); - src_delete(src); -} - -//------------------------------------------------------------------------- -// OnRxInTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxInTest(wxCommandEvent& event) -{ - plotDeviceInputForAFewSecs(rxInAudioDeviceNum, m_plotScalarRxIn); -} - -//------------------------------------------------------------------------- -// OnRxOutTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxOutTest(wxCommandEvent& event) -{ - plotDeviceOutputForAFewSecs(rxOutAudioDeviceNum, m_plotScalarRxOut); -} - -//------------------------------------------------------------------------- -// OnTxInTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxInTest(wxCommandEvent& event) -{ - plotDeviceInputForAFewSecs(txInAudioDeviceNum, m_plotScalarTxIn); -} - -//------------------------------------------------------------------------- -// OnTxOutTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxOutTest(wxCommandEvent& event) -{ - plotDeviceOutputForAFewSecs(txOutAudioDeviceNum, m_plotScalarTxOut); -} - -//------------------------------------------------------------------------- -// OnRefreshClick() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRefreshClick(wxCommandEvent& event) -{ - // restart portaudio, to re-sample available devices - - Pa_Terminate(); - Pa_Init(); - - m_notebook1->SetSelection(0); - showAPIInfo(); - populateParams(m_RxInDevices); - populateParams(m_RxOutDevices); - populateParams(m_TxInDevices); - populateParams(m_TxOutDevices); - - // some devices may have dissapeared, so possibily change sound - // card config - - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// OnApplyAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnApplyAudioParameters(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } -} - -//------------------------------------------------------------------------- -// OnCancelAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnCancelAudioParameters(wxCommandEvent& event) -{ - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } - EndModal(wxCANCEL); -} - -//------------------------------------------------------------------------- -// OnOkAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnOkAudioParameters(wxCommandEvent& event) -{ - int status = ExchangeData(EXCHANGE_DATA_OUT); - - // We only accept OK if config sucessful - - printf("status: %d m_isPaInitialized: %d\n", status, m_isPaInitialized); - if (status == 0) { - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - printf("terminated OK\n"); - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } - EndModal(wxOK); - } - -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/dlg_audiooptions.h b/freedv/tags/1.2.2/freedv-dev/src/dlg_audiooptions.h deleted file mode 100644 index 5aa6741d..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/dlg_audiooptions.h +++ /dev/null @@ -1,176 +0,0 @@ -//========================================================================= -// Name: AudioInfoDisplay.h -// Purpose: Declares simple wxWidgets application with GUI -// created using wxFormBuilder. -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================= -#ifndef __AudioOptsDialog__ -#define __AudioOptsDialog__ - -#include "fdmdv2_main.h" - -#define ID_AUDIO_OPTIONS 1000 -#define AUDIO_IN 0 -#define AUDIO_OUT 1 - -#include "portaudio.h" -#ifdef WIN32 -#if PA_USE_ASIO -#include "pa_asio.h" -#endif -#endif -#include "codec2_fifo.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// AudioInfoDisplay -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class AudioInfoDisplay -{ - public: - wxListCtrl* m_listDevices; - int direction; - wxTextCtrl* m_textDevice; - wxComboBox* m_cbSampleRate; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class AudioOptsDialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class AudioOptsDialog : public wxDialog -{ - private: - - protected: - PaError pa_err; - bool m_isPaInitialized; - - int rxInAudioDeviceNum; - int rxOutAudioDeviceNum; - int txInAudioDeviceNum; - int txOutAudioDeviceNum; - - void buildTestControls(PlotScalar **plotScalar, wxButton **btnTest, - wxPanel *parentPanel, wxBoxSizer *bSizer, wxString buttonLabel); - void plotDeviceInputForAFewSecs(int devNum, PlotScalar *plotScalar); - void plotDeviceOutputForAFewSecs(int devNum, PlotScalar *plotScalar); - - int buildListOfSupportedSampleRates(wxComboBox *cbSampleRate, int devNum, int in_out); - void populateParams(AudioInfoDisplay); - void showAPIInfo(); - int setTextCtrlIfDevNumValid(wxTextCtrl *textCtrl, wxListCtrl *listCtrl, int devNum); - void Pa_Init(void); - void OnDeviceSelect(wxComboBox *cbSampleRate, - wxTextCtrl *textCtrl, - int *devNum, - wxListCtrl *listCtrlDevices, - int index, - int in_out); - - AudioInfoDisplay m_RxInDevices; - AudioInfoDisplay m_RxOutDevices; - AudioInfoDisplay m_TxInDevices; - AudioInfoDisplay m_TxOutDevices; - wxPanel* m_panel1; - wxNotebook* m_notebook1; - - wxPanel* m_panelRx; - - wxListCtrl* m_listCtrlRxInDevices; - wxStaticText* m_staticText51; - wxTextCtrl* m_textCtrlRxIn; - wxStaticText* m_staticText6; - wxComboBox* m_cbSampleRateRxIn; - - wxButton* m_btnRxInTest; - PlotScalar* m_plotScalarRxIn; - - wxListCtrl* m_listCtrlRxOutDevices; - wxStaticText* m_staticText9; - wxTextCtrl* m_textCtrlRxOut; - wxStaticText* m_staticText10; - wxComboBox* m_cbSampleRateRxOut; - - wxButton* m_btnRxOutTest; - PlotScalar* m_plotScalarRxOut; - - wxPanel* m_panelTx; - - wxListCtrl* m_listCtrlTxInDevices; - wxStaticText* m_staticText12; - wxTextCtrl* m_textCtrlTxIn; - wxStaticText* m_staticText11; - wxComboBox* m_cbSampleRateTxIn; - - wxButton* m_btnTxInTest; - PlotScalar* m_plotScalarTxIn; - - wxListCtrl* m_listCtrlTxOutDevices; - wxStaticText* m_staticText81; - wxTextCtrl* m_textCtrlTxOut; - wxStaticText* m_staticText71; - wxComboBox* m_cbSampleRateTxOut; - - wxButton* m_btnTxOutTest; - PlotScalar* m_plotScalarTxOut; - - wxPanel* m_panelAPI; - - wxStaticText* m_staticText7; - wxStaticText* m_textStringVer; - wxStaticText* m_staticText8; - wxStaticText* m_textIntVer; - wxStaticText* m_staticText5; - wxStaticText* m_textCDevCount; - wxStaticText* m_staticText4; - wxStaticText* m_textAPICount; - wxButton* m_btnRefresh; - wxStdDialogButtonSizer* m_sdbSizer1; - wxButton* m_sdbSizer1OK; - wxButton* m_sdbSizer1Apply; - wxButton* m_sdbSizer1Cancel; - - // Virtual event handlers, overide them in your derived class - //virtual void OnActivateApp( wxActivateEvent& event ) { event.Skip(); } -// virtual void OnCloseFrame( wxCloseEvent& event ) { event.Skip(); } - - void OnRxInDeviceSelect( wxListEvent& event ); - - void OnRxInTest( wxCommandEvent& event ); - void OnRxOutTest( wxCommandEvent& event ); - void OnTxInTest( wxCommandEvent& event ); - void OnTxOutTest( wxCommandEvent& event ); - - void OnRxOutDeviceSelect( wxListEvent& event ); - void OnTxInDeviceSelect( wxListEvent& event ); - void OnTxOutDeviceSelect( wxListEvent& event ); - void OnRefreshClick( wxCommandEvent& event ); - void OnApplyAudioParameters( wxCommandEvent& event ); - void OnCancelAudioParameters( wxCommandEvent& event ); - void OnOkAudioParameters( wxCommandEvent& event ); - // Virtual event handlers, overide them in your derived class - void OnClose( wxCloseEvent& event ) { event.Skip(); } - void OnHibernate( wxActivateEvent& event ) { event.Skip(); } - void OnIconize( wxIconizeEvent& event ) { event.Skip(); } - void OnInitDialog( wxInitDialogEvent& event ); - - public: - - AudioOptsDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Audio Config"), const wxPoint& pos = wxPoint(1,1), const wxSize& size = wxSize( 800, 650 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~AudioOptsDialog(); - int ExchangeData(int inout); -}; -#endif //__AudioOptsDialog__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/dlg_filter.cpp b/freedv/tags/1.2.2/freedv-dev/src/dlg_filter.cpp deleted file mode 100644 index 5a5294a9..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/dlg_filter.cpp +++ /dev/null @@ -1,785 +0,0 @@ -//========================================================================== -// Name: dlg_filter.cpp -// Purpose: Dialog for controlling Codec audio filtering -// Date: Nov 25 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "dlg_filter.h" - -#define SLIDER_MAX 100 -#define SLIDER_LENGTH 100 - -#define FILTER_MIN_MAG_DB -20.0 -#define FILTER_MAX_MAG_DB 20.0 - -#define MAX_FREQ_BASS 600.00 -#define MAX_FREQ_TREBLE 3900.00 -#define MAX_FREQ_DEF 3000.00 - -#define MIN_GAIN -20 -#define MAX_GAIN 20 - -#define MAX_LOG10_Q 1.0 -#define MIN_LOG10_Q -1.0 - -// DFT parameters - -#define IMP_AMP 2000.0 // amplitude of impulse -#define NIMP 50 // number of samples in impulse response -#define F_STEP_DFT 10.0 // frequency steps to sample spectrum -#define F_MAG_N (int)(MAX_F_HZ/F_STEP_DFT) // number of frequency steps - -extern struct freedv *g_pfreedv; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class FilterDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -FilterDlg::FilterDlg(wxWindow* parent, bool running, bool *newMicInFilter, bool *newSpkOutFilter, - wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - m_running = running; - m_newMicInFilter = newMicInFilter; - m_newSpkOutFilter = newSpkOutFilter; - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* bSizer30; - bSizer30 = new wxBoxSizer(wxVERTICAL); - - // LPC Post Filter -------------------------------------------------------- - - wxStaticBoxSizer* lpcpfs = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("LPC Post Filter")), wxHORIZONTAL); - - wxBoxSizer* left = new wxBoxSizer(wxVERTICAL); - - m_codec2LPCPostFilterEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - left->Add(m_codec2LPCPostFilterEnable); - - m_codec2LPCPostFilterBassBoost = new wxCheckBox(this, wxID_ANY, _("0-1 kHz 3dB Boost"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - left->Add(m_codec2LPCPostFilterBassBoost); - lpcpfs->Add(left, 0, wxALL, 5); - - newLPCPFControl(&m_codec2LPCPostFilterBeta, &m_staticTextBeta, lpcpfs, "Beta"); - newLPCPFControl(&m_codec2LPCPostFilterGamma, &m_staticTextGamma, lpcpfs, "Gamma"); - - m_LPCPostFilterDefault = new wxButton(this, wxID_ANY, wxT("Default")); - lpcpfs->Add(m_LPCPostFilterDefault, 0, wxALL|wxALIGN_CENTRE_HORIZONTAL|wxALIGN_CENTRE_VERTICAL, 5); - - bSizer30->Add(lpcpfs, 0, wxALL, 0); - - // Speex pre-processor -------------------------------------------------- - - wxStaticBoxSizer* sbSizer_speexpp; - wxStaticBox *sb_speexpp = new wxStaticBox(this, wxID_ANY, _("Speex Mic Audio Pre-Processor")); - sbSizer_speexpp = new wxStaticBoxSizer(sb_speexpp, wxVERTICAL); - - m_ckboxSpeexpp = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_speexpp->SetToolTip(_("Enable noise supression, dereverberation, AGC of mic signal")); - sbSizer_speexpp->Add(m_ckboxSpeexpp, wxALIGN_LEFT, 2); - - bSizer30->Add(sbSizer_speexpp, 0, wxALL, 0); - - // EQ Filters ----------------------------------------------------------- - - wxStaticBoxSizer* eqMicInSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Mic In Equaliser")), wxVERTICAL); - wxBoxSizer* eqMicInSizer1 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* eqMicInSizer2 = new wxBoxSizer(wxHORIZONTAL); - - m_MicInBass = newEQ(eqMicInSizer1, "Bass" , MAX_FREQ_BASS, disableQ); - m_MicInTreble = newEQ(eqMicInSizer1, "Treble", MAX_FREQ_TREBLE, disableQ); - eqMicInSizer->Add(eqMicInSizer1); - - m_MicInEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - eqMicInSizer2->Add(m_MicInEnable,0,wxALIGN_CENTRE_VERTICAL|wxRIGHT,10); - m_MicInMid = newEQ(eqMicInSizer2, "Mid" , MAX_FREQ_DEF, enableQ); - m_MicInDefault = new wxButton(this, wxID_ANY, wxT("Default")); - eqMicInSizer2->Add(m_MicInDefault,0,wxALIGN_CENTRE_VERTICAL|wxLEFT,20); - eqMicInSizer->Add(eqMicInSizer2); - - wxStaticBoxSizer* eqSpkOutSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Speaker Out Equaliser")), wxVERTICAL); - wxBoxSizer* eqSpkOutSizer1 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* eqSpkOutSizer2 = new wxBoxSizer(wxHORIZONTAL); - - m_SpkOutBass = newEQ(eqSpkOutSizer1, "Bass" , MAX_FREQ_BASS, disableQ); - m_SpkOutTreble = newEQ(eqSpkOutSizer1, "Treble", MAX_FREQ_TREBLE, disableQ); - eqSpkOutSizer->Add(eqSpkOutSizer1); - - m_SpkOutEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - eqSpkOutSizer2->Add(m_SpkOutEnable,0,wxALIGN_CENTRE_VERTICAL|wxRIGHT,10); - m_SpkOutMid = newEQ(eqSpkOutSizer2, "Mid" , MAX_FREQ_DEF, enableQ); - m_SpkOutDefault = new wxButton(this, wxID_ANY, wxT("Default")); - eqSpkOutSizer2->Add(m_SpkOutDefault,0,wxALIGN_CENTRE_VERTICAL|wxLEFT,20); - eqSpkOutSizer->Add(eqSpkOutSizer2); - - bSizer30->Add(eqMicInSizer, 0, wxALL, 0); - bSizer30->Add(eqSpkOutSizer, 0, wxALL, 0); - - // Storgage for spectrum magnitude plots ------------------------------------ - - m_MicInMagdB = new float[F_MAG_N]; - for(int i=0; iSetFont(wxFont(8, 70, 90, 90, false, wxEmptyString)); - - bSizer30->Add(m_auiNotebook, 0, wxEXPAND|wxALL, 3); - - m_MicInFreqRespPlot = new PlotSpectrum((wxFrame*) m_auiNotebook, m_MicInMagdB, F_MAG_N, FILTER_MIN_MAG_DB, FILTER_MAX_MAG_DB); - m_auiNotebook->AddPage(m_MicInFreqRespPlot, _("Microphone In Equaliser")); - - m_SpkOutFreqRespPlot = new PlotSpectrum((wxFrame*)m_auiNotebook, m_SpkOutMagdB, F_MAG_N, FILTER_MIN_MAG_DB, FILTER_MAX_MAG_DB); - m_auiNotebook->AddPage(m_SpkOutFreqRespPlot, _("Speaker Out Equaliser")); - - // OK - Cancel buttons at the bottom -------------------------- - - wxBoxSizer* bSizer31 = new wxBoxSizer(wxHORIZONTAL); - - m_sdbSizer5OK = new wxButton(this, wxID_OK); - bSizer31->Add(m_sdbSizer5OK, 0, wxALL, 2); - - m_sdbSizer5Cancel = new wxButton(this, wxID_CANCEL); - bSizer31->Add(m_sdbSizer5Cancel, 0, wxALL, 2); - - bSizer30->Add(bSizer31, 0, wxALIGN_RIGHT|wxALL, 0); - - this->SetSizer(bSizer30); - this->Layout(); - - this->Centre(wxBOTH); - - // Connect Events ------------------------------------------------------- - - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(FilterDlg::OnInitDialog)); - - m_codec2LPCPostFilterEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnEnable), NULL, this); - m_codec2LPCPostFilterBassBoost->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnBassBoost), NULL, this); - m_codec2LPCPostFilterBeta->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnBetaScroll), NULL, this); - m_codec2LPCPostFilterGamma->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnGammaScroll), NULL, this); - m_LPCPostFilterDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnLPCPostFilterDefault), NULL, this); - - m_ckboxSpeexpp->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpeexppEnable), NULL, this); - - m_MicInBass.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassFreqScroll), NULL, this); - m_MicInBass.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassGainScroll), NULL, this); - m_MicInTreble.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleFreqScroll), NULL, this); - m_MicInTreble.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleGainScroll), NULL, this); - m_MicInMid.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidFreqScroll), NULL, this); - m_MicInMid.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidGainScroll), NULL, this); - m_MicInMid.sliderQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidQScroll), NULL, this); - m_MicInEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnMicInEnable), NULL, this); - m_MicInDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnMicInDefault), NULL, this); - - m_SpkOutBass.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassFreqScroll), NULL, this); - m_SpkOutBass.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassGainScroll), NULL, this); - m_SpkOutTreble.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleFreqScroll), NULL, this); - m_SpkOutTreble.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleGainScroll), NULL, this); - m_SpkOutMid.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidFreqScroll), NULL, this); - m_SpkOutMid.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidGainScroll), NULL, this); - m_SpkOutMid.sliderQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidQScroll), NULL, this); - m_SpkOutEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpkOutEnable), NULL, this); - m_SpkOutDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnSpkOutDefault), NULL, this); - - m_sdbSizer5Cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnCancel), NULL, this); - m_sdbSizer5OK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnOK), NULL, this); - -} - -//------------------------------------------------------------------------- -// ~FilterDlg() -//------------------------------------------------------------------------- -FilterDlg::~FilterDlg() -{ - delete m_MicInMagdB; - delete m_SpkOutMagdB; - - // Disconnect Events - - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(FilterDlg::OnInitDialog)); - - m_codec2LPCPostFilterEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnEnable), NULL, this); - m_codec2LPCPostFilterBassBoost->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnBassBoost), NULL, this); - m_codec2LPCPostFilterBeta->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnBetaScroll), NULL, this); - m_codec2LPCPostFilterGamma->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnGammaScroll), NULL, this); - m_LPCPostFilterDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnLPCPostFilterDefault), NULL, this); - - m_MicInBass.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassFreqScroll), NULL, this); - m_MicInBass.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassGainScroll), NULL, this); - m_MicInTreble.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleFreqScroll), NULL, this); - m_MicInTreble.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleGainScroll), NULL, this); - m_MicInMid.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidFreqScroll), NULL, this); - m_MicInMid.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidGainScroll), NULL, this); - m_MicInMid.sliderQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidQScroll), NULL, this); - m_MicInEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnMicInEnable), NULL, this); - m_MicInDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnMicInDefault), NULL, this); - - m_SpkOutBass.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassFreqScroll), NULL, this); - m_SpkOutBass.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassGainScroll), NULL, this); - m_SpkOutTreble.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleFreqScroll), NULL, this); - m_SpkOutTreble.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleGainScroll), NULL, this); - m_SpkOutMid.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidFreqScroll), NULL, this); - m_SpkOutMid.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidGainScroll), NULL, this); - m_SpkOutMid.sliderQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidQScroll), NULL, this); - m_SpkOutEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpkOutEnable), NULL, this); - m_SpkOutDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnSpkOutDefault), NULL, this); - - m_sdbSizer5Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnCancel), NULL, this); - m_sdbSizer5OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnOK), NULL, this); -} - -void FilterDlg::newLPCPFControl(wxSlider **slider, wxStaticText **stValue, wxSizer *s, wxString controlName) -{ - wxBoxSizer *bs = new wxBoxSizer(wxHORIZONTAL); - - wxStaticText* st = new wxStaticText(this, wxID_ANY, controlName, wxDefaultPosition, wxSize(70,-1), wxALIGN_RIGHT); - bs->Add(st, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 2); - - *slider = new wxSlider(this, wxID_ANY, 0, 0, SLIDER_MAX, wxDefaultPosition, wxSize(SLIDER_LENGTH,wxDefaultCoord)); - bs->Add(*slider, 1, wxALIGN_CENTER_VERTICAL|wxALL, 2); - - *stValue = new wxStaticText(this, wxID_ANY, wxT("0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - bs->Add(*stValue, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL, 2); - - s->Add(bs, 0); -} - -void FilterDlg::newEQControl(wxSlider** slider, wxStaticText** value, wxStaticBoxSizer *bs, wxString controlName) -{ - wxStaticText* label = new wxStaticText(this, wxID_ANY, controlName, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); - bs->Add(label, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 0); - - *slider = new wxSlider(this, wxID_ANY, 0, 0, SLIDER_MAX, wxDefaultPosition, wxSize(SLIDER_LENGTH,wxDefaultCoord)); - bs->Add(*slider, 1, wxALIGN_CENTER_VERTICAL|wxALL, 0); - - *value = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(40,-1), wxALIGN_LEFT); - bs->Add(*value, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxRIGHT, 5); -} - -EQ FilterDlg::newEQ(wxSizer *bs, wxString eqName, float maxFreqHz, bool enableQ) -{ - EQ eq; - - wxStaticBoxSizer *bsEQ = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, eqName), wxHORIZONTAL); - - newEQControl(&eq.sliderFreq, &eq.valueFreq, bsEQ, "Freq"); - eq.maxFreqHz = maxFreqHz; - eq.sliderFreqId = eq.sliderFreq->GetId(); - - newEQControl(&eq.sliderGain, &eq.valueGain, bsEQ, "Gain"); - if (enableQ) - newEQControl(&eq.sliderQ, &eq.valueQ, bsEQ, "Q"); - else - eq.sliderQ = NULL; - - bs->Add(bsEQ); - - return eq; -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void FilterDlg::ExchangeData(int inout, bool storePersistent) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - if(inout == EXCHANGE_DATA_IN) - { - // LPC Post filter - - m_codec2LPCPostFilterEnable->SetValue(wxGetApp().m_codec2LPCPostFilterEnable); - m_codec2LPCPostFilterBassBoost->SetValue(wxGetApp().m_codec2LPCPostFilterBassBoost); - m_beta = wxGetApp().m_codec2LPCPostFilterBeta; setBeta(); - m_gamma = wxGetApp().m_codec2LPCPostFilterGamma; setGamma(); - - // Speex Pre-Processor - - m_ckboxSpeexpp->SetValue(wxGetApp().m_speexpp_enable); - - // Mic In Equaliser - - m_MicInBass.freqHz = wxGetApp().m_MicInBassFreqHz; - m_MicInBass.freqHz = limit(m_MicInBass.freqHz, 1.0, MAX_FREQ_BASS); - setFreq(&m_MicInBass); - m_MicInBass.gaindB = wxGetApp().m_MicInBassGaindB; - m_MicInBass.gaindB = limit(m_MicInBass.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInBass); - - m_MicInTreble.freqHz = wxGetApp().m_MicInTrebleFreqHz; - m_MicInTreble.freqHz = limit(m_MicInTreble.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_MicInTreble); - m_MicInTreble.gaindB = wxGetApp().m_MicInTrebleGaindB; - m_MicInTreble.gaindB = limit(m_MicInTreble.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInTreble); - - m_MicInMid.freqHz = wxGetApp().m_MicInMidFreqHz; - m_MicInMid.freqHz = limit(m_MicInMid.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_MicInMid); - m_MicInMid.gaindB = wxGetApp().m_MicInMidGaindB; - m_MicInMid.gaindB = limit(m_MicInMid.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInMid); - m_MicInMid.Q = wxGetApp().m_MicInMidQ; - m_MicInMid.Q = limit(m_MicInMid.Q, pow(10.0,MIN_LOG10_Q), pow(10.0, MAX_LOG10_Q)); - setQ(&m_MicInMid); - - m_MicInEnable->SetValue(wxGetApp().m_MicInEQEnable); - - plotMicInFilterSpectrum(); - - // Spk Out Equaliser - - m_SpkOutBass.freqHz = wxGetApp().m_SpkOutBassFreqHz; - m_SpkOutBass.freqHz = limit(m_SpkOutBass.freqHz, 1.0, MAX_FREQ_BASS); - setFreq(&m_SpkOutBass); - m_SpkOutBass.gaindB = wxGetApp().m_SpkOutBassGaindB; - m_SpkOutBass.gaindB = limit(m_SpkOutBass.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutBass); - - m_SpkOutTreble.freqHz = wxGetApp().m_SpkOutTrebleFreqHz; - m_SpkOutTreble.freqHz = limit(m_SpkOutTreble.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_SpkOutTreble); - m_SpkOutTreble.gaindB = wxGetApp().m_SpkOutTrebleGaindB; - m_SpkOutTreble.gaindB = limit(m_SpkOutTreble.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutTreble); - - m_SpkOutMid.freqHz = wxGetApp().m_SpkOutMidFreqHz; - m_SpkOutMid.freqHz = limit(m_SpkOutMid.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_SpkOutMid); - m_SpkOutMid.gaindB = wxGetApp().m_SpkOutMidGaindB; - m_SpkOutMid.gaindB = limit(m_SpkOutMid.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutMid); - m_SpkOutMid.Q = wxGetApp().m_SpkOutMidQ; - m_SpkOutMid.Q = limit(m_SpkOutMid.Q, pow(10.0,MIN_LOG10_Q), pow(10.0, MAX_LOG10_Q)); - setQ(&m_SpkOutMid); - - m_SpkOutEnable->SetValue(wxGetApp().m_SpkOutEQEnable); - - plotSpkOutFilterSpectrum(); - } - if(inout == EXCHANGE_DATA_OUT) - { - // LPC Post filter - - wxGetApp().m_codec2LPCPostFilterEnable = m_codec2LPCPostFilterEnable->GetValue(); - wxGetApp().m_codec2LPCPostFilterBassBoost = m_codec2LPCPostFilterBassBoost->GetValue(); - wxGetApp().m_codec2LPCPostFilterBeta = m_beta; - wxGetApp().m_codec2LPCPostFilterGamma = m_gamma; - - // Speex Pre-Processor - - wxGetApp().m_speexpp_enable = m_ckboxSpeexpp->GetValue(); - - // Mic In Equaliser - - wxGetApp().m_MicInBassFreqHz = m_MicInBass.freqHz; - wxGetApp().m_MicInBassGaindB = m_MicInBass.gaindB; - - wxGetApp().m_MicInTrebleFreqHz = m_MicInTreble.freqHz; - wxGetApp().m_MicInTrebleGaindB = m_MicInTreble.gaindB; - - wxGetApp().m_MicInMidFreqHz = m_MicInMid.freqHz; - wxGetApp().m_MicInMidGaindB = m_MicInMid.gaindB; - wxGetApp().m_MicInMidQ = m_MicInMid.Q; - - // Spk Out Equaliser - - wxGetApp().m_SpkOutBassFreqHz = m_SpkOutBass.freqHz; - wxGetApp().m_SpkOutBassGaindB = m_SpkOutBass.gaindB; - - wxGetApp().m_SpkOutTrebleFreqHz = m_SpkOutTreble.freqHz; - wxGetApp().m_SpkOutTrebleGaindB = m_SpkOutTreble.gaindB; - - wxGetApp().m_SpkOutMidFreqHz = m_SpkOutMid.freqHz; - wxGetApp().m_SpkOutMidGaindB = m_SpkOutMid.gaindB; - wxGetApp().m_SpkOutMidQ = m_SpkOutMid.Q; - - if (storePersistent) { - pConfig->Write(wxT("/Filter/codec2LPCPostFilterEnable"), wxGetApp().m_codec2LPCPostFilterEnable); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterBassBoost"), wxGetApp().m_codec2LPCPostFilterBassBoost); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterBeta"), (int)(m_beta*100.0)); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterGamma"), (int)(m_gamma*100.0)); - - pConfig->Write(wxT("/Filter/speexpp_enable"), wxGetApp().m_speexpp_enable); - - pConfig->Write(wxT("/Filter/MicInBassFreqHz"), (int)m_MicInBass.freqHz); - pConfig->Write(wxT("/Filter/MicInBassGaindB"), (int)(10.0*m_MicInBass.gaindB)); - pConfig->Write(wxT("/Filter/MicInTrebleFreqHz"), (int)m_MicInTreble.freqHz); - pConfig->Write(wxT("/Filter/MicInTrebleGaindB"), (int)(10.0*m_MicInTreble.gaindB)); - pConfig->Write(wxT("/Filter/MicInMidFreqHz"), (int)m_MicInMid.freqHz); - pConfig->Write(wxT("/Filter/MicInMidGaindB"), (int)(10.0*m_MicInMid.gaindB)); - pConfig->Write(wxT("/Filter/MicInMidQ"), (int)(100.0*m_MicInMid.Q)); - - pConfig->Write(wxT("/Filter/SpkOutBassFreqHz"), (int)m_SpkOutBass.freqHz); - pConfig->Write(wxT("/Filter/SpkOutBassGaindB"), (int)(10.0*m_SpkOutBass.gaindB)); - pConfig->Write(wxT("/Filter/SpkOutTrebleFreqHz"), (int)m_SpkOutTreble.freqHz); - pConfig->Write(wxT("/Filter/SpkOutTrebleGaindB"), (int)(10.0*m_SpkOutTreble.gaindB)); - pConfig->Write(wxT("/Filter/SpkOutMidQ"), (int)(100.0*m_SpkOutMid.Q)); - pConfig->Write(wxT("/Filter/SpkOutMidFreqHz"), (int)m_SpkOutMid.freqHz); - pConfig->Write(wxT("/Filter/SpkOutMidGaindB"), (int)(10.0*m_SpkOutMid.gaindB)); - - pConfig->Flush(); - } - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -float FilterDlg::limit(float value, float min, float max) { - if (value < min) return min; - if (value > max) return max; - return value; -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void FilterDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnDefault() -//------------------------------------------------------------------------- - -void FilterDlg::OnLPCPostFilterDefault(wxCommandEvent& event) -{ - m_beta = CODEC2_LPC_PF_BETA; setBeta(); - m_gamma = CODEC2_LPC_PF_GAMMA; setGamma(); - m_codec2LPCPostFilterEnable->SetValue(true); - m_codec2LPCPostFilterBassBoost->SetValue(true); -} - -void FilterDlg::OnMicInDefault(wxCommandEvent& event) -{ - m_MicInBass.freqHz = 100.0; - m_MicInBass.gaindB = 0.0; - setFreq(&m_MicInBass); setGain(&m_MicInBass); - - m_MicInTreble.freqHz = 3000.0; - m_MicInTreble.gaindB = 0.0; - setFreq(&m_MicInTreble); setGain(&m_MicInTreble); - - m_MicInMid.freqHz = 1500.0; - m_MicInMid.gaindB = 0.0; - m_MicInMid.Q = 1.0; - setFreq(&m_MicInMid); setGain(&m_MicInMid); setQ(&m_MicInMid); - - plotMicInFilterSpectrum(); -} - -void FilterDlg::OnSpkOutDefault(wxCommandEvent& event) -{ - m_SpkOutBass.freqHz = 100.0; - m_SpkOutBass.gaindB = 0.0; - setFreq(&m_SpkOutBass); setGain(&m_SpkOutBass); - - m_SpkOutTreble.freqHz = 3000.0; - m_SpkOutTreble.gaindB = 0.0; - setFreq(&m_SpkOutTreble); setGain(&m_SpkOutTreble); - - m_SpkOutMid.freqHz = 1500.0; - m_SpkOutMid.gaindB = 0.0; - m_SpkOutMid.Q = 1.0; - setFreq(&m_SpkOutMid); setGain(&m_SpkOutMid); setQ(&m_SpkOutMid); - - plotSpkOutFilterSpectrum(); -} - -//------------------------------------------------------------------------- -// OnOK() -//------------------------------------------------------------------------- -void FilterDlg::OnOK(wxCommandEvent& event) -{ - //printf("FilterDlg::OnOK\n"); - ExchangeData(EXCHANGE_DATA_OUT, true); - this->EndModal(wxID_OK); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void FilterDlg::OnClose(wxCloseEvent& event) -{ - this->EndModal(wxID_OK); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void FilterDlg::OnInitDialog(wxInitDialogEvent& event) -{ - //printf("FilterDlg::OnInitDialog\n"); - ExchangeData(EXCHANGE_DATA_IN, false); - //printf("m_beta: %f\n", m_beta); -} - -void FilterDlg::setBeta(void) { - wxString buf; - buf.Printf(wxT("%3.2f"), m_beta); - m_staticTextBeta->SetLabel(buf); - int slider = (int)(m_beta*SLIDER_MAX + 0.5); - m_codec2LPCPostFilterBeta->SetValue(slider); -} - -void FilterDlg::setCodec2(void) { - if (m_running) { - codec2_set_lpc_post_filter(freedv_get_codec2(g_pfreedv), - m_codec2LPCPostFilterEnable->GetValue(), - m_codec2LPCPostFilterBassBoost->GetValue(), - m_beta, m_gamma); - } -} - -void FilterDlg::setGamma(void) { - wxString buf; - buf.Printf(wxT("%3.2f"), m_gamma); - m_staticTextGamma->SetLabel(buf); - int slider = (int)(m_gamma*SLIDER_MAX + 0.5); - m_codec2LPCPostFilterGamma->SetValue(slider); -} - -void FilterDlg::OnEnable(wxScrollEvent& event) { - setCodec2(); -} - -void FilterDlg::OnBassBoost(wxScrollEvent& event) { - setCodec2(); -} - -void FilterDlg::OnBetaScroll(wxScrollEvent& event) { - m_beta = (float)m_codec2LPCPostFilterBeta->GetValue()/SLIDER_MAX; - setBeta(); - setCodec2(); -} - -void FilterDlg::OnGammaScroll(wxScrollEvent& event) { - m_gamma = (float)m_codec2LPCPostFilterGamma->GetValue()/SLIDER_MAX; - setGamma(); - setCodec2(); -} - -// immediately change enable flags rather using ExchangeData() so we can switch on and off at run time - -void FilterDlg::OnSpeexppEnable(wxScrollEvent& event) { - wxGetApp().m_speexpp_enable = m_ckboxSpeexpp->GetValue(); -} - -void FilterDlg::OnMicInEnable(wxScrollEvent& event) { - wxGetApp().m_MicInEQEnable = m_MicInEnable->GetValue(); -} - -void FilterDlg::OnSpkOutEnable(wxScrollEvent& event) { - wxGetApp().m_SpkOutEQEnable = m_SpkOutEnable->GetValue(); - //printf("wxGetApp().m_SpkOutEQEnable: %d\n", wxGetApp().m_SpkOutEQEnable); -} - -void FilterDlg::setFreq(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%3.0f"), eq->freqHz); - eq->valueFreq->SetLabel(buf); - int slider = (int)((eq->freqHz/eq->maxFreqHz)*SLIDER_MAX + 0.5); - eq->sliderFreq->SetValue(slider); -} - -void FilterDlg::sliderToFreq(EQ *eq, bool micIn) -{ - eq->freqHz = ((float)eq->sliderFreq->GetValue()/SLIDER_MAX)*eq->maxFreqHz; - if (eq->freqHz < 1.0) eq->freqHz = 1.0; // sox doesn't like 0 Hz; - setFreq(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } -} - -void FilterDlg::setGain(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%3.1f"), eq->gaindB); - eq->valueGain->SetLabel(buf); - int slider = (int)(((eq->gaindB-MIN_GAIN)/(MAX_GAIN-MIN_GAIN))*SLIDER_MAX + 0.5); - eq->sliderGain->SetValue(slider); -} - -void FilterDlg::sliderToGain(EQ *eq, bool micIn) -{ - float range = MAX_GAIN-MIN_GAIN; - - eq->gaindB = MIN_GAIN + range*((float)eq->sliderGain->GetValue()/SLIDER_MAX); - //printf("gaindB: %f\n", eq->gaindB); - setGain(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } - -} - -void FilterDlg::setQ(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%2.1f"), eq->Q); - eq->valueQ->SetLabel(buf); - - float log10_range = MAX_LOG10_Q - MIN_LOG10_Q; - - int slider = (int)(((log10(eq->Q+1E-6)-MIN_LOG10_Q)/log10_range)*SLIDER_MAX + 0.5); - eq->sliderQ->SetValue(slider); -} - -void FilterDlg::sliderToQ(EQ *eq, bool micIn) -{ - float log10_range = MAX_LOG10_Q - MIN_LOG10_Q; - - float sliderNorm = (float)eq->sliderQ->GetValue()/SLIDER_MAX; - float log10Q = MIN_LOG10_Q + sliderNorm*(log10_range); - eq->Q = pow(10.0, log10Q); - //printf("log10Q: %f eq->Q: %f\n", log10Q, eq->Q); - setQ(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } -} - -void FilterDlg::plotMicInFilterSpectrum(void) { - plotFilterSpectrum(&m_MicInBass, &m_MicInMid, &m_MicInTreble, m_MicInFreqRespPlot, m_MicInMagdB); -} - -void FilterDlg::plotSpkOutFilterSpectrum(void) { - plotFilterSpectrum(&m_SpkOutBass, &m_SpkOutMid, &m_SpkOutTreble, m_SpkOutFreqRespPlot, m_SpkOutMagdB); -} - -void FilterDlg::adjRunTimeMicInFilter(void) { - // signal an adjustment in running filter coeffs - - if (m_running) { - ExchangeData(EXCHANGE_DATA_OUT, false); - *m_newMicInFilter = true; - } -} - -void FilterDlg::adjRunTimeSpkOutFilter(void) { - // signal an adjustment in running filter coeffs - - if (m_running) { - ExchangeData(EXCHANGE_DATA_OUT, false); - *m_newSpkOutFilter = true; - } -} - - -void FilterDlg::plotFilterSpectrum(EQ *eqBass, EQ *eqMid, EQ *eqTreble, PlotSpectrum* freqRespPlot, float *magdB) { - char *argBass[10]; - char *argTreble[10]; - char *argMid[10]; - char argstorage[10][80]; - float magBass[F_MAG_N]; - float magTreble[F_MAG_N]; - float magMid[F_MAG_N]; - int i; - - for(i=0; i<10; i++) { - argBass[i] = &argstorage[i][0]; - argTreble[i] = &argstorage[i][0]; - argMid[i] = &argstorage[i][0]; - } - sprintf(argBass[0], "bass"); - sprintf(argBass[1], "%f", eqBass->gaindB+1E-6); - sprintf(argBass[2], "%f", eqBass->freqHz); - - calcFilterSpectrum(magBass, 2, argBass); - - sprintf(argTreble[0], "treble"); - sprintf(argTreble[1], "%f", eqTreble->gaindB+1E-6); - sprintf(argTreble[2], "%f", eqTreble->freqHz); - - calcFilterSpectrum(magTreble, 2, argTreble); - - sprintf(argTreble[0], "equalizer"); - sprintf(argTreble[1], "%f", eqMid->freqHz); - sprintf(argTreble[2], "%f", eqMid->Q); - sprintf(argTreble[3], "%f", eqMid->gaindB+1E-6); - - calcFilterSpectrum(magMid, 3, argMid); - - for(i=0; im_newdata = true; - freqRespPlot->Refresh(); -} - -void FilterDlg::calcFilterSpectrum(float magdB[], int argc, char *argv[]) { - void *sbq; - short in[NIMP]; - short out[NIMP]; - COMP X[F_MAG_N]; - float f, w; - int i, k; - - // find impulse response ----------------------------------- - - for(i=0; i. -// -//========================================================================== - -#ifndef __FILTER_DIALOG__ -#define __FILTER_DIALOG__ - -#include "fdmdv2_main.h" - -enum {disableQ = false, enableQ = true}; - -typedef struct { - wxSlider *sliderFreq; - wxStaticText *valueFreq; - wxSlider *sliderGain; - wxStaticText *valueGain; - wxSlider *sliderQ; - wxStaticText *valueQ; - - int sliderFreqId; - int sliderGainId; - int sliderQId; - - float freqHz; - float gaindB; - float Q; - - float maxFreqHz; -} EQ; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class FilterDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class FilterDlg : public wxDialog -{ - public: - FilterDlg( wxWindow* parent, bool running, bool *newMicInFilter, bool *newSpkOutFilter, - wxWindowID id = wxID_ANY, const wxString& title = _("Filter"), - const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 800, 630 ), - long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~FilterDlg(); - - void ExchangeData(int inout, bool storePersistent); - - protected: - // Handlers for events. - void OnCancel(wxCommandEvent& event); - void OnOK(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void OnInitDialog(wxInitDialogEvent& event); - void OnLPCPostFilterDefault(wxCommandEvent& event); - - void OnBetaScroll(wxScrollEvent& event); - void OnGammaScroll(wxScrollEvent& event); - void OnEnable(wxScrollEvent& event); - void OnBassBoost(wxScrollEvent& event); - - void OnSpeexppEnable(wxScrollEvent& event); - - void OnMicInBassFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInBass, true); } - void OnMicInBassGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInBass, true); } - void OnMicInTrebleFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInTreble, true); } - void OnMicInTrebleGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInTreble, true); } - void OnMicInMidFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInMid, true); } - void OnMicInMidGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInMid, true); } - void OnMicInMidQScroll(wxScrollEvent& event) { sliderToQ(&m_MicInMid, true); } - void OnMicInEnable(wxScrollEvent& event); - void OnMicInDefault(wxCommandEvent& event); - - void OnSpkOutBassFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutBass, false); } - void OnSpkOutBassGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutBass, false); } - void OnSpkOutTrebleFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutTreble, false); } - void OnSpkOutTrebleGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutTreble, false); } - void OnSpkOutMidFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutMid, false); } - void OnSpkOutMidGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutMid, false); } - void OnSpkOutMidQScroll(wxScrollEvent& event) { sliderToQ(&m_SpkOutMid, false); } - void OnSpkOutEnable(wxScrollEvent& event); - void OnSpkOutDefault(wxCommandEvent& event); - - wxStaticText* m_staticText8; - wxCheckBox* m_codec2LPCPostFilterEnable; - wxStaticText* m_staticText9; - wxCheckBox* m_codec2LPCPostFilterBassBoost; - wxStaticText* m_staticText91; - wxSlider* m_codec2LPCPostFilterBeta; - wxStaticText* m_staticTextBeta; - wxStaticText* m_staticText911; - wxSlider* m_codec2LPCPostFilterGamma; - wxStaticText* m_staticTextGamma; - wxButton* m_LPCPostFilterDefault; - - wxCheckBox* m_ckboxSpeexpp; - - wxStdDialogButtonSizer* m_sdbSizer5; - wxButton* m_sdbSizer5OK; - wxButton* m_sdbSizer5Cancel; - PlotSpectrum* m_MicInFreqRespPlot; - PlotSpectrum* m_SpkOutFreqRespPlot; - - wxCheckBox* m_MicInEnable; - wxButton* m_MicInDefault; - wxCheckBox* m_SpkOutEnable; - wxButton* m_SpkOutDefault; - - float *m_MicInMagdB; - float *m_SpkOutMagdB; - - private: - bool m_running; - float m_beta; - float m_gamma; - - void setBeta(void); // sets slider and static text from m_beta - void setGamma(void); // sets slider and static text from m_gamma - void setCodec2(void); - - void newEQControl(wxSlider** slider, wxStaticText** value, wxStaticBoxSizer *bs, wxString controlName); - EQ newEQ(wxSizer *bs, wxString eqName, float maxFreqHz, bool enableQ); - void newLPCPFControl(wxSlider **slider, wxStaticText **stValue, wxSizer *sbs, wxString controlName); - wxAuiNotebook *m_auiNotebook; - void setFreq(EQ *eq); - void setGain(EQ *eq); - void setQ(EQ *eq); - void sliderToFreq(EQ *eq, bool micIn); - void sliderToGain(EQ *eq, bool micIn); - void sliderToQ(EQ *eq, bool micIn); - void plotFilterSpectrum(EQ *eqBass, EQ *eqMid, EQ* eqTreble, PlotSpectrum* freqRespPlot, float *magdB); - void calcFilterSpectrum(float magdB[], int arc, char *argv[]); - void plotMicInFilterSpectrum(void); - void plotSpkOutFilterSpectrum(void); - void adjRunTimeMicInFilter(void); - void adjRunTimeSpkOutFilter(void); - - EQ m_MicInBass; - EQ m_MicInMid; - EQ m_MicInTreble; - - EQ m_SpkOutBass; - EQ m_SpkOutMid; - EQ m_SpkOutTreble; - - float limit(float value, float min, float max); - - bool *m_newMicInFilter; - bool *m_newSpkOutFilter; - -}; - -#endif // __FILTER_DIALOG__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/dlg_options.cpp b/freedv/tags/1.2.2/freedv-dev/src/dlg_options.cpp deleted file mode 100644 index 79b6c87f..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/dlg_options.cpp +++ /dev/null @@ -1,616 +0,0 @@ -//========================================================================== -// Name: dlg_options.cpp -// Purpose: Dialog for controlling misc FreeDV options -// Date: May 24 2013 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "dlg_options.h" - -extern bool g_modal; -extern struct freedv *g_pfreedv; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class OptionsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -OptionsDlg::OptionsDlg(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* bSizer30; - bSizer30 = new wxBoxSizer(wxVERTICAL); - - //------------------------------ - // Txt Msg Text Box - //------------------------------ - - wxStaticBoxSizer* sbSizer_callSign; - wxStaticBox *sb_textMsg = new wxStaticBox(this, wxID_ANY, _("Txt Msg")); - sbSizer_callSign = new wxStaticBoxSizer(sb_textMsg, wxVERTICAL); - - m_txtCtrlCallSign = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - m_txtCtrlCallSign->SetToolTip(_("Txt Msg you can send along with Voice")); - sbSizer_callSign->Add(m_txtCtrlCallSign, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 3); - - bSizer30->Add(sbSizer_callSign,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //---------------------------------------------------------------------- - // Voice Keyer - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer28a = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Voice Keyer")), wxHORIZONTAL); - - wxStaticText *m_staticText28b = new wxStaticText(this, wxID_ANY, _("Wave File: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28b, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtCtrlVoiceKeyerWaveFile = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300,-1), 0); - m_txtCtrlVoiceKeyerWaveFile->SetToolTip(_("Wave file to play for Voice Keyer")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerWaveFile, 0, 0, 5); - - m_buttonChooseVoiceKeyerWaveFile = new wxButton(this, wxID_APPLY, _("Choose"), wxDefaultPosition, wxSize(-1,-1), 0); - staticBoxSizer28a->Add(m_buttonChooseVoiceKeyerWaveFile, 0, wxALIGN_CENTER_VERTICAL, 5); - - wxStaticText *m_staticText28c = new wxStaticText(this, wxID_ANY, _(" Rx Pause: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28c, 0, wxALIGN_CENTER_VERTICAL , 5); - m_txtCtrlVoiceKeyerRxPause = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0); - m_txtCtrlVoiceKeyerRxPause->SetToolTip(_("How long to wait in Rx mode before repeat")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerRxPause, 0, 0, 5); - - wxStaticText *m_staticText28d = new wxStaticText(this, wxID_ANY, _(" Repeats: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28d, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtCtrlVoiceKeyerRepeats = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0); - m_txtCtrlVoiceKeyerRepeats->SetToolTip(_("How long to wait in Rx mode before repeat")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerRepeats, 0, 0, 5); - - bSizer30->Add(staticBoxSizer28a,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - -#ifdef __WXMSW__ - //------------------------------ - // debug console, for WIndows build make console pop up for debug messages - //------------------------------ - - wxStaticBoxSizer* sbSizer_console; - wxStaticBox *sb_console = new wxStaticBox(this, wxID_ANY, _("Debug")); - sbSizer_console = new wxStaticBoxSizer(sb_console, wxHORIZONTAL); - - m_ckboxDebugConsole = new wxCheckBox(this, wxID_ANY, _("Show Console"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_console->Add(m_ckboxDebugConsole, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_console,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); -#endif - - //------------------------------ - // FreeDV 700 Options - //------------------------------ - - wxStaticBoxSizer* sbSizer_freedv700; - wxStaticBox *sb_freedv700 = new wxStaticBox(this, wxID_ANY, _("FreeDV 700 Options")); - sbSizer_freedv700 = new wxStaticBoxSizer(sb_freedv700, wxHORIZONTAL); - - m_ckboxFreeDV700txClip = new wxCheckBox(this, wxID_ANY, _("Clipping"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_freedv700->Add(m_ckboxFreeDV700txClip, 0, wxALIGN_LEFT, 0); - m_ckboxFreeDV700Combine = new wxCheckBox(this, wxID_ANY, _("Diversity Combine for plots"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_freedv700->Add(m_ckboxFreeDV700Combine, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_freedv700, 0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Half/Full duplex selection - //------------------------------ - - wxStaticBox *sb_duplex = new wxStaticBox(this, wxID_ANY, _("Half/Full Duplex Operation")); - wxStaticBoxSizer* sbSizer_duplex = new wxStaticBoxSizer(sb_duplex, wxHORIZONTAL); - m_ckHalfDuplex = new wxCheckBox(this, wxID_ANY, _("Half Duplex"), wxDefaultPosition, wxSize(-1,-1), 0); - sbSizer_duplex->Add(m_ckHalfDuplex, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5); - bSizer30->Add(sbSizer_duplex,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Test Frames/Channel simulation check box - //------------------------------ - - wxStaticBoxSizer* sbSizer_testFrames; - wxStaticBox *sb_testFrames = new wxStaticBox(this, wxID_ANY, _("Testing and Channel Simulation")); - sbSizer_testFrames = new wxStaticBoxSizer(sb_testFrames, wxHORIZONTAL); - - m_ckboxTestFrame = new wxCheckBox(this, wxID_ANY, _("Test Frames"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxTestFrame, 0, wxALIGN_LEFT, 0); - - m_ckboxChannelNoise = new wxCheckBox(this, wxID_ANY, _("Channel Noise SNR (dB):"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxChannelNoise, 0, wxALIGN_LEFT, 0); - m_txtNoiseSNR = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_testFrames->Add(m_txtNoiseSNR, 0, wxALIGN_LEFT, 0); - - m_ckboxAttnCarrierEn = new wxCheckBox(this, wxID_ANY, _("Attn Carrier Carrier:"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxAttnCarrierEn, 0, wxALIGN_LEFT, 0); - m_txtAttnCarrier = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_testFrames->Add(m_txtAttnCarrier, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_testFrames,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Interfering tone - //------------------------------ - - wxStaticBoxSizer* sbSizer_tone; - wxStaticBox *sb_tone = new wxStaticBox(this, wxID_ANY, _("Simulated Interference Tone")); - sbSizer_tone = new wxStaticBoxSizer(sb_tone, wxHORIZONTAL); - - m_ckboxTone = new wxCheckBox(this, wxID_ANY, _("Tone Freq (Hz):"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_tone->Add(m_ckboxTone, 0, wxALIGN_LEFT, 0); - m_txtToneFreqHz = new wxTextCtrl(this, wxID_ANY, "1000", wxDefaultPosition, wxSize(60,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_tone->Add(m_txtToneFreqHz, 0, wxALIGN_LEFT, 0); - wxStaticText *m_staticTextta = new wxStaticText(this, wxID_ANY, _(" Amplitude (pk): "), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_tone->Add(m_staticTextta, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtToneAmplitude = new wxTextCtrl(this, wxID_ANY, "1000", wxDefaultPosition, wxSize(60,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_tone->Add(m_txtToneAmplitude, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_tone,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - -#ifdef __EXPERIMENTAL_UDP__ - //------------------------------ - // Txt Encoding - //------------------------------ - - wxStaticBoxSizer* sbSizer_encoding = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Text Encoding")), wxHORIZONTAL); - -#ifdef SHORT_VARICODE - m_rb_textEncoding1 = new wxRadioButton( this, wxID_ANY, wxT("Long varicode"), wxDefaultPosition, wxDefaultSize, 0); - m_rb_textEncoding1->SetValue(true); - sbSizer_encoding->Add(m_rb_textEncoding1, 0, wxALIGN_LEFT|wxALL, 1); - m_rb_textEncoding2 = new wxRadioButton( this, wxID_ANY, wxT("Short Varicode"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_encoding->Add(m_rb_textEncoding2, 0, wxALIGN_LEFT|wxALL, 1); -#endif - - m_ckboxEnableChecksum = new wxCheckBox(this, wxID_ANY, _("Use Checksum on Rx"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_encoding->Add(m_ckboxEnableChecksum, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_encoding,0, wxALL|wxEXPAND, 3); - - //------------------------------ - // Event processing - //------------------------------ - - wxStaticBoxSizer* sbSizer_events; - wxStaticBox *sb_events = new wxStaticBox(this, wxID_ANY, _("Event Processing")); - sbSizer_events = new wxStaticBoxSizer(sb_events, wxVERTICAL); - - // event processing enable and spam timer - - wxStaticBoxSizer* sbSizer_events_top; - wxStaticBox* sb_events1 = new wxStaticBox(this, wxID_ANY, _("")); - sbSizer_events_top = new wxStaticBoxSizer(sb_events1, wxHORIZONTAL); - - m_ckbox_events = new wxCheckBox(this, wxID_ANY, _("Enable System Calls Syscall Spam Timer"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_events->SetToolTip(_("Enable processing of events and generation of system calls")); - sbSizer_events_top->Add(m_ckbox_events, 0, 0, 5); - m_txt_spam_timer = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - m_txt_spam_timer->SetToolTip(_("Many matching events can cause a flood of syscalls. Set minimum time (seconds) between syscalls for each event here")); - sbSizer_events_top->Add(m_txt_spam_timer, 0, 0, 5); - m_rb_spam_timer = new wxRadioButton( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - m_rb_spam_timer->SetForegroundColour( wxColour(0, 255, 0 ) ); - sbSizer_events_top->Add(m_rb_spam_timer, 0, 0, 10); - sbSizer_events->Add(sbSizer_events_top, 0, 0, 5); - - // list of regexps - - wxStaticBoxSizer* sbSizer_regexp = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Regular Expressions to Process Events")), wxHORIZONTAL); - m_txt_events_regexp_match = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,100), wxTE_MULTILINE); - m_txt_events_regexp_match->SetToolTip(_("Enter regular expressions to match events")); - sbSizer_regexp->Add(m_txt_events_regexp_match, 1, wxEXPAND, 5); - m_txt_events_regexp_replace = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,100), wxTE_MULTILINE); - m_txt_events_regexp_replace->SetToolTip(_("Enter regular expressions to replace events")); - sbSizer_regexp->Add(m_txt_events_regexp_replace, 1, wxEXPAND, 5); - sbSizer_events->Add(sbSizer_regexp, 1, wxEXPAND, 5); - - // log of events and responses - - wxStaticBoxSizer* sbSizer_event_log = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Log of Events and Responses")), wxVERTICAL); - wxBoxSizer* bSizer33 = new wxBoxSizer(wxHORIZONTAL); - m_txt_events_in = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,50), wxTE_MULTILINE | wxTE_READONLY); - bSizer33->Add(m_txt_events_in, 1, wxEXPAND, 5); - m_txt_events_out = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,50), wxTE_MULTILINE | wxTE_READONLY); - bSizer33->Add(m_txt_events_out, 1, wxEXPAND, 5); - sbSizer_event_log->Add(bSizer33, 1, wxEXPAND, 5); - sbSizer_events->Add(sbSizer_event_log, 1, wxEXPAND, 5); - - bSizer30->Add(sbSizer_events,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // UDP control port - //------------------------------ - - wxStaticBoxSizer* sbSizer_udp; - wxStaticBox* sb_udp = new wxStaticBox(this, wxID_ANY, _("UDP Control Port")); - sbSizer_udp = new wxStaticBoxSizer(sb_udp, wxHORIZONTAL); - m_ckbox_udp_enable = new wxCheckBox(this, wxID_ANY, _("Enable UDP Control Port UDP Port Number:"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_udp->SetToolTip(_("Enable control of FreeDV via UDP port")); - sbSizer_udp->Add(m_ckbox_udp_enable, 0, wxALIGN_CENTER_HORIZONTAL, 5); - m_txt_udp_port = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(50,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_udp->Add(m_txt_udp_port, 0, wxALIGN_CENTER_HORIZONTAL, 5); - - bSizer30->Add(sbSizer_udp,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); -#endif - - //------------------------------ - // OK - Cancel - Apply Buttons - //------------------------------ - - wxBoxSizer* bSizer31 = new wxBoxSizer(wxHORIZONTAL); - - m_sdbSizer5OK = new wxButton(this, wxID_OK); - bSizer31->Add(m_sdbSizer5OK, 0, wxALL, 2); - - m_sdbSizer5Cancel = new wxButton(this, wxID_CANCEL); - bSizer31->Add(m_sdbSizer5Cancel, 0, wxALL, 2); - - m_sdbSizer5Apply = new wxButton(this, wxID_APPLY); - bSizer31->Add(m_sdbSizer5Apply, 0, wxALL, 2); - - bSizer30->Add(bSizer31, 0, wxALIGN_CENTER, 0); - - this->SetSizer(bSizer30); - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - this->Layout(); - - this->Centre(wxBOTH); - - // Connect Events ------------------------------------------------------- - - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(OptionsDlg::OnInitDialog)); - - m_sdbSizer5OK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnOK), NULL, this); - m_sdbSizer5Cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnCancel), NULL, this); - m_sdbSizer5Apply->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnApply), NULL, this); - - m_ckboxTestFrame->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnTestFrame), NULL, this); - m_ckboxChannelNoise->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnChannelNoise), NULL, this); - m_ckboxAttnCarrierEn->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnAttnCarrierEn), NULL, this); - - m_ckboxFreeDV700txClip->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700txClip), NULL, this); - m_ckboxFreeDV700Combine->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700Combine), NULL, this); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnDebugConsole), NULL, this); -#endif - - m_buttonChooseVoiceKeyerWaveFile->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnChooseVoiceKeyerWaveFile), NULL, this); - - event_in_serial = 0; - event_out_serial = 0; -} - -//------------------------------------------------------------------------- -// ~OptionsDlg() -//------------------------------------------------------------------------- -OptionsDlg::~OptionsDlg() -{ - - // Disconnect Events - - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(OptionsDlg::OnInitDialog)); - - m_sdbSizer5OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnOK), NULL, this); - m_sdbSizer5Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnCancel), NULL, this); - m_sdbSizer5Apply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnApply), NULL, this); - - m_ckboxTestFrame->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnTestFrame), NULL, this); - m_ckboxChannelNoise->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnChannelNoise), NULL, this); - m_ckboxAttnCarrierEn->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnAttnCarrierEn), NULL, this); - - m_ckboxFreeDV700txClip->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700txClip), NULL, this); - m_ckboxFreeDV700Combine->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700Combine), NULL, this); - m_buttonChooseVoiceKeyerWaveFile->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnChooseVoiceKeyerWaveFile), NULL, this); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnDebugConsole), NULL, this); -#endif -} - - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void OptionsDlg::ExchangeData(int inout, bool storePersistent) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - - if(inout == EXCHANGE_DATA_IN) - { - m_txtCtrlCallSign->SetValue(wxGetApp().m_callSign); - - /* Voice Keyer */ - - m_txtCtrlVoiceKeyerWaveFile->SetValue(wxGetApp().m_txtVoiceKeyerWaveFile); - m_txtCtrlVoiceKeyerRxPause->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intVoiceKeyerRxPause)); - m_txtCtrlVoiceKeyerRepeats->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intVoiceKeyerRepeats)); - - m_ckHalfDuplex->SetValue(wxGetApp().m_boolHalfDuplex); - - m_ckboxTestFrame->SetValue(wxGetApp().m_testFrames); - - m_ckboxChannelNoise->SetValue(wxGetApp().m_channel_noise); - m_txtNoiseSNR->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_noise_snr)); - - m_ckboxTone->SetValue(wxGetApp().m_tone); - m_txtToneFreqHz->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_tone_freq_hz)); - m_txtToneAmplitude->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_tone_amplitude)); - - m_ckboxAttnCarrierEn->SetValue(wxGetApp().m_attn_carrier_en); - m_txtAttnCarrier->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_attn_carrier)); - -#ifdef __EXPERIMENTAL_UDP__ - m_ckbox_events->SetValue(wxGetApp().m_events); - m_txt_spam_timer->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_events_spam_timer)); - - m_txt_events_regexp_match->SetValue(wxGetApp().m_events_regexp_match); - m_txt_events_regexp_replace->SetValue(wxGetApp().m_events_regexp_replace); - - m_ckbox_udp_enable->SetValue(wxGetApp().m_udp_enable); - m_txt_udp_port->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_udp_port)); - -#ifdef SHORT_VARICODE - if (wxGetApp().m_textEncoding == 1) - m_rb_textEncoding1->SetValue(true); - if (wxGetApp().m_textEncoding == 2) - m_rb_textEncoding2->SetValue(true); -#endif - m_ckboxEnableChecksum->SetValue(wxGetApp().m_enable_checksum); -#endif - - m_ckboxFreeDV700txClip->SetValue(wxGetApp().m_FreeDV700txClip); - m_ckboxFreeDV700Combine->SetValue(wxGetApp().m_FreeDV700Combine); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->SetValue(wxGetApp().m_debug_console); -#endif - } - - if(inout == EXCHANGE_DATA_OUT) - { - wxGetApp().m_callSign = m_txtCtrlCallSign->GetValue(); - - wxGetApp().m_boolHalfDuplex = m_ckHalfDuplex->GetValue(); - pConfig->Write(wxT("/Rig/HalfDuplex"), wxGetApp().m_boolHalfDuplex); - - /* Voice Keyer */ - - wxGetApp().m_txtVoiceKeyerWaveFile = m_txtCtrlVoiceKeyerWaveFile->GetValue(); - pConfig->Write(wxT("/VoiceKeyer/WaveFile"), wxGetApp().m_txtVoiceKeyerWaveFile); - long tmp; - m_txtCtrlVoiceKeyerRxPause->GetValue().ToLong(&tmp); if (tmp < 0) tmp = 0; wxGetApp().m_intVoiceKeyerRxPause = (int)tmp; - pConfig->Write(wxT("/VoiceKeyer/RxPause"), wxGetApp().m_intVoiceKeyerRxPause); - m_txtCtrlVoiceKeyerRepeats->GetValue().ToLong(&tmp); - if (tmp < 0) tmp = 0; if (tmp > 100) tmp = 100; - wxGetApp().m_intVoiceKeyerRepeats = (int)tmp; - pConfig->Write(wxT("/VoiceKeyer/Repeats"), wxGetApp().m_intVoiceKeyerRepeats); - - wxGetApp().m_testFrames = m_ckboxTestFrame->GetValue(); - - wxGetApp().m_channel_noise = m_ckboxChannelNoise->GetValue(); - long noise_snr; - m_txtNoiseSNR->GetValue().ToLong(&noise_snr); - wxGetApp().m_noise_snr = (int)noise_snr; - - wxGetApp().m_tone = m_ckboxTone->GetValue(); - long tone_freq_hz, tone_amplitude; - m_txtToneFreqHz->GetValue().ToLong(&tone_freq_hz); - wxGetApp().m_tone_freq_hz = (int)tone_freq_hz; - m_txtToneAmplitude->GetValue().ToLong(&tone_amplitude); - wxGetApp().m_tone_amplitude = (int)tone_amplitude; - - wxGetApp().m_attn_carrier_en = m_ckboxAttnCarrierEn->GetValue(); - long attn_carrier; - m_txtAttnCarrier->GetValue().ToLong(&attn_carrier); - wxGetApp().m_attn_carrier = (int)attn_carrier; - -#ifdef __EXPERIMENTAL_UDP__ - wxGetApp().m_events = m_ckbox_events->GetValue(); - long spam_timer; - m_txt_spam_timer->GetValue().ToLong(&spam_timer); - wxGetApp().m_events_spam_timer = (int)spam_timer; - - // make sure regexp lists are terminated by a \n - - if (m_txt_events_regexp_match->GetValue().Last() != '\n') { - m_txt_events_regexp_match->SetValue(m_txt_events_regexp_match->GetValue()+'\n'); - } - if (m_txt_events_regexp_replace->GetValue().Last() != '\n') { - m_txt_events_regexp_replace->SetValue(m_txt_events_regexp_replace->GetValue()+'\n'); - } - wxGetApp().m_events_regexp_match = m_txt_events_regexp_match->GetValue(); - wxGetApp().m_events_regexp_replace = m_txt_events_regexp_replace->GetValue(); - - wxGetApp().m_udp_enable = m_ckbox_udp_enable->GetValue(); - long port; - m_txt_udp_port->GetValue().ToLong(&port); - wxGetApp().m_udp_port = (int)port; - -#ifdef SHORT_VARICODE - if (m_rb_textEncoding1->GetValue()) - wxGetApp().m_textEncoding = 1; - if (m_rb_textEncoding2->GetValue()) - wxGetApp().m_textEncoding = 2; -#endif - wxGetApp().m_enable_checksum = m_ckboxEnableChecksum->GetValue(); -#endif - - wxGetApp().m_FreeDV700txClip = m_ckboxFreeDV700txClip->GetValue(); - wxGetApp().m_FreeDV700Combine = m_ckboxFreeDV700Combine->GetValue(); - -#ifdef __WXMSW__ - wxGetApp().m_debug_console = m_ckboxDebugConsole->GetValue(); -#endif - - if (storePersistent) { - pConfig->Write(wxT("/Data/CallSign"), wxGetApp().m_callSign); -#ifdef SHORT_VARICODE - pConfig->Write(wxT("/Data/TextEncoding"), wxGetApp().m_textEncoding); -#endif - pConfig->Write(wxT("/Data/EnableChecksumOnMsgRx"), wxGetApp().m_enable_checksum); - - pConfig->Write(wxT("/Events/enable"), wxGetApp().m_events); - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - pConfig->Write(wxT("/Events/regexp_match"), wxGetApp().m_events_regexp_match); - pConfig->Write(wxT("/Events/regexp_replace"), wxGetApp().m_events_regexp_replace); - - pConfig->Write(wxT("/UDP/enable"), wxGetApp().m_udp_enable); - pConfig->Write(wxT("/UDP/port"), wxGetApp().m_udp_port); - - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - - pConfig->Write(wxT("/FreeDV700/txClip"), wxGetApp().m_FreeDV700txClip); - - pConfig->Write(wxT("/Noise/noise_snr"), wxGetApp().m_noise_snr); - -#ifdef __WXMSW__ - pConfig->Write(wxT("/Debug/console"), wxGetApp().m_debug_console); -#endif - - pConfig->Flush(); - } - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -//------------------------------------------------------------------------- -// OnOK() -//------------------------------------------------------------------------- -void OptionsDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT, true); - //this->EndModal(wxID_OK); - g_modal = false; - this->Show(false); -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void OptionsDlg::OnCancel(wxCommandEvent& event) -{ - //this->EndModal(wxID_CANCEL); - g_modal = false; - this->Show(false); -} - -//------------------------------------------------------------------------- -// OnApply() -//------------------------------------------------------------------------- -void OptionsDlg::OnApply(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT, true); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void OptionsDlg::OnInitDialog(wxInitDialogEvent& event) -{ - ExchangeData(EXCHANGE_DATA_IN, false); -} - -// immediately change flags rather using ExchangeData() so we can switch on and off at run time - -void OptionsDlg::OnTestFrame(wxScrollEvent& event) { - wxGetApp().m_testFrames = m_ckboxTestFrame->GetValue(); -} - -void OptionsDlg::OnChannelNoise(wxScrollEvent& event) { - wxGetApp().m_channel_noise = m_ckboxChannelNoise->GetValue(); -} - - -void OptionsDlg::OnChooseVoiceKeyerWaveFile(wxCommandEvent& event) { - wxFileDialog openFileDialog( - this, - wxT("Voice Keyer wave file"), - wxGetApp().m_txtVoiceKeyerWaveFilePath, - wxEmptyString, - wxT("WAV files (*.wav)|*.wav"), - wxFD_OPEN - ); - if(openFileDialog.ShowModal() == wxID_CANCEL) { - return; // the user changed their mind... - } - - wxString fileName, extension; - wxGetApp().m_txtVoiceKeyerWaveFile = openFileDialog.GetPath(); - wxFileName::SplitPath(wxGetApp().m_txtVoiceKeyerWaveFile, &wxGetApp().m_txtVoiceKeyerWaveFilePath, &fileName, &extension); - m_txtCtrlVoiceKeyerWaveFile->SetValue(wxGetApp().m_txtVoiceKeyerWaveFile); -} - -// Run time update of carrier amplitude attenuation - -void OptionsDlg::OnAttnCarrierEn(wxScrollEvent& event) { - long attn_carrier; - m_txtAttnCarrier->GetValue().ToLong(&attn_carrier); - wxGetApp().m_attn_carrier = (int)attn_carrier; - - /* uncheck -> checked, attenuate selected carrier */ - - if (m_ckboxAttnCarrierEn->GetValue() && !wxGetApp().m_attn_carrier_en) { - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C) { - freedv_set_carrier_ampl(g_pfreedv, wxGetApp().m_attn_carrier, 0.25); - } else { - wxMessageBox("Carrier attenuation feature only works on 700C", wxT("Warning"), wxOK | wxICON_WARNING, this); - } - } - - /* checked -> unchecked, reset selected carrier */ - - if (!m_ckboxAttnCarrierEn->GetValue() && wxGetApp().m_attn_carrier_en) { - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C) { - freedv_set_carrier_ampl(g_pfreedv, wxGetApp().m_attn_carrier, 1.0); - } - } - - wxGetApp().m_attn_carrier_en = m_ckboxAttnCarrierEn->GetValue(); -} - -void OptionsDlg::OnFreeDV700txClip(wxScrollEvent& event) { - wxGetApp().m_FreeDV700txClip = m_ckboxFreeDV700txClip->GetValue(); -} - -void OptionsDlg::OnFreeDV700Combine(wxScrollEvent& event) { - wxGetApp().m_FreeDV700Combine = m_ckboxFreeDV700Combine->GetValue(); -} - -void OptionsDlg::updateEventLog(wxString event_in, wxString event_out) { - wxString event_in_with_serial, event_out_with_serial; - event_in_with_serial.Printf(_T("[%d] %s"), event_in_serial++, event_in); - event_out_with_serial.Printf(_T("[%d] %s"), event_out_serial++, event_out); - - m_txt_events_in->AppendText(event_in_with_serial+"\n"); - m_txt_events_out->AppendText(event_out_with_serial+"\n"); -} - - -void OptionsDlg::OnDebugConsole(wxScrollEvent& event) { - wxGetApp().m_debug_console = m_ckboxDebugConsole->GetValue(); -#ifdef __WXMSW__ - // somewhere to send printfs while developing, causes conmsole to pop up on Windows - if (wxGetApp().m_debug_console) { - int ret = AllocConsole(); - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - fprintf(stderr, "AllocConsole: %d m_debug_console: %d\n", ret, wxGetApp().m_debug_console); - } -#endif -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/dlg_options.h b/freedv/tags/1.2.2/freedv-dev/src/dlg_options.h deleted file mode 100644 index 081448cb..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/dlg_options.h +++ /dev/null @@ -1,137 +0,0 @@ -//========================================================================== -// Name: dlg_options.h -// Purpose: Dialog for controlling misc FreeDV options -// Created: Nov 25 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#ifndef __OPTIONS_DIALOG__ -#define __OPTIONS_DIALOG__ - -#include "fdmdv2_main.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class OptionsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class OptionsDlg : public wxDialog -{ - public: - OptionsDlg( wxWindow* parent, - wxWindowID id = wxID_ANY, const wxString& title = _("Options"), - const wxPoint& pos = wxDefaultPosition, -#ifdef __WXMSW__ - /* we add debug console check box for windows */ - const wxSize& size = wxSize(600,410), -#else - const wxSize& size = wxSize(600,380), -#endif - long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~OptionsDlg(); - - void ExchangeData(int inout, bool storePersistent); - void updateEventLog(wxString event_in, wxString event_out); - - bool enableEventsChecked() {return m_ckbox_events->GetValue();} - - void SetSpamTimerLight(bool state) { - - // Colours don't work on Windows - - if (state) { - m_rb_spam_timer->SetForegroundColour( wxColour( 255,0 , 0 ) ); // red - m_rb_spam_timer->SetValue(true); - } - else { - m_rb_spam_timer->SetForegroundColour( wxColour( 0, 255, 0 ) ); // green - m_rb_spam_timer->SetValue(false); - } - } - - - protected: - - // Handlers for events. - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnApply(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void OnInitDialog(wxInitDialogEvent& event); - - void OnTestFrame(wxScrollEvent& event); - void OnChannelNoise(wxScrollEvent& event); - void OnAttnCarrierEn(wxScrollEvent& event); - void OnFreeDV700txClip(wxScrollEvent& event); - void OnFreeDV700Combine(wxScrollEvent& event); - void OnDebugConsole(wxScrollEvent& event); - - wxTextCtrl *m_txtCtrlCallSign; // TODO: this should be renamed to tx_txtmsg, and rename all related incl persis strge - - wxCheckBox* m_ckHalfDuplex; - - /* Voice Keyer */ - - wxButton *m_buttonChooseVoiceKeyerWaveFile; - wxTextCtrl *m_txtCtrlVoiceKeyerWaveFile; - wxTextCtrl *m_txtCtrlVoiceKeyerRxPause; - wxTextCtrl *m_txtCtrlVoiceKeyerRepeats; - - /* test frames, other simulated channel impairments */ - - wxCheckBox *m_ckboxTestFrame; - wxCheckBox *m_ckboxChannelNoise; - wxTextCtrl *m_txtNoiseSNR; - wxCheckBox *m_ckboxAttnCarrierEn; - wxTextCtrl *m_txtAttnCarrier; - - wxCheckBox *m_ckboxTone; - wxTextCtrl *m_txtToneFreqHz; - wxTextCtrl *m_txtToneAmplitude; - - wxCheckBox *m_ckboxFreeDV700txClip; - wxCheckBox *m_ckboxFreeDV700Combine; - - wxRadioButton *m_rb_textEncoding1; - wxRadioButton *m_rb_textEncoding2; - wxCheckBox *m_ckboxEnableChecksum; - - wxCheckBox *m_ckbox_events; - wxTextCtrl *m_txt_events_regexp_match; - wxTextCtrl *m_txt_events_regexp_replace; - wxTextCtrl *m_txt_events_in; - wxTextCtrl *m_txt_events_out; - wxTextCtrl *m_txt_spam_timer; - wxRadioButton *m_rb_spam_timer; - - wxCheckBox *m_ckbox_udp_enable; - wxTextCtrl *m_txt_udp_port; - - wxButton* m_sdbSizer5OK; - wxButton* m_sdbSizer5Cancel; - wxButton* m_sdbSizer5Apply; - - wxCheckBox *m_ckboxDebugConsole; - - unsigned int event_in_serial, event_out_serial; - - void OnChooseVoiceKeyerWaveFile(wxCommandEvent& event); - - private: -}; - -#endif // __OPTIONS_DIALOG__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/dlg_plugin.cpp b/freedv/tags/1.2.2/freedv-dev/src/dlg_plugin.cpp deleted file mode 100644 index 68610f42..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/dlg_plugin.cpp +++ /dev/null @@ -1,148 +0,0 @@ -//========================================================================== -// Name: dlg_plugin.cpp -// Purpose: Subclasses dialog GUI for PlugIn Config. Creates simple -// wxWidgets dialog GUI to set a few text strings. -// Date: Jan 2016 -// Authors: David Rowe -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "dlg_plugin.h" -#include "fdmdv2_main.h" - -#ifdef __WIN32__ -#include -#endif -#if defined(__FreeBSD__) || defined(__WXOSX__) -#include -#include -#endif - -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlugInDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlugInDlg::PlugInDlg(const wxString& title, int numParam, wxString paramName[], wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - m_name = title; - m_numParam = numParam; - assert(m_numParam <= PLUGIN_MAX_PARAMS); - - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - this->SetSizer(mainSizer); - - int i; - for (i=0; iAdd(m_txtCtrlParam[i], 0, 0, 5); - mainSizer->Add(staticBoxSizer28a, 0, wxEXPAND, 5); - } - - //---------------------------------------------------------------------- - // OK - Cancel - Apply - //---------------------------------------------------------------------- - - wxBoxSizer* boxSizer12 = new wxBoxSizer(wxHORIZONTAL); - - m_buttonOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxSize(-1,-1), 0); - m_buttonOK->SetDefault(); - boxSizer12->Add(m_buttonOK, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonCancel, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - mainSizer->Add(boxSizer12, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL, 5); - - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - Centre(wxBOTH); - - // Connect events - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(PlugInDlg::OnInitDialog), NULL, this); - m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnOK), NULL, this); - m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnCancel), NULL, this); - -} - -//------------------------------------------------------------------------- -// ~PlugInDlg() -//------------------------------------------------------------------------- -PlugInDlg::~PlugInDlg() -{ - // Disconnect Events - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(PlugInDlg::OnInitDialog), NULL, this); - m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnOK), NULL, this); - m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnCancel), NULL, this); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void PlugInDlg::OnInitDialog(wxInitDialogEvent& event) -{ - ExchangeData(EXCHANGE_DATA_IN); -} - - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void PlugInDlg::ExchangeData(int inout) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - wxString str; - int i; - - if(inout == EXCHANGE_DATA_IN) - { - for (i=0; iSetValue(wxGetApp().m_txtPlugInParam[i]); - } - } - if(inout == EXCHANGE_DATA_OUT) - { - for (i=0; iGetValue(); - wxString configStr = "/" + m_name + "/" + m_paramName[i]; - pConfig->Write(configStr, wxGetApp().m_txtPlugInParam[i]); - } - pConfig->Flush(); - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void PlugInDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void PlugInDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - this->EndModal(wxID_OK); -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/dlg_plugin.h b/freedv/tags/1.2.2/freedv-dev/src/dlg_plugin.h deleted file mode 100644 index 72efc7bb..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/dlg_plugin.h +++ /dev/null @@ -1,65 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.h -// Purpose: Subclasses dialog GUI for PTT Config. -// -// Created: May. 11, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __PLUGIN_DIALOG__ -#define __PLUGIN_DIALOG__ - -#include "fdmdv2_main.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlugInDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlugInDlg : public wxDialog -{ - public: - PlugInDlg(const wxString& title = _("PTT Config"), int numParam = 0, wxString paramNames[]=NULL, wxWindow* parent=NULL, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(450,300), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); - virtual ~PlugInDlg(); - void ExchangeData(int inout); - - protected: - wxString m_name; - int m_numParam; - wxString m_paramName[PLUGIN_MAX_PARAMS]; - - wxTextCtrl* m_txtCtrlParam[PLUGIN_MAX_PARAMS]; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - - -protected: - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - virtual void OnInitDialog(wxInitDialogEvent& event); -}; - -#endif // __PLUGIN_DIALOG__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.cpp b/freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.cpp deleted file mode 100644 index 1a04c9c4..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.cpp +++ /dev/null @@ -1,570 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.cpp -// Purpose: Subclasses dialog GUI for PTT Config. Creates simple -// wxWidgets dialog GUI to select real/virtual Comm ports. -// Date: May 11 2012 -// Authors: David Rowe, David Witten, Joel Stanley -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "dlg_ptt.h" -#include "fdmdv2_main.h" - -#ifdef __WIN32__ -#include -#endif -#if defined(__FreeBSD__) || defined(__WXOSX__) -#include -#include -#endif - -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class ComPortsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -ComPortsDlg::ComPortsDlg(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - this->SetSizer(mainSizer); - - //---------------------------------------------------------------------- - // Vox tone option - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer28 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("VOX PTT Settings")), wxHORIZONTAL); - m_ckLeftChannelVoxTone = new wxCheckBox(this, wxID_ANY, _("Left Channel Vox Tone"), wxDefaultPosition, wxSize(-1,-1), 0); - staticBoxSizer28->Add(m_ckLeftChannelVoxTone, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5); - - mainSizer->Add(staticBoxSizer28, 0, wxEXPAND, 5); - - //---------------------------------------------------------------------- - // Hamlib for CAT PTT - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer18 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Hamlib Settings")), wxHORIZONTAL); - wxGridSizer* gridSizerhl = new wxGridSizer(5, 2, 0, 0); - staticBoxSizer18->Add(gridSizerhl, 1, wxEXPAND|wxALIGN_LEFT, 5); - - /* Use Hamlib for PTT checkbox. */ - - m_ckUseHamlibPTT = new wxCheckBox(this, wxID_ANY, _("Use Hamlib PTT"), wxDefaultPosition, wxSize(-1, -1), 0); - m_ckUseHamlibPTT->SetValue(false); - gridSizerhl->Add(m_ckUseHamlibPTT, 0, wxALIGN_CENTER_VERTICAL, 0); - gridSizerhl->Add(new wxStaticText(this, -1, wxT("")), 0, wxEXPAND); - - /* Hamlib Rig Type combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Rig Model:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbRigName = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1), 0, NULL, wxCB_DROPDOWN); - wxGetApp().m_hamlib->populateComboBox(m_cbRigName); - m_cbRigName->SetSelection(wxGetApp().m_intHamlibRig); - gridSizerhl->Add(m_cbRigName, 0, wxALIGN_CENTER_VERTICAL, 0); - - /* Hamlib Serial Port combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Device:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialPort = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizerhl->Add(m_cbSerialPort, 0, wxALIGN_CENTER_VERTICAL, 0); - - /* Hamlib Serial Rate combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Rate:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialRate = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizerhl->Add(m_cbSerialRate, 0, wxALIGN_CENTER_VERTICAL, 0); - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Params:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialParams = new wxStaticText(this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, 0); - gridSizerhl->Add(m_cbSerialParams, 0, wxALIGN_CENTER_VERTICAL, 0); - - mainSizer->Add(staticBoxSizer18, 0, wxEXPAND, 5); - - //---------------------------------------------------------------------- - // Serial port PTT - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer17 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Serial Port Settings")), wxVERTICAL); - mainSizer->Add(staticBoxSizer17, 1, wxEXPAND, 5); - wxStaticBoxSizer* staticBoxSizer31 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("PTT Port")), wxVERTICAL); - staticBoxSizer17->Add(staticBoxSizer31, 1, wxEXPAND, 5); - -#ifdef __WXMSW__ - m_ckUseSerialPTT = new wxCheckBox(this, wxID_ANY, _("Use Serial Port PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckUseSerialPTT->SetValue(false); - staticBoxSizer31->Add(m_ckUseSerialPTT, 0, wxALIGN_LEFT, 20); - - wxArrayString m_listCtrlPortsArr; - m_listCtrlPorts = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1,45), m_listCtrlPortsArr, wxLB_SINGLE | wxLB_SORT); - staticBoxSizer31->Add(m_listCtrlPorts, 1, wxALIGN_CENTER, 0); -#endif - -#if defined(__WXOSX__) || defined(__WXGTK__) - wxBoxSizer* bSizer83; - bSizer83 = new wxBoxSizer(wxHORIZONTAL); - - wxGridSizer* gridSizer200 = new wxGridSizer(1, 3, 0, 0); - - m_ckUseSerialPTT = new wxCheckBox(this, wxID_ANY, _("Use Serial Port PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckUseSerialPTT->SetValue(false); - gridSizer200->Add(m_ckUseSerialPTT, 1, wxALIGN_CENTER|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 2); - - m_staticText12 = new wxStaticText(this, wxID_ANY, _("Serial Device: "), wxDefaultPosition, wxDefaultSize, 0); - m_staticText12->Wrap(-1); - gridSizer200->Add(m_staticText12, 1,wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 2); - - m_cbCtlDevicePath = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizer200->Add(m_cbCtlDevicePath, 1, wxEXPAND|wxALIGN_CENTER|wxALIGN_RIGHT, 2); - - bSizer83->Add(gridSizer200, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 2); - staticBoxSizer31->Add(bSizer83, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - wxBoxSizer* boxSizer19 = new wxBoxSizer(wxVERTICAL); - staticBoxSizer17->Add(boxSizer19, 1, wxEXPAND, 5); - wxStaticBoxSizer* staticBoxSizer16 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Signal polarity")), wxHORIZONTAL); - boxSizer19->Add(staticBoxSizer16, 1, wxEXPAND|wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - wxGridSizer* gridSizer17 = new wxGridSizer(2, 2, 0, 0); - staticBoxSizer16->Add(gridSizer17, 1, wxEXPAND|wxALIGN_RIGHT, 5); - - m_rbUseDTR = new wxRadioButton(this, wxID_ANY, _("Use DTR"), wxDefaultPosition, wxSize(-1,-1), 0); - m_rbUseDTR->SetToolTip(_("Toggle DTR line for PTT")); - m_rbUseDTR->SetValue(1); - gridSizer17->Add(m_rbUseDTR, 0, wxALIGN_CENTER|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5); - - m_ckDTRPos = new wxCheckBox(this, wxID_ANY, _("DTR = +V"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckDTRPos->SetToolTip(_("Set Polarity of the DTR line")); - m_ckDTRPos->SetValue(false); - gridSizer17->Add(m_ckDTRPos, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - m_rbUseRTS = new wxRadioButton(this, wxID_ANY, _("Use RTS"), wxDefaultPosition, wxSize(-1,-1), 0); - m_rbUseRTS->SetToolTip(_("Toggle the RTS pin for PTT")); - m_rbUseRTS->SetValue(1); - gridSizer17->Add(m_rbUseRTS, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - m_ckRTSPos = new wxCheckBox(this, wxID_ANY, _("RTS = +V"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckRTSPos->SetValue(false); - m_ckRTSPos->SetToolTip(_("Set Polarity of the RTS line")); - gridSizer17->Add(m_ckRTSPos, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - //---------------------------------------------------------------------- - // OK - Cancel - Apply - //---------------------------------------------------------------------- - - wxBoxSizer* boxSizer12 = new wxBoxSizer(wxHORIZONTAL); - - m_buttonTest = new wxButton(this, wxID_APPLY, _("Test PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonTest, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxSize(-1,-1), 0); - m_buttonOK->SetDefault(); - boxSizer12->Add(m_buttonOK, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonCancel, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonApply = new wxButton(this, wxID_APPLY, _("Apply"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonApply, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - mainSizer->Add(boxSizer12, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL, 5); - - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - Centre(wxBOTH); - - // Connect events - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(ComPortsDlg::OnInitDialog), NULL, this); - m_ckUseHamlibPTT->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseHamLibClicked), NULL, this); - m_ckUseSerialPTT->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseSerialClicked), NULL, this); - m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnOK), NULL, this); - m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnCancel), NULL, this); - m_buttonApply->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnApply), NULL, this); - m_buttonTest->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnTest), NULL, this); -} - -//------------------------------------------------------------------------- -// ~ComPortsDlg() -//------------------------------------------------------------------------- -ComPortsDlg::~ComPortsDlg() -{ - // Disconnect Events - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(ComPortsDlg::OnInitDialog), NULL, this); - m_ckUseHamlibPTT->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseHamLibClicked), NULL, this); - m_ckUseSerialPTT->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseSerialClicked), NULL, this); - m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnOK), NULL, this); - m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnCancel), NULL, this); - m_buttonApply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnApply), NULL, this); - m_buttonTest->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnTest), NULL, this); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void ComPortsDlg::OnInitDialog(wxInitDialogEvent& event) -{ - populatePortList(); - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// populatePortList() -//------------------------------------------------------------------------- -void ComPortsDlg::populatePortList() -{ - - /* populate Hamlib serial rate combo box */ - - wxString serialRates[] = {"default", "300", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"}; - for(int i=0; iAppend(serialRates[i]); - } - -#ifdef __WXMSW__ - m_listCtrlPorts->Clear(); - m_cbSerialPort->Clear(); - wxArrayString aStr; - wxRegKey key(wxRegKey::HKLM, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM")); - if(!key.Exists()) - { - return; - } - else - { - // Get the number of subkeys and enumerate them. - if(!key.Open(wxRegKey::Read)) - { - return; - } - size_t subkeys; - size_t values; - if(!key.GetKeyInfo(&subkeys, NULL, &values, NULL)) - { - return; - } - if(!key.HasValues()) - { - return; - } - wxString key_name; - long el = 1; - key.GetFirstValue(key_name, el); - wxString valType; - wxString key_data; - for(unsigned int i = 0; i < values; i++) - { - key.QueryValue(key_name, key_data); - //wxPrintf("Value: %s Data: %s\n", key_name, key_data); - aStr.Add(key_data, 1); - key.GetNextValue(key_name, el); - } - } - m_listCtrlPorts->Append(aStr); - m_cbSerialPort->Append(aStr); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - m_cbSerialPort->Clear(); - m_cbCtlDevicePath->Clear(); -#if defined(__FreeBSD__) || defined(__WXOSX__) - glob_t gl; -#ifdef __FreeBSD__ - if(glob("/dev/tty*", GLOB_MARK, NULL, &gl)==0) { -#else - if(glob("/dev/tty.*", GLOB_MARK, NULL, &gl)==0) { -#endif - for(unsigned int i=0; i= 'l' && gl.gl_pathv[i][8] <= 's') - continue; - if(gl.gl_pathv[i][8] >= 'L' && gl.gl_pathv[i][8] <= 'S') - continue; - - /* Exclude virtual TTYs */ - if(gl.gl_pathv[i][8] == 'v') - continue; - - /* Exclude initial-state and lock-state devices */ -#ifndef __WXOSX__ - if(strchr(gl.gl_pathv[i], '.') != NULL) - continue; -#endif - - m_cbSerialPort->Append(gl.gl_pathv[i]); - m_cbCtlDevicePath->Append(gl.gl_pathv[i]); - } - globfree(&gl); - } -#else - /* TODO(Joel): http://stackoverflow.com/questions/2530096/how-to-find-all-serial-devices-ttys-ttyusb-on-linux-without-opening-them */ - m_cbSerialPort->Append("/dev/ttyUSB0"); - m_cbSerialPort->Append("/dev/ttyUSB1"); - m_cbSerialPort->Append("/dev/ttyS0"); - m_cbSerialPort->Append("/dev/ttyS1"); - - m_cbCtlDevicePath->Clear(); - m_cbCtlDevicePath->Append("/dev/ttyUSB0"); - m_cbCtlDevicePath->Append("/dev/ttyUSB1"); - m_cbCtlDevicePath->Append("/dev/ttyS0"); - m_cbCtlDevicePath->Append("/dev/ttyS1"); -#endif -#endif - - -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void ComPortsDlg::ExchangeData(int inout) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - wxString str; - - if(inout == EXCHANGE_DATA_IN) { - m_ckLeftChannelVoxTone->SetValue(wxGetApp().m_leftChannelVoxTone); - - /* Hamlib */ - - m_ckUseHamlibPTT->SetValue(wxGetApp().m_boolHamlibUseForPTT); - m_cbRigName->SetSelection(wxGetApp().m_intHamlibRig); - m_cbSerialPort->SetValue(wxGetApp().m_strHamlibSerialPort); - - if (wxGetApp().m_intHamlibSerialRate == 0) { - m_cbSerialRate->SetSelection(0); - } else { - m_cbSerialRate->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intHamlibSerialRate)); - } - - /* Serial PTT */ - - m_ckUseSerialPTT->SetValue(wxGetApp().m_boolUseSerialPTT); - str = wxGetApp().m_strRigCtrlPort; -#ifdef __WXMSW__ - m_listCtrlPorts->SetStringSelection(str); -#endif -#if defined(__WXOSX__) || defined(__WXGTK__) - m_cbCtlDevicePath->SetValue(str); -#endif - m_rbUseRTS->SetValue(wxGetApp().m_boolUseRTS); - m_ckRTSPos->SetValue(wxGetApp().m_boolRTSPos); - m_rbUseDTR->SetValue(wxGetApp().m_boolUseDTR); - m_ckDTRPos->SetValue(wxGetApp().m_boolDTRPos); - } - - if (inout == EXCHANGE_DATA_OUT) { - wxGetApp().m_leftChannelVoxTone = m_ckLeftChannelVoxTone->GetValue(); - pConfig->Write(wxT("/Rig/leftChannelVoxTone"), wxGetApp().m_leftChannelVoxTone); - - /* Hamlib settings. */ - - wxGetApp().m_boolHamlibUseForPTT = m_ckUseHamlibPTT->GetValue(); - wxGetApp().m_intHamlibRig = m_cbRigName->GetSelection(); - wxGetApp().m_strHamlibSerialPort = m_cbSerialPort->GetValue(); - - wxString s = m_cbSerialRate->GetValue(); - if (s == "default") { - wxGetApp().m_intHamlibSerialRate = 0; - } else { - long tmp; - m_cbSerialRate->GetValue().ToLong(&tmp); - wxGetApp().m_intHamlibSerialRate = tmp; - } - fprintf(stderr, "serial rate: %d\n", wxGetApp().m_intHamlibSerialRate); - - pConfig->Write(wxT("/Hamlib/UseForPTT"), wxGetApp().m_boolHamlibUseForPTT); - pConfig->Write(wxT("/Hamlib/RigName"), wxGetApp().m_intHamlibRig); - pConfig->Write(wxT("/Hamlib/SerialPort"), wxGetApp().m_strHamlibSerialPort); - pConfig->Write(wxT("/Hamlib/SerialRate"), wxGetApp().m_intHamlibSerialRate); - - /* Serial settings */ - - wxGetApp().m_boolUseSerialPTT = m_ckUseSerialPTT->IsChecked(); -#ifdef __WXMSW__ - wxGetApp().m_strRigCtrlPort = m_listCtrlPorts->GetStringSelection(); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - wxGetApp().m_strRigCtrlPort = m_cbCtlDevicePath->GetValue(); -#endif - wxGetApp().m_boolUseRTS = m_rbUseRTS->GetValue(); - wxGetApp().m_boolRTSPos = m_ckRTSPos->IsChecked(); - wxGetApp().m_boolUseDTR = m_rbUseDTR->GetValue(); - wxGetApp().m_boolDTRPos = m_ckDTRPos->IsChecked(); - - pConfig->Write(wxT("/Rig/UseSerialPTT"), wxGetApp().m_boolUseSerialPTT); - pConfig->Write(wxT("/Rig/Port"), wxGetApp().m_strRigCtrlPort); - pConfig->Write(wxT("/Rig/UseRTS"), wxGetApp().m_boolUseRTS); - pConfig->Write(wxT("/Rig/RTSPolarity"), wxGetApp().m_boolRTSPos); - pConfig->Write(wxT("/Rig/UseDTR"), wxGetApp().m_boolUseDTR); - pConfig->Write(wxT("/Rig/DTRPolarity"), wxGetApp().m_boolDTRPos); - - pConfig->Flush(); - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -//------------------------------------------------------------------------- -// PTTUseHamLibClicked() -//------------------------------------------------------------------------- -void ComPortsDlg::PTTUseHamLibClicked(wxCommandEvent& event) -{ - m_ckUseSerialPTT->SetValue(false); -} - - -/* Attempt to toggle PTT for 1 second */ - -void ComPortsDlg::OnTest(wxCommandEvent& event) { - - /* Tone PTT */ - - if (m_ckLeftChannelVoxTone->GetValue()) { - wxMessageBox("Testing of tone based PTT not supported; try PTT after pressing Start on main window", - wxT("Error"), wxOK | wxICON_ERROR, this); - } - - /* Hamlib PTT */ - - if (m_ckUseHamlibPTT->GetValue()) { - - // set up current hamlib config from GUI - - int rig = m_cbRigName->GetSelection(); - wxString port = m_cbSerialPort->GetValue(); - wxString s = m_cbSerialRate->GetValue(); - int serial_rate; - if (s == "default") { - serial_rate = 0; - } else { - long tmp; - m_cbSerialRate->GetValue().ToLong(&tmp); - serial_rate = tmp; - } - - // display serial params - - fprintf(stderr, "serial rate: %d\n", serial_rate); - - // try to open rig - - Hamlib *hamlib = wxGetApp().m_hamlib; - bool status = hamlib->connect(rig, port.mb_str(wxConvUTF8), serial_rate); - if (status == false) { - wxMessageBox("Couldn't connect to Radio with hamlib", wxT("Error"), wxOK | wxICON_ERROR, this); - return; - } - else { - wxString hamlib_serial_config; - hamlib_serial_config.sprintf(" %d, %d, %d", - hamlib->get_serial_rate(), - hamlib->get_data_bits(), - hamlib->get_stop_bits()); - m_cbSerialParams->SetLabel(hamlib_serial_config); - } - - // toggle PTT - - wxString hamlibError; - if (hamlib->ptt(true, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - return; - } - - wxSleep(1); - - if (hamlib->ptt(false, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - } - - /* Serial PTT */ - - if (m_ckUseSerialPTT->IsChecked()) { - Serialport *serialport = wxGetApp().m_serialport; - - wxString ctrlport; -#ifdef __WXMSW__ - ctrlport = m_listCtrlPorts->GetStringSelection(); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - ctrlport = m_cbCtlDevicePath->GetValue(); -#endif - fprintf(stderr, "opening serial port\n"); - - bool success = serialport->openport(ctrlport.c_str(), - m_rbUseRTS->GetValue(), - m_ckRTSPos->IsChecked(), - m_rbUseDTR->GetValue(), - m_ckDTRPos->IsChecked()); - - fprintf(stderr, "serial port open\n"); - - if (!success) { - wxMessageBox("Couldn't open serial port", wxT("Error"), wxOK | wxICON_ERROR, this); - } - - // assert PTT port for 1 sec - - serialport->ptt(true); - wxSleep(1); - serialport->ptt(false); - - fprintf(stderr, "closing serial port\n"); - serialport->closeport(); - fprintf(stderr, "serial port closed\n"); - } - -} - - -//------------------------------------------------------------------------- -// PTTUseSerialClicked() -//------------------------------------------------------------------------- -void ComPortsDlg::PTTUseSerialClicked(wxCommandEvent& event) -{ - m_ckUseHamlibPTT->SetValue(false); -} - -//------------------------------------------------------------------------- -// OnApply() -//------------------------------------------------------------------------- -void ComPortsDlg::OnApply(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void ComPortsDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void ComPortsDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - this->EndModal(wxID_OK); -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.h b/freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.h deleted file mode 100644 index 5a35c3bd..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/dlg_ptt.h +++ /dev/null @@ -1,95 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.h -// Purpose: Subclasses dialog GUI for PTT Config. -// -// Created: May. 11, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __COMPORTS_DIALOG__ -#define __COMPORTS_DIALOG__ - -#include "fdmdv2_main.h" -#include "hamlib.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class ComPortsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class ComPortsDlg : public wxDialog -{ - public: - ComPortsDlg(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("PTT Config"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(450,300), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); - virtual ~ComPortsDlg(); - void ExchangeData(int inout); - - protected: - wxCheckBox* m_ckLeftChannelVoxTone; - - /* Hamlib settings.*/ - - wxCheckBox *m_ckUseHamlibPTT; - wxComboBox *m_cbRigName; - wxComboBox *m_cbSerialPort; - wxComboBox *m_cbSerialRate; - wxStaticText *m_cbSerialParams; - Hamlib *m_hamlib; - - /* Serial Settings */ - - wxListBox *m_listCtrlPorts; - wxCheckBox *m_ckUseSerialPTT; - wxStaticText *m_staticText12; - wxComboBox *m_cbCtlDevicePath; - wxRadioButton *m_rbUseDTR; - wxCheckBox *m_ckRTSPos; - wxRadioButton *m_rbUseRTS; - wxCheckBox *m_ckDTRPos; - - /* Test - Ok - Cancel - Apply */ - - wxButton* m_buttonTest; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - wxButton* m_buttonApply; - - -protected: - void populatePortList(); - - void PTTUseHamLibClicked(wxCommandEvent& event); - void PTTUseSerialClicked(wxCommandEvent& event); - - void OnTest(wxCommandEvent& event); - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnApply(wxCommandEvent& event); - virtual void OnInitDialog(wxInitDialogEvent& event); - -}; - -#endif // __COMPORTS_DIALOG__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_defines.h b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_defines.h deleted file mode 100644 index a6878890..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_defines.h +++ /dev/null @@ -1,106 +0,0 @@ -//========================================================================== -// Name: fdmdv2_defines.h -// Purpose: Definitions used by plots derived from fdmdv2_plot class. -// Created: August 27, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_DEFINES__ -#define __FDMDV2_DEFINES__ - -#include "wx/wx.h" -#include "freedv_api.h" -#include "modem_stats.h" - -// Spectrogram and Waterfall - -#define MIN_MAG_DB -40.0 // min of spectrogram/waterfall magnitude axis -#define MAX_MAG_DB 0.0 // max of spectrogram/waterfall magnitude axis -#define STEP_MAG_DB 5.0 // magnitude axis step -#define BETA 0.95 // constant for time averaging spectrum data -#define MIN_F_HZ 0 // min freq on Waterfall and Spectrum -#define MAX_F_HZ 3000 // max freq on Waterfall and Spectrum -#define STEP_F_HZ 500 // major (e.g. text legend) freq step on Waterfall and Spectrum graticule -#define STEP_MINOR_F_HZ 100 // minor (ticks) freq step on Waterfall and Spectrum graticule -#define WATERFALL_SECS_Y 30 // number of seconds respresented by y axis of waterfall -#define WATERFALL_SECS_STEP 5 // graticule y axis steps of waterfall -#define DT 0.1 // time between real time graphing updates -#define FS 8000 // FDMDV modem sample rate - -// Scatter diagram - -#define SCATTER_MEM_SECS 10 -// (symbols/frame)/(graphics update period) = symbols/s sent to scatter memory -// memory (symbols) = secs of memory * symbols/sec -#define SCATTER_MEM_SYMS_MAX ((int)(SCATTER_MEM_SECS*((MODEM_STATS_NC_MAX+1)/DT))) -#define SCATTER_EYE_MEM_ROWS ((int)(SCATTER_MEM_SECS/DT)) - -// Waveform plotting constants - -#define WAVEFORM_PLOT_FS 400 // sample rate (points/s) of waveform plotted to screen -#define WAVEFORM_PLOT_TIME 5 // length or entire waveform on screen -#define WAVEFORM_PLOT_BUF ((int)(DT*WAVEFORM_PLOT_FS)) // number of new samples we plot per DT - -// sample rate I/O & conversion constants - -#define MAX_FPB 8096 // maximum value of portAudio framesPerBuffer -#define PA_FPB 1024 // nominal value of portAudio framesPerBuffer -#define SAMPLE_RATE 48000 // 48 kHz sampling rate rec. as we can trust accuracy of sound card -#define N8 160 // processing buffer size at 8 kHz -#define MEM8 (FDMDV_OS_TAPS/FDMDV_OS) -#define N48 (N8*SAMPLE_RATE/FS) // processing buffer size at 48 kHz -#define NUM_CHANNELS 2 // I think most sound cards prefer stereo we will convert to mono -#define VOX_TONE_FREQ 1000.0 // optional left channel vox tone freq -#define VOX_TONE_AMP 30000 // optional left channel vox tone amp - -#define MAX_BITS_PER_CODEC_FRAME 64 // 1600 bit/s mode -#define MAX_BYTES_PER_CODEC_FRAME (MAX_BITS_PER_CODEC_FRAME/8) -#define MAX_BITS_PER_FDMDV_FRAME 40 // 2000 bit/s mode - -// Squelch -#define SQ_DEFAULT_SNR 2.0 - -// Level Gauge -#define FROM_RADIO_MAX 0.8 -#define FROM_MIC_MAX 0.8 -#define LEVEL_BETA 0.99 - -// SNR -#define SNRSLOW_BETA 0.5 // time constant for slow SNR for display - -// Text messaging Data -#define MAX_CALLSIGN 80 -#define MAX_EVENT_LOG 10 -#define MAX_EVENT_RULES 100 - -enum -{ - ID_ROTATE_LEFT = wxID_HIGHEST + 1, - ID_ROTATE_RIGHT, - ID_RESIZE, - ID_PAINT_BG -}; - -// Codec 2 LPC Post Filter defaults, from codec-dev/src/quantise.c - -#define CODEC2_LPC_PF_GAMMA 0.5 -#define CODEC2_LPC_PF_BETA 0.2 - -// PlugIns ... - -#define PLUGIN_MAX_PARAMS 4 - -#endif //__FDMDV2_DEFINES__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_main.cpp b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_main.cpp deleted file mode 100644 index 506406c1..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_main.cpp +++ /dev/null @@ -1,4042 +0,0 @@ -//========================================================================== -// Name: fdmdv2_main.cpp -// -// Purpose: FreeDV main() -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "fdmdv2_main.h" - -#define wxUSE_FILEDLG 1 -#define wxUSE_LIBPNG 1 -#define wxUSE_LIBJPEG 1 -#define wxUSE_GIF 1 -#define wxUSE_PCX 1 -#define wxUSE_LIBTIFF 1 - -//------------------------------------------------------------------- -// Bunch of globals used for communication with sound card call -// back functions -// ------------------------------------------------------------------ - -int g_in, g_out; - -// Global Codec2 & modem states - just one reqd for tx & rx -int g_Nc; -int g_mode; -struct freedv *g_pfreedv; -struct MODEM_STATS g_stats; -float g_pwr_scale; -int g_clip; - -// test Frames -int g_testFrames; -int g_test_frame_sync_state; -int g_test_frame_count; -int g_channel_noise; -int g_resyncs; -float g_sig_pwr_av = 0.0; -struct FIFO *g_error_pattern_fifo; -short *g_error_hist, *g_error_histn; -float g_tone_phase; - -// time averaged magnitude spectrum used for waterfall and spectrum display -float g_avmag[MODEM_STATS_NSPEC]; - -// GUI controls that affect rx and tx processes -int g_SquelchActive; -float g_SquelchLevel; -int g_analog; -int g_split; -int g_tx; -float g_snr; -bool g_half_duplex; -bool g_modal; - -// sending and receiving Call Sign data -struct FIFO *g_txDataInFifo; -struct FIFO *g_rxDataOutFifo; - -// tx/rx processing states -int g_State, g_prev_State; -paCallBackData *g_rxUserdata; - -// FIFOs used for plotting waveforms -struct FIFO *g_plotDemodInFifo; -struct FIFO *g_plotSpeechOutFifo; -struct FIFO *g_plotSpeechInFifo; - -// Soundcard config -int g_nSoundCards; -int g_soundCard1InDeviceNum; -int g_soundCard1OutDeviceNum; -int g_soundCard1SampleRate; -int g_soundCard2InDeviceNum; -int g_soundCard2OutDeviceNum; -int g_soundCard2SampleRate; - -// playing and recording from sound files - -SNDFILE *g_sfPlayFile; -bool g_playFileToMicIn; -bool g_loopPlayFileToMicIn; -int g_playFileToMicInEventId; - -SNDFILE *g_sfRecFile; -bool g_recFileFromRadio; -unsigned int g_recFromRadioSamples; -int g_recFileFromRadioEventId; - -SNDFILE *g_sfPlayFileFromRadio; -bool g_playFileFromRadio; -int g_sfFs; -bool g_loopPlayFileFromRadio; -int g_playFileFromRadioEventId; -float g_blink; - -wxWindow *g_parent; - -// Click to tune rx and tx frequency offset states -float g_RxFreqOffsetHz; -COMP g_RxFreqOffsetPhaseRect; -float g_TxFreqOffsetHz; -COMP g_TxFreqOffsetPhaseRect; - -// experimental mutex to make sound card callbacks mutually exclusive -// TODO: review code and see if we need this any more, as fifos should -// now be thread safe - -wxMutex g_mutexProtectingCallbackData; - -// Speex pre-processor states - -SpeexPreprocessState *g_speex_st; - -// WxWidgets - initialize the application -IMPLEMENT_APP(MainApp); - -//FILE *ftest; -FILE *g_logfile; - -//------------------------------------------------------------------------- -// OnInit() -//------------------------------------------------------------------------- -bool MainApp::OnInit() -{ - if(!wxApp::OnInit()) - { - return false; - } - SetVendorName(wxT("CODEC2-Project")); - SetAppName(wxT("FreeDV")); // not needed, it's the default value - -#ifdef FILE_RATHER_THAN_REGISTRY - // Force use of file-based configuration persistance on Windows platforma - wxConfig *pConfig = new wxConfig(); - wxFileConfig *pFConfig = new wxFileConfig(wxT("FreeDV"), wxT("CODEC2-Project"), wxT("FreeDV.conf"), wxT("FreeDV.conf"), wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH); - pConfig->Set(pFConfig); - pConfig->SetRecordDefaults(); -#else - wxConfigBase *pConfig = wxConfigBase::Get(); - pConfig->SetRecordDefaults(); -#endif - - m_rTopWindow = wxRect(0, 0, 0, 0); - m_strRxInAudio.Empty(); - m_strRxOutAudio.Empty(); - m_textVoiceInput.Empty(); - m_textVoiceOutput.Empty(); - m_strSampleRate.Empty(); - m_strBitrate.Empty(); - - // Look for Plug In - - m_plugIn = false; - #ifdef __WXMSW__ - wchar_t dll_path[] = L"afreedvplugin.dll"; - m_plugInHandle = LoadLibrary(dll_path); - #else - m_plugInHandle = dlopen("afreedvplugin.so", RTLD_LAZY); - #endif - - if (m_plugInHandle) { - printf("plugin: .so found\n"); - - // lets get some information abt the plugIn - - void (*plugin_namefp)(char s[]); - void *(*plugin_openfp)(char *param_names[], int *nparams, int (*aplugin_get_persistant)(char *, char *)); - - #ifdef __WXMSW__ - plugin_namefp = (void (*)(char*))GetProcAddress((HMODULE)m_plugInHandle, "plugin_name"); - plugin_openfp = (void* (*)(char**,int *, int (*)(char *, char *)))GetProcAddress((HMODULE)m_plugInHandle, "plugin_open"); - m_plugin_startfp = (void (*)(void *))GetProcAddress((HMODULE)m_plugInHandle, "plugin_start"); - m_plugin_stopfp = (void (*)(void *))GetProcAddress((HMODULE)m_plugInHandle, "plugin_stop"); - m_plugin_rx_samplesfp = (void (*)(void *, short *, int))GetProcAddress((HMODULE)m_plugInHandle, "plugin_rx_samples"); - #else - plugin_namefp = (void (*)(char*))dlsym(m_plugInHandle, "plugin_name"); - plugin_openfp = (void* (*)(char**,int *, int (*)(char *, char *)))dlsym(m_plugInHandle, "plugin_open"); - m_plugin_startfp = (void (*)(void *))dlsym(m_plugInHandle, "plugin_start"); - m_plugin_stopfp = (void (*)(void *))dlsym(m_plugInHandle, "plugin_stop"); - m_plugin_rx_samplesfp = (void (*)(void *, short *, int))dlsym(m_plugInHandle, "plugin_rx_samples"); - #endif - - if ((plugin_namefp != NULL) && (plugin_openfp != NULL)) { - - char s[256]; - m_plugIn = true; - (plugin_namefp)(s); - fprintf(stderr, "plugin name: %s\n", s); - m_plugInName = s; - - char param_name1[80], param_name2[80]; - char *param_names[2] = {param_name1, param_name2}; - int nparams, i; - m_plugInStates = (plugin_openfp)(param_names, &nparams, plugin_get_persistant); - m_numPlugInParam = nparams; - for(i=0; iRead(configStr, wxT("")); - //fprintf(stderr, " plugin param name[%d]: %s\n", i, param_names[i]); - fprintf(stderr, " plugin param name[%d]: %s values: %s\n", i, m_plugInParamName[i].mb_str().data(), m_txtPlugInParam[i].mb_str().data()); - } - } - - else { - fprintf(stderr, "plugin: fps not found...\n"); - } - } - else { - fprintf(stderr, "plugin not found...\n"); - } - - // Create the main application window - - frame = new MainFrame(m_plugInName, NULL); - SetTopWindow(frame); - - // Should guarantee that the first plot tab defined is the one - // displayed. But it doesn't when built from command line. Why? - - frame->m_auiNbookCtrl->ChangeSelection(0); - frame->Layout(); - frame->Show(); - g_parent =frame; - - - return true; -} - -//------------------------------------------------------------------------- -// OnExit() -//------------------------------------------------------------------------- -int MainApp::OnExit() -{ - fprintf(stderr, "MainApp::OnExit\n"); - if (m_plugIn) { - #ifdef __WXMSW__ - FreeLibrary((HMODULE)m_plugInHandle); - #else - dlclose(m_plugInHandle); - #endif - } - - return 0; -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainFrame(wxFrame* pa->ent) : TopFrame(parent) -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -MainFrame::MainFrame(wxString plugInName, wxWindow *parent) : TopFrame(plugInName, parent) -{ - m_zoom = 1.; - - #ifdef __WXMSW__ - g_logfile = stderr; - #else - g_logfile = stderr; - #endif - - - SetMinSize(wxSize(400,400)); - - // Init Hamlib library, but we dont start talking to any rigs yet - - wxGetApp().m_hamlib = new Hamlib(); - - // Init Serialport library, but as for Hamlib we dont start talking to any rigs yet - - wxGetApp().m_serialport = new Serialport(); - - tools->AppendSeparator(); - wxMenuItem* m_menuItemToolsConfigDelete; - m_menuItemToolsConfigDelete = new wxMenuItem(tools, wxID_ANY, wxString(_("&Restore defaults")) , wxT("Delete config file/keys and restore defaults"), wxITEM_NORMAL); - this->Connect(m_menuItemToolsConfigDelete->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::OnDeleteConfig)); - - tools->Append(m_menuItemToolsConfigDelete); - - wxConfigBase *pConfig = wxConfigBase::Get(); - - // restore frame position and size - int x = pConfig->Read(wxT("/MainFrame/left"), 20); - int y = pConfig->Read(wxT("/MainFrame/top"), 20); - int w = pConfig->Read(wxT("/MainFrame/width"), 800); - int h = pConfig->Read(wxT("/MainFrame/height"), 550); - - // sanitise frame position as a first pass at Win32 registry bug - - fprintf(g_logfile, "x = %d y = %d w = %d h = %d\n", x,y,w,h); - if (x < 0 || x > 2048) x = 20; - if (y < 0 || y > 2048) y = 20; - if (w < 0 || w > 2048) w = 800; - if (h < 0 || h > 2048) h = 550; - - wxGetApp().m_show_wf = pConfig->Read(wxT("/MainFrame/show_wf"), 1); - wxGetApp().m_show_spect = pConfig->Read(wxT("/MainFrame/show_spect"), 1); - wxGetApp().m_show_scatter = pConfig->Read(wxT("/MainFrame/show_scatter"), 1); - wxGetApp().m_show_timing = pConfig->Read(wxT("/MainFrame/show_timing"), 1); - wxGetApp().m_show_freq = pConfig->Read(wxT("/MainFrame/show_freq"), 1); - wxGetApp().m_show_speech_in = pConfig->Read(wxT("/MainFrame/show_speech_in"), 1); - wxGetApp().m_show_speech_out = pConfig->Read(wxT("/MainFrame/show_speech_out"), 1); - wxGetApp().m_show_demod_in = pConfig->Read(wxT("/MainFrame/show_demod_in"), 1); - wxGetApp().m_show_test_frame_errors = pConfig->Read(wxT("/MainFrame/show_test_frame_errors"), 1); - wxGetApp().m_show_test_frame_errors_hist = pConfig->Read(wxT("/MainFrame/show_test_frame_errors_hist"), 1); - - wxGetApp().m_rxNbookCtrl = pConfig->Read(wxT("/MainFrame/rxNbookCtrl"), (long)0); - - g_SquelchActive = pConfig->Read(wxT("/Audio/SquelchActive"), (long)0); - g_SquelchLevel = pConfig->Read(wxT("/Audio/SquelchLevel"), (int)(SQ_DEFAULT_SNR*2)); - g_SquelchLevel /= 2.0; - - Move(x, y); - SetClientSize(w, h); - - if(wxGetApp().m_show_wf) - { - // Add Waterfall Plot window - m_panelWaterfall = new PlotWaterfall((wxFrame*) m_auiNbookCtrl, false, 0); - m_panelWaterfall->SetToolTip(_("Left click to tune, Right click to toggle mono/colour")); - m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Waterfall"), true, wxNullBitmap); - } - if(wxGetApp().m_show_spect) - { - // Add Spectrum Plot window - m_panelSpectrum = new PlotSpectrum((wxFrame*) m_auiNbookCtrl, g_avmag, - MODEM_STATS_NSPEC*((float)MAX_F_HZ/MODEM_STATS_MAX_F_HZ)); - m_panelSpectrum->SetToolTip(_("Left click to tune")); - m_auiNbookCtrl->AddPage(m_panelSpectrum, _("Spectrum"), true, wxNullBitmap); - } - if(wxGetApp().m_show_scatter) - { - // Add Scatter Plot window - m_panelScatter = new PlotScatter((wxFrame*) m_auiNbookCtrl); - m_auiNbookCtrl->AddPage(m_panelScatter, _("Scatter"), true, wxNullBitmap); - } - if(wxGetApp().m_show_demod_in) - { - // Add Demod Input window - m_panelDemodIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelDemodIn, _("Frm Radio"), true, wxNullBitmap); - g_plotDemodInFifo = fifo_create(2*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_speech_in) - { - // Add Speech Input window - m_panelSpeechIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechIn, _("Frm Mic"), true, wxNullBitmap); - g_plotSpeechInFifo = fifo_create(4*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_speech_out) - { - // Add Speech Output window - m_panelSpeechOut = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechOut, _("To Spkr/Hdphns"), true, wxNullBitmap); - g_plotSpeechOutFifo = fifo_create(2*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_timing) - { - // Add Timing Offset window - m_panelTimeOffset = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 5.0, DT, -0.5, 0.5, 1, 0.1, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelTimeOffset, L"Timing \u0394", true, wxNullBitmap); - } - if(wxGetApp().m_show_freq) - { - // Add Frequency Offset window - m_panelFreqOffset = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 5.0, DT, -200, 200, 1, 50, "%3.0fHz", 0); - m_auiNbookCtrl->AddPage(m_panelFreqOffset, L"Frequency \u0394", true, wxNullBitmap); - } - - if(wxGetApp().m_show_test_frame_errors) - { - // Add Test Frame Errors window - m_panelTestFrameErrors = new PlotScalar((wxFrame*) m_auiNbookCtrl, 2*MODEM_STATS_NC_MAX, 30.0, DT, 0, 2*MODEM_STATS_NC_MAX+2, 1, 1, "", 1); - m_auiNbookCtrl->AddPage(m_panelTestFrameErrors, L"Test Frame Errors", true, wxNullBitmap); - } - - if(wxGetApp().m_show_test_frame_errors_hist) - { - // Add Test Frame Historgram window. 1 column for every bit, 2 bits per carrier - m_panelTestFrameErrorsHist = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 1.0, 1.0/(2*FDMDV_NC_MAX), 0.001, 0.1, 1.0/FDMDV_NC_MAX, 0.1, "%0.0E", 0); - m_auiNbookCtrl->AddPage(m_panelTestFrameErrorsHist, L"Test Frame Histogram", true, wxNullBitmap); - m_panelTestFrameErrorsHist->setBarGraph(1); - m_panelTestFrameErrorsHist->setLogY(1); - } - - wxGetApp().m_framesPerBuffer = pConfig->Read(wxT("/Audio/framesPerBuffer"), PA_FPB); - - g_soundCard1InDeviceNum = pConfig->Read(wxT("/Audio/soundCard1InDeviceNum"), -1); - g_soundCard1OutDeviceNum = pConfig->Read(wxT("/Audio/soundCard1OutDeviceNum"), -1); - g_soundCard1SampleRate = pConfig->Read(wxT("/Audio/soundCard1SampleRate"), -1); - - g_soundCard2InDeviceNum = pConfig->Read(wxT("/Audio/soundCard2InDeviceNum"), -1); - g_soundCard2OutDeviceNum = pConfig->Read(wxT("/Audio/soundCard2OutDeviceNum"), -1); - g_soundCard2SampleRate = pConfig->Read(wxT("/Audio/soundCard2SampleRate"), -1); - - g_nSoundCards = 0; - if ((g_soundCard1InDeviceNum > -1) && (g_soundCard1OutDeviceNum > -1)) { - g_nSoundCards = 1; - if ((g_soundCard2InDeviceNum > -1) && (g_soundCard2OutDeviceNum > -1)) - g_nSoundCards = 2; - } - - wxGetApp().m_playFileToMicInPath = pConfig->Read("/File/playFileToMicInPath", wxT("")); - wxGetApp().m_recFileFromRadioPath = pConfig->Read("/File/recFileFromRadioPath", wxT("")); - wxGetApp().m_recFileFromRadioSecs = pConfig->Read("/File/recFileFromRadioSecs", 30); - wxGetApp().m_playFileFromRadioPath = pConfig->Read("/File/playFileFromRadioPath", wxT("")); - - // PTT ------------------------------------------------------------------- - - wxGetApp().m_boolHalfDuplex = pConfig->ReadBool(wxT("/Rig/HalfDuplex"), true); - wxGetApp().m_leftChannelVoxTone = pConfig->ReadBool("/Rig/leftChannelVoxTone", false); - - wxGetApp().m_txtVoiceKeyerWaveFilePath = pConfig->Read(wxT("/VoiceKeyer/WaveFilePath"), wxT("")); - wxGetApp().m_txtVoiceKeyerWaveFile = pConfig->Read(wxT("/VoiceKeyer/WaveFile"), wxT("voicekeyer.wav")); - wxGetApp().m_intVoiceKeyerRxPause = pConfig->Read(wxT("/VoiceKeyer/RxPause"), 10); - wxGetApp().m_intVoiceKeyerRepeats = pConfig->Read(wxT("/VoiceKeyer/Repeats"), 5); - - wxGetApp().m_boolHamlibUseForPTT = pConfig->ReadBool("/Hamlib/UseForPTT", false); - wxGetApp().m_intHamlibRig = pConfig->ReadLong("/Hamlib/RigName", 0); - wxGetApp().m_strHamlibSerialPort = pConfig->Read("/Hamlib/SerialPort", ""); - wxGetApp().m_intHamlibSerialRate = pConfig->ReadLong("/Hamlib/SerialRate", 0); - - wxGetApp().m_boolUseSerialPTT = pConfig->ReadBool(wxT("/Rig/UseSerialPTT"), false); - wxGetApp().m_strRigCtrlPort = pConfig->Read(wxT("/Rig/Port"), wxT("")); - wxGetApp().m_boolUseRTS = pConfig->ReadBool(wxT("/Rig/UseRTS"), true); - wxGetApp().m_boolRTSPos = pConfig->ReadBool(wxT("/Rig/RTSPolarity"), true); - wxGetApp().m_boolUseDTR = pConfig->ReadBool(wxT("/Rig/UseDTR"), false); - wxGetApp().m_boolDTRPos = pConfig->ReadBool(wxT("/Rig/DTRPolarity"), false); - - assert(wxGetApp().m_serialport != NULL); - - // ----------------------------------------------------------------------- - - bool slow = false; // prevents compile error when using default bool - wxGetApp().m_snrSlow = pConfig->Read("/Audio/snrSlow", slow); - - bool t = true; // prevents compile error when using default bool - wxGetApp().m_codec2LPCPostFilterEnable = pConfig->Read(wxT("/Filter/codec2LPCPostFilterEnable"), t); - wxGetApp().m_codec2LPCPostFilterBassBoost = pConfig->Read(wxT("/Filter/codec2LPCPostFilterBassBoost"), t); - wxGetApp().m_codec2LPCPostFilterGamma = (float)pConfig->Read(wxT("/Filter/codec2LPCPostFilterGamma"), CODEC2_LPC_PF_GAMMA*100)/100.0; - wxGetApp().m_codec2LPCPostFilterBeta = (float)pConfig->Read(wxT("/Filter/codec2LPCPostFilterBeta"), CODEC2_LPC_PF_BETA*100)/100.0; - //printf("main(): m_codec2LPCPostFilterBeta: %f\n", wxGetApp().m_codec2LPCPostFilterBeta); - - wxGetApp().m_speexpp_enable = pConfig->Read(wxT("/Filter/speexpp_enable"), t); - - wxGetApp().m_MicInBassFreqHz = (float)pConfig->Read(wxT("/Filter/MicInBassFreqHz"), 1); - wxGetApp().m_MicInBassGaindB = (float)pConfig->Read(wxT("/Filter/MicInBassGaindB"), (long)0)/10.0; - wxGetApp().m_MicInTrebleFreqHz = (float)pConfig->Read(wxT("/Filter/MicInTrebleFreqHz"), 1); - wxGetApp().m_MicInTrebleGaindB = (float)pConfig->Read(wxT("/Filter/MicInTrebleGaindB"), (long)0)/10.0; - wxGetApp().m_MicInMidFreqHz = (float)pConfig->Read(wxT("/Filter/MicInMidFreqHz"), 1); - wxGetApp().m_MicInMidGaindB = (float)pConfig->Read(wxT("/Filter/MicInMidGaindB"), (long)0)/10.0; - wxGetApp().m_MicInMidQ = (float)pConfig->Read(wxT("/Filter/MicInMidQ"), (long)100)/100.0; - - bool f = false; - wxGetApp().m_MicInEQEnable = (float)pConfig->Read(wxT("/Filter/MicInEQEnable"), f); - - wxGetApp().m_SpkOutBassFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutBassFreqHz"), 1); - wxGetApp().m_SpkOutBassGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutBassGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutTrebleFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutTrebleFreqHz"), 1); - wxGetApp().m_SpkOutTrebleGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutTrebleGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutMidFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutMidFreqHz"), 1); - wxGetApp().m_SpkOutMidGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutMidGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutMidQ = (float)pConfig->Read(wxT("/Filter/SpkOutMidQ"), (long)100)/100.0; - - wxGetApp().m_SpkOutEQEnable = (float)pConfig->Read(wxT("/Filter/SpkOutEQEnable"), f); - - wxGetApp().m_callSign = pConfig->Read("/Data/CallSign", wxT("")); - wxGetApp().m_textEncoding = pConfig->Read("/Data/TextEncoding", 1); - wxGetApp().m_enable_checksum = pConfig->Read("/Data/EnableChecksumOnMsgRx", f); - - wxGetApp().m_events = pConfig->Read("/Events/enable", f); - wxGetApp().m_events_spam_timer = (int)pConfig->Read(wxT("/Events/spam_timer"), 10); - wxGetApp().m_events_regexp_match = pConfig->Read("/Events/regexp_match", wxT("s=(.*)")); - wxGetApp().m_events_regexp_replace = pConfig->Read("/Events/regexp_replace", - wxT("curl http://qso.freedv.org/cgi-bin/onspot.cgi?s=\\1")); - // make sure regexp lists are terminated by a \n - - if (wxGetApp().m_events_regexp_match.Last() != '\n') { - wxGetApp().m_events_regexp_match = wxGetApp().m_events_regexp_match+'\n'; - } - if (wxGetApp().m_events_regexp_replace.Last() != '\n') { - wxGetApp().m_events_regexp_replace = wxGetApp().m_events_regexp_replace+'\n'; - } - - wxGetApp().m_udp_enable = (float)pConfig->Read(wxT("/UDP/enable"), f); - wxGetApp().m_udp_port = (int)pConfig->Read(wxT("/UDP/port"), 3000); - - wxGetApp().m_FreeDV700txClip = (float)pConfig->Read(wxT("/FreeDV700/txClip"), t); - wxGetApp().m_FreeDV700Combine = 1; - wxGetApp().m_noise_snr = (float)pConfig->Read(wxT("/Noise/noise_snr"), 2); - - wxGetApp().m_debug_console = (float)pConfig->Read(wxT("/Debug/console"), f); - - wxGetApp().m_attn_carrier_en = 0; - wxGetApp().m_attn_carrier = 0; - - wxGetApp().m_tone = 0; - wxGetApp().m_tone_freq_hz = 1000; - wxGetApp().m_tone_amplitude = 500; - - int mode = pConfig->Read(wxT("/Audio/mode"), (long)0); - if (mode == 0) - m_rb1600->SetValue(1); - //if (mode == 2) - // m_rb700b->SetValue(1); - if (mode == 3) - m_rb700c->SetValue(1); - if (mode == 4) - m_rb800xa->SetValue(1); - - pConfig->SetPath(wxT("/")); - -// this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - //m_togRxID->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); - //m_togTxID->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnTxIDUI), NULL, this); - m_togBtnOnOff->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnOnOffUI), NULL, this); - m_togBtnSplit->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnSplitClickUI), NULL, this); - m_togBtnAnalog->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnAnalogClickUI), NULL, this); - //m_togBtnALC->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnALCClickUI), NULL, this); - // m_btnTogPTT->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnPTT_UI), NULL, this); - - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - m_btnTogPTT->Disable(); - m_togBtnVoiceKeyer->Disable(); - //m_togBtnALC->Disable(); - - // squelch settings - char sqsnr[15]; - m_sliderSQ->SetValue((int)((g_SquelchLevel+5.0)*2.0)); - sprintf(sqsnr, "%4.1f", g_SquelchLevel); - wxString sqsnr_string(sqsnr); - m_textSQ->SetLabel(sqsnr_string); - m_ckboxSQ->SetValue(g_SquelchActive); - - // SNR settings - - m_ckboxSNR->SetValue(wxGetApp().m_snrSlow); - setsnrBeta(wxGetApp().m_snrSlow); - -#ifdef _USE_TIMER - Bind(wxEVT_TIMER, &MainFrame::OnTimer, this); // ID_MY_WINDOW); - m_plotTimer.SetOwner(this, ID_TIMER_WATERFALL); - //m_panelWaterfall->Refresh(); -#endif - - m_RxRunning = false; - -#ifdef _USE_ONIDLE - Connect(wxEVT_IDLE, wxIdleEventHandler(MainFrame::OnIdle), NULL, this); -#endif //_USE_ONIDLE - - g_sfPlayFile = NULL; - g_playFileToMicIn = false; - g_loopPlayFileToMicIn = false; - - g_sfRecFile = NULL; - g_recFileFromRadio = false; - - g_sfPlayFileFromRadio = NULL; - g_playFileFromRadio = false; - g_loopPlayFileFromRadio = false; - - // init click-tune states - - g_RxFreqOffsetHz = 0.0; - g_RxFreqOffsetPhaseRect.real = cos(0.0); - g_RxFreqOffsetPhaseRect.imag = sin(0.0); - m_panelWaterfall->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelSpectrum->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - - g_TxFreqOffsetHz = 0.0; - g_TxFreqOffsetPhaseRect.real = cos(0.0); - g_TxFreqOffsetPhaseRect.imag = sin(0.0); - - g_tx = 0; - g_split = 0; - - // data states - g_txDataInFifo = fifo_create(MAX_CALLSIGN*VARICODE_MAX_BITS); - g_rxDataOutFifo = fifo_create(MAX_CALLSIGN*VARICODE_MAX_BITS); - - sox_biquad_start(); - - g_testFrames = 0; - g_test_frame_sync_state = 0; - g_resyncs = 0; - wxGetApp().m_testFrames = false; - g_tone_phase = 0.0; - - g_modal = false; - -#ifdef __EXPERIMENTAL_UDP__ - // Start UDP listener thread - - m_UDPThread = NULL; - startUDPThread(); -#endif - - optionsDlg = new OptionsDlg(NULL); - m_schedule_restore = false; - - vk_state = VK_IDLE; - - // Init optional Windows debug console so we can see all those printfs - -#ifdef __WXMSW__ - if (wxGetApp().m_debug_console) { - // somewhere to send printfs while developing - int ret = AllocConsole(); - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - fprintf(stderr, "AllocConsole: %d m_debug_console: %d\n", ret, wxGetApp().m_debug_console); - } -#endif - - //ftest = fopen("ftest.raw", "wb"); - //assert(ftest != NULL); -} - -//------------------------------------------------------------------------- -// ~MainFrame() -//------------------------------------------------------------------------- -MainFrame::~MainFrame() -{ - int x; - int y; - int w; - int h; - - fprintf(stderr, "MainFrame::~MainFrame()\n"); - //fclose(ftest); - #ifdef __WXMSW__ - fclose(g_logfile); - #endif - - if (optionsDlg != NULL) { - delete optionsDlg; - optionsDlg = NULL; - } - -#ifdef __EXPERIMENTAL_UDP__ - stopUDPThread(); -#endif - - if (wxGetApp().m_hamlib) delete wxGetApp().m_hamlib; - if (wxGetApp().m_serialport) delete wxGetApp().m_serialport; - - wxConfigBase *pConfig = wxConfigBase::Get(); - if(pConfig) - { - if (!IsIconized()) { - GetClientSize(&w, &h); - GetPosition(&x, &y); - printf("x = %d y = %d w = %d h = %d\n", x,y,w,h); - pConfig->Write(wxT("/MainFrame/left"), (long) x); - pConfig->Write(wxT("/MainFrame/top"), (long) y); - pConfig->Write(wxT("/MainFrame/width"), (long) w); - pConfig->Write(wxT("/MainFrame/height"), (long) h); - } - pConfig->Write(wxT("/MainFrame/show_wf"), wxGetApp().m_show_wf); - pConfig->Write(wxT("/MainFrame/show_spect"), wxGetApp().m_show_spect); - pConfig->Write(wxT("/MainFrame/show_scatter"), wxGetApp().m_show_scatter); - pConfig->Write(wxT("/MainFrame/show_timing"), wxGetApp().m_show_timing); - pConfig->Write(wxT("/MainFrame/show_freq"), wxGetApp().m_show_freq); - pConfig->Write(wxT("/MainFrame/show_speech_in"), wxGetApp().m_show_speech_in); - pConfig->Write(wxT("/MainFrame/show_speech_out"), wxGetApp().m_show_speech_out); - pConfig->Write(wxT("/MainFrame/show_demod_in"), wxGetApp().m_show_demod_in); - pConfig->Write(wxT("/MainFrame/show_test_frame_errors"), wxGetApp().m_show_test_frame_errors); - pConfig->Write(wxT("/MainFrame/show_test_frame_errors_hist"), wxGetApp().m_show_test_frame_errors_hist); - - pConfig->Write(wxT("/MainFrame/rxNbookCtrl"), wxGetApp().m_rxNbookCtrl); - - pConfig->Write(wxT("/Audio/SquelchActive"), g_SquelchActive); - pConfig->Write(wxT("/Audio/SquelchLevel"), (int)(g_SquelchLevel*2.0)); - - pConfig->Write(wxT("/Audio/framesPerBuffer"), wxGetApp().m_framesPerBuffer); - - pConfig->Write(wxT("/Audio/soundCard1InDeviceNum"), g_soundCard1InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1OutDeviceNum"), g_soundCard1OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1SampleRate"), g_soundCard1SampleRate ); - - pConfig->Write(wxT("/Audio/soundCard2InDeviceNum"), g_soundCard2InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2OutDeviceNum"), g_soundCard2OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2SampleRate"), g_soundCard2SampleRate ); - - pConfig->Write(wxT("/VoiceKeyer/WaveFilePath"), wxGetApp().m_txtVoiceKeyerWaveFilePath); - pConfig->Write(wxT("/VoiceKeyer/WaveFile"), wxGetApp().m_txtVoiceKeyerWaveFile); - pConfig->Write(wxT("/VoiceKeyer/RxPause"), wxGetApp().m_intVoiceKeyerRxPause); - pConfig->Write(wxT("/VoiceKeyer/Repeats"), wxGetApp().m_intVoiceKeyerRepeats); - - pConfig->Write(wxT("/Rig/HalfDuplex"), wxGetApp().m_boolHalfDuplex); - pConfig->Write(wxT("/Rig/leftChannelVoxTone"), wxGetApp().m_leftChannelVoxTone); - pConfig->Write("/Hamlib/UseForPTT", wxGetApp().m_boolHamlibUseForPTT); - pConfig->Write("/Hamlib/RigName", wxGetApp().m_intHamlibRig); - pConfig->Write("/Hamlib/SerialPort", wxGetApp().m_strHamlibSerialPort); - pConfig->Write("/Hamlib/SerialRate", wxGetApp().m_intHamlibSerialRate); - - - pConfig->Write(wxT("/File/playFileToMicInPath"), wxGetApp().m_playFileToMicInPath); - pConfig->Write(wxT("/File/recFileFromRadioPath"), wxGetApp().m_recFileFromRadioPath); - pConfig->Write(wxT("/File/recFileFromRadioSecs"), wxGetApp().m_recFileFromRadioSecs); - pConfig->Write(wxT("/File/playFileFromRadioPath"), wxGetApp().m_playFileFromRadioPath); - - pConfig->Write(wxT("/Audio/snrSlow"), wxGetApp().m_snrSlow); - - pConfig->Write(wxT("/Data/CallSign"), wxGetApp().m_callSign); - pConfig->Write(wxT("/Data/TextEncoding"), wxGetApp().m_textEncoding); - pConfig->Write(wxT("/Data/EnableChecksumOnMsgRx"), wxGetApp().m_enable_checksum); - pConfig->Write(wxT("/Events/enable"), wxGetApp().m_events); - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - pConfig->Write(wxT("/Events/regexp_match"), wxGetApp().m_events_regexp_match); - pConfig->Write(wxT("/Events/regexp_replace"), wxGetApp().m_events_regexp_replace); - - pConfig->Write(wxT("/UDP/enable"), wxGetApp().m_udp_enable); - pConfig->Write(wxT("/UDP/port"), wxGetApp().m_udp_port); - - pConfig->Write(wxT("/Filter/MicInEQEnable"), wxGetApp().m_MicInEQEnable); - pConfig->Write(wxT("/Filter/SpkOutEQEnable"), wxGetApp().m_SpkOutEQEnable); - - pConfig->Write(wxT("/FreeDV700/txClip"), wxGetApp().m_FreeDV700txClip); - pConfig->Write(wxT("/Noise/noise_snr"), wxGetApp().m_noise_snr); - - pConfig->Write(wxT("/Debug/console"), wxGetApp().m_debug_console); - - int mode; - if (m_rb1600->GetValue()) - mode = 0; - //if (m_rb700b->GetValue()) - // mode = 2; - if (m_rb700c->GetValue()) - mode = 3; - if (m_rb800xa->GetValue()) - mode = 4; - pConfig->Write(wxT("/Audio/mode"), mode); - } - - //m_togRxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); - //m_togTxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnTxIDUI), NULL, this); - m_togBtnOnOff->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnOnOffUI), NULL, this); - m_togBtnSplit->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnSplitClickUI), NULL, this); - m_togBtnAnalog->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnAnalogClickUI), NULL, this); - //m_togBtnALC->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnALCClickUI), NULL, this); - //m_btnTogPTT->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnPTT_UI), NULL, this); - - sox_biquad_finish(); - - if (m_RxRunning) - { - stopRxStream(); - } - if (g_sfPlayFile != NULL) - { - sf_close(g_sfPlayFile); - g_sfPlayFile = NULL; - } - if (g_sfRecFile != NULL) - { - sf_close(g_sfRecFile); - g_sfRecFile = NULL; - } -#ifdef _USE_TIMER - if(m_plotTimer.IsRunning()) - { - m_plotTimer.Stop(); - Unbind(wxEVT_TIMER, &MainFrame::OnTimer, this); - } -#endif //_USE_TIMER - -#ifdef _USE_ONIDLE - Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainFrame::OnIdle), NULL, this); -#endif // _USE_ONIDLE - - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - - -#ifdef _USE_ONIDLE -void MainFrame::OnIdle(wxIdleEvent &evt) { -} -#endif - - -#ifdef _USE_TIMER -//---------------------------------------------------------------- -// OnTimer() -// -// when the timer fires every DT seconds we update the GUI displays. -// the tabs only the plot that is visible actually gets updated, this -// keeps CPU load reasonable -//---------------------------------------------------------------- -void MainFrame::OnTimer(wxTimerEvent &evt) -{ - - int r,c; - - if (m_panelWaterfall->checkDT()) { - m_panelWaterfall->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelWaterfall->m_newdata = true; - m_panelWaterfall->Refresh(); - } - - m_panelSpectrum->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelSpectrum->m_newdata = true; - m_panelSpectrum->Refresh(); - - /* update scatter/eye plot ------------------------------------------------------------*/ - - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_800XA) { - /* FSK Mode - eye diagram ---------------------------------------------------------*/ - - /* add samples row by row */ - - int i; - for (i=0; iadd_new_samples_eye(&g_stats.rx_eye[i][0], g_stats.neyesamp); - } - } - else { - /* PSK Modes - scatter plot -------------------------------------------------------*/ - for (r=0; radd_new_samples_scatter(&g_stats.rx_symbols[r][0]); - } - - if (/*(freedv_get_mode(g_pfreedv) == FREEDV_MODE_700B) ||*/(freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C)) { - - if (wxGetApp().m_FreeDV700Combine) { - m_panelScatter->setNc(g_Nc/2); /* m_FreeDV700Combine may have changed at run time */ - - /* - FreeDV 700 uses diversity, so optionaly combine - symbols for scatter plot, as combined symbols are - used for demodulation. Note we need to use a copy - of the symbols, as we are not sure when the stats - will be updated. - */ - - COMP rx_symbols_copy[g_Nc/2]; - - for(c=0; cadd_new_samples_scatter(rx_symbols_copy); - } - else { - m_panelScatter->setNc(g_Nc); /* m_FreeDV700Combine may have changed at run time */ - /* - Sometimes useful to plot carriers separately, e.g. to determine if tx carrier power is constant - across carriers. - */ - m_panelScatter->add_new_samples_scatter(&g_stats.rx_symbols[r][0]); - } - } - - } - } - - m_panelScatter->Refresh(); - - // Oscilliscope type speech plots ------------------------------------------------------- - - short speechInPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotSpeechInFifo, speechInPlotSamples, WAVEFORM_PLOT_BUF)) { - memset(speechInPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - //fprintf(stderr, "empty!\n"); - } - m_panelSpeechIn->add_new_short_samples(0, speechInPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelSpeechIn->Refresh(); - - short speechOutPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotSpeechOutFifo, speechOutPlotSamples, WAVEFORM_PLOT_BUF)) - memset(speechOutPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - m_panelSpeechOut->add_new_short_samples(0, speechOutPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelSpeechOut->Refresh(); - - short demodInPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotDemodInFifo, demodInPlotSamples, WAVEFORM_PLOT_BUF)) { - memset(demodInPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - } - m_panelDemodIn->add_new_short_samples(0,demodInPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelDemodIn->Refresh(); - - // Demod states ----------------------------------------------------------------------- - - m_panelTimeOffset->add_new_sample(0, (float)g_stats.rx_timing/FDMDV_NOM_SAMPLES_PER_FRAME); - m_panelTimeOffset->Refresh(); - - m_panelFreqOffset->add_new_sample(0, g_stats.foff); - m_panelFreqOffset->Refresh(); - - // SNR text box and gauge ------------------------------------------------------------ - - // LP filter g_stats.snr_est some more to stabilise the - // display. g_stats.snr_est already has some low pass filtering - // but we need it fairly fast to activate squelch. So we - // optionally perform some further filtering for the display - // version of SNR. The "Slow" checkbox controls the amount of - // filtering. The filtered snr also controls the squelch - - g_snr = m_snrBeta*g_snr + (1.0 - m_snrBeta)*g_stats.snr_est; - float snr_limited = g_snr; - if (snr_limited < -5.0) snr_limited = -5.0; - if (snr_limited > 20.0) snr_limited = 20.0; - - char snr[15]; - sprintf(snr, "%d", (int)(g_snr+0.5)); // round to nearest dB - - //printf("snr_est: %f m_snrBeta: %f g_snr: %f snr_limited: %f\n", g_stats.snr_est, m_snrBeta, g_snr, snr_limited); - - wxString snr_string(snr); - m_textSNR->SetLabel(snr_string); - m_gaugeSNR->SetValue((int)(snr_limited+5)); - - - // Level Gauge ----------------------------------------------------------------------- - - float tooHighThresh; - if (!g_tx && m_RxRunning) - { - // receive mode - display From Radio peaks - // peak from this DT sampling period - int maxDemodIn = 0; - for(int i=0; i m_maxLevel) - m_maxLevel = maxDemodIn; - - tooHighThresh = FROM_RADIO_MAX; - } - else - { - // transmit mode - display From Mic peaks - - // peak from this DT sampling period - int maxSpeechIn = 0; - for(int i=0; i m_maxLevel) - m_maxLevel = maxSpeechIn; - - tooHighThresh = FROM_MIC_MAX; - } - - // Peak Reading meter: updates peaks immediately, then slowly decays - int maxScaled = (int)(100.0 * ((float)m_maxLevel/32767.0)); - m_gaugeLevel->SetValue(maxScaled); - //printf("maxScaled: %d\n", maxScaled); - if (((float)maxScaled/100) > tooHighThresh) - m_textLevel->SetLabel("Too High"); - else - m_textLevel->SetLabel(""); - - m_maxLevel *= LEVEL_BETA; - - // sync LED (Colours don't work on Windows) ------------------------ - - //fprintf(stderr, "g_State: %d m_rbSync->GetValue(): %d\n", g_State, m_rbSync->GetValue()); - if (g_State) { - if (g_prev_State == 0) { - g_resyncs++; - } - m_rbSync->SetForegroundColour( wxColour( 0, 255, 0 ) ); // green - m_rbSync->SetValue(true); - } - else { - m_rbSync->SetForegroundColour( wxColour( 255, 0, 0 ) ); // red - m_rbSync->SetValue(false); - } - g_prev_State = g_State; - - // send Callsign ---------------------------------------------------- - - char callsign[MAX_CALLSIGN]; - strncpy(callsign, (const char*) wxGetApp().m_callSign.mb_str(wxConvUTF8), MAX_CALLSIGN-1); - - // buffer 1 txt message to ensure tx data fifo doesn't "run dry" - - if ((unsigned)fifo_used(g_txDataInFifo) < strlen(callsign)) { - unsigned int i; - - //fprintf(g_logfile, "tx callsign: %s.\n", callsign); - - /* optionally append checksum */ - - if (wxGetApp().m_enable_checksum) { - - unsigned char checksum = 0; - char callsign_checksum_cr[MAX_CALLSIGN+1]; - - for(i=0; i MAX_CALLSIGN-1)) { - // CR completes line - *m_pcallsign = 0; - - /* Checksums can be disabled, e.g. for compatability with - older vesions. In that case we print msg but don't do - any event processing. If checksums enabled, only print - out when checksum is good. */ - - if (wxGetApp().m_enable_checksum) { - // lets see if checksum is OK - - unsigned char checksum_rx = 0; - if (strlen(m_callsign) > 2) { - for(unsigned int i=0; iSetValue(s); - -#ifdef __UDP_EXPERIMENTAL__ - char s1[MAX_CALLSIGN]; - sprintf(s1,"rx_txtmsg %s", m_callsign); - processTxtEvent(s1); - - m_checksumGood++; - s.Printf("%d", m_checksumGood); - m_txtChecksumGood->SetLabel(s); -#endif - } - else { -#ifdef __UDP_EXPERIMENTAL__ - m_checksumBad++; - s.Printf("%d", m_checksumBad); - m_txtChecksumBad->SetLabel(s); -#endif - } - } - - //fprintf(g_logfile,"resetting callsign %s %ld\n", m_callsign, m_pcallsign-m_callsign); - // reset ptr to start of string - m_pcallsign = m_callsign; - } - else { - //fprintf(g_logfile, "new char %d %c\n", ashort, (char)ashort); - *m_pcallsign++ = (char)ashort; - } - - /* If checksums disabled, display txt chars as they arrive */ - - if (!wxGetApp().m_enable_checksum) { - m_txtCtrlCallSign->SetValue(m_callsign); - } - } - - - // Run time update of EQ filters ----------------------------------- - - if (m_newMicInFilter || m_newSpkOutFilter) { - g_mutexProtectingCallbackData.Lock(); - deleteEQFilters(g_rxUserdata); - designEQFilters(g_rxUserdata); - g_mutexProtectingCallbackData.Unlock(); - m_newMicInFilter = m_newSpkOutFilter = false; - } - g_rxUserdata->micInEQEnable = wxGetApp().m_MicInEQEnable; - g_rxUserdata->spkOutEQEnable = wxGetApp().m_SpkOutEQEnable; - - - if (g_mode != -1) { - - // Run time update of FreeDV 700 tx clipper - - freedv_set_clip(g_pfreedv, (int)wxGetApp().m_FreeDV700txClip); - - // Test Frame Bit Error Updates ------------------------------------ - - // Toggle test frame mode at run time - - if (!freedv_get_test_frames(g_pfreedv) && wxGetApp().m_testFrames) { - - // reset stats on check box off to on transition - - freedv_set_test_frames(g_pfreedv, 1); - freedv_set_total_bits(g_pfreedv, 0); - freedv_set_total_bit_errors(g_pfreedv, 0); - } - freedv_set_test_frames(g_pfreedv, wxGetApp().m_testFrames); - freedv_set_test_frames_diversity(g_pfreedv, wxGetApp().m_FreeDV700Combine); - g_channel_noise = wxGetApp().m_channel_noise; - - if (g_State) { - char bits[80], errors[80], ber[80], resyncs[80]; - - // update stats on main page - - sprintf(bits, "Bits: %d", freedv_get_total_bits(g_pfreedv)); wxString bits_string(bits); m_textBits->SetLabel(bits_string); - sprintf(errors, "Errs: %d", freedv_get_total_bit_errors(g_pfreedv)); wxString errors_string(errors); m_textErrors->SetLabel(errors_string); - float b = (float)freedv_get_total_bit_errors(g_pfreedv)/(1E-6+freedv_get_total_bits(g_pfreedv)); - sprintf(ber, "BER: %4.3f", b); wxString ber_string(ber); m_textBER->SetLabel(ber_string); - sprintf(resyncs, "Resyncs: %d", g_resyncs); wxString resyncs_string(resyncs); m_textResyncs->SetLabel(resyncs_string); - - // update error pattern plots if supported - - int sz_error_pattern = freedv_get_sz_error_pattern(g_pfreedv); - //fprintf(stderr, "sz_error_pattern: %d\n", sz_error_pattern); - if (sz_error_pattern) { - short error_pattern[sz_error_pattern]; - - if (fifo_read(g_error_pattern_fifo, error_pattern, sz_error_pattern) == 0) { - int i,b; - - /* both modes map IQ to alternate bits, but on same carrier */ - - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_1600) { - /* FreeDV 1600 mapping from error pattern to two bits on each carrier */ - - for(b=0; badd_new_sample(b, b + 0.8*error_pattern[i]); - g_error_hist[b] += error_pattern[i]; - g_error_histn[b]++; - } - //if (b%2) - // printf("g_error_hist[%d]: %d\n", b/2, g_error_hist[b/2]); - } - - /* calculate BERs and send to plot */ - - float ber[2*FDMDV_NC_MAX]; - for(b=0; b<2*FDMDV_NC_MAX; b++) { - ber[b] = 0.0; - } - for(b=0; badd_new_samples(0, ber, 2*FDMDV_NC_MAX); - } - - if (/*(freedv_get_mode(g_pfreedv) == FREEDV_MODE_700B) || */(freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C)) { - int c; - //fprintf(stderr, "after g_error_pattern_fifo read 2\n"); - - /* - FreeDV 700 mapping from error pattern to bit on each carrier, see - data bit to carrier mapping in: - - codec2-dev/octave/cohpsk_frame_design.ods - - We can plot a histogram of the errors/carrier before or after diversity - recombination. Actually one bar for each IQ bit in carrier order. - */ - - int hist_Nc = sz_error_pattern/4; - //fprintf(stderr, "hist_Nc: %d\n", hist_Nc); - - for(i=0; iadd_new_sample(c, c + 0.8*error_pattern[i]); - g_error_hist[c] += error_pattern[i]; - g_error_histn[c]++; - //printf("i: %d c: %d\n", i, c); - } - for(; i<2*MODEM_STATS_NC_MAX*4; i++) { - c = floor(i/4); - m_panelTestFrameErrors->add_new_sample(c, c); - //printf("i: %d c: %d\n", i, c); - } - - /* calculate BERs and send to plot */ - - float ber[2*FDMDV_NC_MAX]; - for(b=0; b<2*FDMDV_NC_MAX; b++) { - ber[b] = 0.0; - } - for(b=0; badd_new_samples(0, ber, 2*FDMDV_NC_MAX); - } - - m_panelTestFrameErrors->Refresh(); - m_panelTestFrameErrorsHist->Refresh(); - } - } - } - } - - // command from UDP thread that is best processed in main thread to avoid seg faults - - if (m_schedule_restore) { - if (IsIconized()) - Restore(); - m_schedule_restore = false; - } - -#ifdef __UDP_EXPERIMENTAL__ - // Light Spam Timer LED if at least one timer is running - - int i; - optionsDlg->SetSpamTimerLight(false); - for(i=0; iSetSpamTimerLight(true); -#endif - - // Blink file playback status line - - if (g_playFileFromRadio) { - g_blink += DT; - //fprintf(g_logfile, "g_blink: %f\n", g_blink); - if ((g_blink >= 1.0) && (g_blink < 2.0)) - SetStatusText(wxT("Playing into from radio"), 0); - if (g_blink >= 2.0) { - SetStatusText(wxT(""), 0); - g_blink = 0.0; - } - } - - // Voice Keyer state machine - - VoiceKeyerProcessEvent(VK_DT); -} -#endif - - -//------------------------------------------------------------------------- -// OnCloseFrame() -//------------------------------------------------------------------------- -void MainFrame::OnCloseFrame(wxCloseEvent& event) -{ - fprintf(stderr, "MainFrame::OnCloseFrame()\n"); - Pa_Terminate(); - Destroy(); -} - -//------------------------------------------------------------------------- -// OnTop() -//------------------------------------------------------------------------- -void MainFrame::OnTop(wxCommandEvent& event) -{ - int style = GetWindowStyle(); - - if (style & wxSTAY_ON_TOP) - { - style &= ~wxSTAY_ON_TOP; - } - else - { - style |= wxSTAY_ON_TOP; - } - SetWindowStyle(style); -} - -//------------------------------------------------------------------------- -// OnDeleteConfig() -//------------------------------------------------------------------------- -void MainFrame::OnDeleteConfig(wxCommandEvent&) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - if(pConfig->DeleteAll()) - { - wxLogMessage(wxT("Config file/registry key successfully deleted.")); - - delete wxConfigBase::Set(NULL); - wxConfigBase::DontCreateOnDemand(); - } - else - { - wxLogError(wxT("Deleting config file/registry key failed.")); - } -} - -//------------------------------------------------------------------------- -// Paint() -//------------------------------------------------------------------------- -void MainFrame::OnPaint(wxPaintEvent& WXUNUSED(event)) -{ - wxPaintDC dc(this); - - if(GetMenuBar()->IsChecked(ID_PAINT_BG)) - { - dc.Clear(); - } - dc.SetUserScale(m_zoom, m_zoom); -} - -//------------------------------------------------------------------------- -// OnCmdSliderScroll() -//------------------------------------------------------------------------- -void MainFrame::OnCmdSliderScroll(wxScrollEvent& event) -{ - char sqsnr[15]; - g_SquelchLevel = (float)m_sliderSQ->GetValue()/2.0 - 5.0; - sprintf(sqsnr, "%4.1f", g_SquelchLevel); // 0.5 dB steps - wxString sqsnr_string(sqsnr); - m_textSQ->SetLabel(sqsnr_string); - - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnCheckSQClick() -//------------------------------------------------------------------------- -void MainFrame::OnCheckSQClick(wxCommandEvent& event) -{ - if(!g_SquelchActive) - { - g_SquelchActive = true; - } - else - { - g_SquelchActive = false; - } -} - -void MainFrame::setsnrBeta(bool snrSlow) -{ - if(snrSlow) - { - m_snrBeta = 0.95; // make this closer to 1.0 to smooth SNR est further - } - else - { - m_snrBeta = 0.0; // no smoothing of SNR estimate from demodulator - } -} - -//------------------------------------------------------------------------- -// OnCheckSQClick() -//------------------------------------------------------------------------- -void MainFrame::OnCheckSNRClick(wxCommandEvent& event) -{ - wxGetApp().m_snrSlow = m_ckboxSNR->GetValue(); - setsnrBeta(wxGetApp().m_snrSlow); - //printf("m_snrSlow: %d\n", (int)wxGetApp().m_snrSlow); -} - -// check for space bar press (only when running) - -int MainApp::FilterEvent(wxEvent& event) -{ - if ((event.GetEventType() == wxEVT_KEY_DOWN) && - (((wxKeyEvent&)event).GetKeyCode() == WXK_SPACE)) - { - // only use space to toggle PTT if we are running and no modal dialogs (like options) up - //fprintf(stderr,"frame->m_RxRunning: %d g_modal: %d\n", - // frame->m_RxRunning, g_modal); - if (frame->m_RxRunning && !g_modal) { - - // space bar controls rx/rx if keyer not running - if (frame->vk_state == VK_IDLE) { - if (frame->m_btnTogPTT->GetValue()) - frame->m_btnTogPTT->SetValue(false); - else - frame->m_btnTogPTT->SetValue(true); - - frame->togglePTT(); - } - else // spavce bar stops keyer - frame->VoiceKeyerProcessEvent(VK_SPACE_BAR); - - return true; // absorb space so we don't toggle control with focus (e.g. Start) - - } - } - - return -1; -} - -//------------------------------------------------------------------------- -// OnTogBtnPTT () -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnPTT (wxCommandEvent& event) -{ - togglePTT(); - event.Skip(); -} - -void MainFrame::togglePTT(void) { - - // Change tabbed page in centre panel depending on PTT state - - if (g_tx) - { - // tx-> rx transition, swap to the page we were on for last rx - m_auiNbookCtrl->ChangeSelection(wxGetApp().m_rxNbookCtrl); - } - else - { - // rx-> tx transition, swap to Mic In page to monitor speech - wxGetApp().m_rxNbookCtrl = m_auiNbookCtrl->GetSelection(); - m_auiNbookCtrl->ChangeSelection(m_auiNbookCtrl->GetPageIndex((wxWindow *)m_panelSpeechIn)); -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"ptt"); processTxtEvent(e); -#endif - } - - g_tx = m_btnTogPTT->GetValue(); - - // Hamlib PTT - - if (wxGetApp().m_boolHamlibUseForPTT) { - Hamlib *hamlib = wxGetApp().m_hamlib; - wxString hamlibError; - if (wxGetApp().m_boolHamlibUseForPTT && hamlib != NULL) { - if (hamlib->ptt(g_tx, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - } - } - - // Serial PTT - - if (wxGetApp().m_boolUseSerialPTT && (wxGetApp().m_serialport->isopen())) { - wxGetApp().m_serialport->ptt(g_tx); - } - - // reset level gauge - - m_maxLevel = 0; - m_textLevel->SetLabel(wxT("")); - m_gaugeLevel->SetValue(0); -} - -/* - Voice Keyer: - - + space bar turns keyer off - + 5 secs of valid sync turns it off - - [X] complete state machine and builds OK - [ ] file select dialog - [ ] test all states - [ ] restore size -*/ - -void MainFrame::OnTogBtnVoiceKeyerClick (wxCommandEvent& event) -{ - if (vk_state == VK_IDLE) - VoiceKeyerProcessEvent(VK_START); - else - VoiceKeyerProcessEvent(VK_SPACE_BAR); - - event.Skip(); -} - - -int MainFrame::VoiceKeyerStartTx(void) -{ - int next_state; - - // start playing wave file or die trying - - SF_INFO sfInfo; - sfInfo.format = 0; - - g_sfPlayFile = sf_open(wxGetApp().m_txtVoiceKeyerWaveFile, SFM_READ, &sfInfo); - if(g_sfPlayFile == NULL) { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open:") + wxGetApp().m_txtVoiceKeyerWaveFile, wxOK); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - else { - SetStatusText(wxT("Voice Keyer: Playing File") + wxGetApp().m_txtVoiceKeyerWaveFile + wxT(" to Mic Input") , 0); - g_loopPlayFileToMicIn = false; - g_playFileToMicIn = true; - - m_btnTogPTT->SetValue(true); togglePTT(); - next_state = VK_TX; - } - - return next_state; -} - - -void MainFrame::VoiceKeyerProcessEvent(int vk_event) { - int next_state = vk_state; - - switch(vk_state) { - - case VK_IDLE: - if (vk_event == VK_START) { - // sample these puppies at start just in case they are changed while VK running - vk_rx_pause = wxGetApp().m_intVoiceKeyerRxPause; - vk_repeats = wxGetApp().m_intVoiceKeyerRepeats; - fprintf(stderr, "vk_rx_pause: %d vk_repeats: %d\n", vk_rx_pause, vk_repeats); - - vk_repeat_counter = 0; - next_state = VoiceKeyerStartTx(); - } - break; - - case VK_TX: - - // In this state we are transmitting and playing a wave file - // to Mic In - - if (vk_event == VK_SPACE_BAR) { - m_btnTogPTT->SetValue(false); togglePTT(); - StopPlayFileToMicIn(); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if (vk_event == VK_PLAY_FINISHED) { - m_btnTogPTT->SetValue(false); togglePTT(); - vk_repeat_counter++; - if (vk_repeat_counter > vk_repeats) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - else { - vk_rx_time = 0.0; - next_state = VK_RX; - } - } - - break; - - case VK_RX: - - // in this state we are receiving and waiting for - // delay timer or valid sync - - if (vk_event == VK_DT) { - if (freedv_get_sync(g_pfreedv) == 1) { - // if we detect sync simulate a smooth transition to SYNC_WAIT State - TODO: review - if (vk_rx_time >= DT) { - vk_rx_time -= DT; - } else { - next_state = VK_SYNC_WAIT; - } - } else { - vk_rx_time += DT; - if (vk_rx_time >= vk_rx_pause) { - next_state = VoiceKeyerStartTx(); - } - } - } - - if (vk_event == VK_SPACE_BAR) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - break; - - case VK_SYNC_WAIT: - - // In this state we wait for valid sync to last - // VK_SYNC_WAIT_TIME seconds - - if (vk_event == VK_SPACE_BAR) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if (vk_event == VK_DT) { - if (freedv_get_sync(g_pfreedv) == 0) { - // if we lose sync simulate a smooth transition to return in RX State - TODO: review - if (vk_rx_time >= DT) { - vk_rx_time -= DT; - } else { - next_state = VK_RX; - } - } else { - vk_rx_time += DT; - } - - // drop out of voice keyer if we get a few seconds of valid sync - - if (vk_rx_time >= VK_SYNC_WAIT_TIME) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - } - break; - - default: - // catch anything we missed - - m_btnTogPTT->SetValue(false); togglePTT(); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - //if ((vk_event != VK_DT) || (vk_state != next_state)) - // fprintf(stderr, "VoiceKeyerProcessEvent: vk_state: %d vk_event: %d next_state: %d vk_repeat_counter: %d\n", vk_state, vk_event, next_state, vk_repeat_counter); - vk_state = next_state; -} - - -//------------------------------------------------------------------------- -// OnTogBtnRxID() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnRxID(wxCommandEvent& event) -{ - // empty any junk in rx data FIFO - short junk; - while(fifo_read(g_rxDataOutFifo,&junk,1) == 0); - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnTogBtnTxID() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnTxID(wxCommandEvent& event) -{ - event.Skip(); -} - -void MainFrame::OnTogBtnSplitClick(wxCommandEvent& event) { - if (g_split) - g_split = 0; - else - g_split = 1; - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnTogBtnAnalogClick() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnAnalogClick (wxCommandEvent& event) -{ - if (g_analog == 0) { - g_analog = 1; - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(FS/2))); - m_panelWaterfall->setFs(FS); - } - else { - g_analog = 0; - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(freedv_get_modem_sample_rate(g_pfreedv)/2))); - m_panelWaterfall->setFs(freedv_get_modem_sample_rate(g_pfreedv)); - } - - g_State = g_prev_State = 0; - g_stats.snr_est = 0; - - event.Skip(); -} - -void MainFrame::OnCallSignReset(wxCommandEvent& event) -{ - m_pcallsign = m_callsign; - memset(m_callsign, 0, MAX_CALLSIGN); - wxString s; - s.Printf("%s", m_callsign); - m_txtCtrlCallSign->SetValue(s); -#ifdef __UDP__EXPERIMENTAL__ - m_checksumGood = m_checksumBad = 0; - m_txtChecksumGood->SetLabel(_("0")); - m_txtChecksumBad->SetLabel(_("0")); -#endif -} - -void MainFrame::OnBerReset(wxCommandEvent& event) -{ - freedv_set_total_bits(g_pfreedv, 0); - freedv_set_total_bit_errors(g_pfreedv, 0); - g_resyncs = 0; - int i; - for(i=0; i<2*g_Nc; i++) { - g_error_hist[i] = 0; - g_error_histn[i] = 0; - } - -} - -#ifdef ALC -//------------------------------------------------------------------------- -// OnTogBtnALCClick() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnALCClick(wxCommandEvent& event) -{ - wxMessageBox(wxT("Got Click!"), wxT("OnTogBtnALCClick"), wxOK); - - event.Skip(); -} -#endif - -// extra panel added to file open dialog to add loop checkbox -MyExtraPlayFilePanel::MyExtraPlayFilePanel(wxWindow *parent): wxPanel(parent) -{ - m_cb = new wxCheckBox(this, -1, wxT("Loop")); - m_cb->SetToolTip(_("When checked file will repeat forever")); - m_cb->SetValue(g_loopPlayFileToMicIn); - - // bug: I can't this to align right..... - wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL); - sizerTop->Add(m_cb, 0, wxALIGN_RIGHT, 0); - SetSizerAndFit(sizerTop); -} - -static wxWindow* createMyExtraPlayFilePanel(wxWindow *parent) -{ - return new MyExtraPlayFilePanel(parent); -} - -void MainFrame::StopPlayFileToMicIn(void) -{ - g_mutexProtectingCallbackData.Lock(); - g_playFileToMicIn = false; - sf_close(g_sfPlayFile); - SetStatusText(wxT("")); - g_mutexProtectingCallbackData.Unlock(); -} - -//------------------------------------------------------------------------- -// OnPlayFileToMicIn() -//------------------------------------------------------------------------- -void MainFrame::OnPlayFileToMicIn(wxCommandEvent& event) -{ - wxUnusedVar(event); - - if(g_playFileToMicIn) { - StopPlayFileToMicIn(); - VoiceKeyerProcessEvent(VK_PLAY_FINISHED); - } - else - { - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Play File to Mic In"), - wxGetApp().m_playFileToMicInPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_OPEN | wxFD_FILE_MUST_EXIST - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraPlayFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_playFileToMicInPath, &fileName, &extension); - //wxLogDebug("m_playFileToMicInPath: %s", wxGetApp().m_playFileToMicInPath); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = FS; - } - } - g_sfPlayFile = sf_open(soundFile.c_str(), SFM_READ, &sfInfo); - if(g_sfPlayFile == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - - // Huh?! I just copied wxWidgets-2.9.4/samples/dialogs .... - g_loopPlayFileToMicIn = static_cast(ctrl)->getLoopPlayFileToMicIn(); - - SetStatusText(wxT("Playing File: ") + fileName + wxT(" to Mic Input") , 0); - g_playFileToMicIn = true; - } -} - -//------------------------------------------------------------------------- -// OnPlayFileFromRadio() -// This puppy "plays" a recorded file into the demodulator input, allowing us -// to replay off air signals. -//------------------------------------------------------------------------- -void MainFrame::OnPlayFileFromRadio(wxCommandEvent& event) -{ - wxUnusedVar(event); - - printf("OnPlayFileFromRadio:: %d\n", (int)g_playFileFromRadio); - if (g_playFileFromRadio) - { - printf("OnPlayFileFromRadio:: Stop\n"); - g_mutexProtectingCallbackData.Lock(); - g_playFileFromRadio = false; - sf_close(g_sfPlayFileFromRadio); - SetStatusText(wxT(""),0); - SetStatusText(wxT(""),1); - g_mutexProtectingCallbackData.Unlock(); - } - else - { - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Play File - From Radio"), - wxGetApp().m_playFileFromRadioPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_OPEN | wxFD_FILE_MUST_EXIST - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraPlayFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_playFileFromRadioPath, &fileName, &extension); - //wxLogDebug("m_playFileToFromRadioPath: %s", wxGetApp().m_playFileFromRadioPath); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } - } - g_sfPlayFileFromRadio = sf_open(soundFile.c_str(), SFM_READ, &sfInfo); - g_sfFs = sfInfo.samplerate; - if(g_sfPlayFileFromRadio == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - - // Huh?! I just copied wxWidgets-2.9.4/samples/dialogs .... - g_loopPlayFileFromRadio = static_cast(ctrl)->getLoopPlayFileToMicIn(); - - SetStatusText(wxT("Playing into from radio"), 0); - if(extension == wxT("raw")) { - wxString stringnumber = wxString::Format(wxT("%d"), (int)sfInfo.samplerate); - SetStatusText(wxT("raw file assuming Fs=") + stringnumber, 1); - } - fprintf(g_logfile, "OnPlayFileFromRadio:: Playing File\n"); - g_playFileFromRadio = true; - g_blink = 0.0; - } -} - -// extra panel added to file save dialog to set number of seconds to record for - -MyExtraRecFilePanel::MyExtraRecFilePanel(wxWindow *parent): wxPanel(parent) -{ - wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL); - - wxStaticText* staticText = new wxStaticText(this, wxID_ANY, _("Seconds:"), wxDefaultPosition, wxDefaultSize, 0); - sizerTop->Add(staticText, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_secondsToRecord = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - m_secondsToRecord->SetToolTip(_("Number of seconds to record for")); - m_secondsToRecord->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_recFileFromRadioSecs)); - sizerTop->Add(m_secondsToRecord, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5); - SetSizerAndFit(sizerTop); -} - -static wxWindow* createMyExtraRecFilePanel(wxWindow *parent) -{ - return new MyExtraRecFilePanel(parent); -} - -//------------------------------------------------------------------------- -// OnRecFileFromRadio() -//------------------------------------------------------------------------- -void MainFrame::OnRecFileFromRadio(wxCommandEvent& event) -{ - wxUnusedVar(event); - - if (g_recFileFromRadio) { - printf("Stopping Record....\n"); - g_mutexProtectingCallbackData.Lock(); - g_recFileFromRadio = false; - sf_close(g_sfRecFile); - SetStatusText(wxT("")); - g_mutexProtectingCallbackData.Unlock(); - } - else { - - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Record File From Radio"), - wxGetApp().m_recFileFromRadioPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_SAVE - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraRecFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_recFileFromRadioPath, &fileName, &extension); - wxLogDebug("m_recFileFromRadioPath: %s", wxGetApp().m_recFileFromRadioPath); - wxLogDebug("soundFile: %s", soundFile); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } - else if(extension == wxT("wav")) - { - sfInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } else { - wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); - return; - } - } - else { - wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); - return; - } - - // Bug: on Win32 I cant read m_recFileFromRadioSecs, so have hard coded it -#ifdef __WIN32__ - long secs = wxGetApp().m_recFileFromRadioSecs; - g_recFromRadioSamples = FS*(unsigned int)secs; -#else - // work out number of samples to record - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - wxString secsString = static_cast(ctrl)->getSecondsToRecord(); - wxLogDebug("test: %s secsString: %s\n", wxT("testing 123"), secsString); - - long secs; - if (secsString.ToLong(&secs)) { - wxGetApp().m_recFileFromRadioSecs = (unsigned int)secs; - //printf(" secondsToRecord: %d\n", (unsigned int)secs); - g_recFromRadioSamples = FS*(unsigned int)secs; - //printf("g_recFromRadioSamples: %d\n", g_recFromRadioSamples); - } - else { - wxMessageBox(wxT("Invalid number of Seconds"), wxT("Record File From Radio"), wxOK); - return; - } -#endif - - g_sfRecFile = sf_open(soundFile.c_str(), SFM_WRITE, &sfInfo); - if(g_sfRecFile == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - SetStatusText(wxT("Recording File: ") + fileName + wxT(" From Radio") , 0); - g_recFileFromRadio = true; - } - -} - -//------------------------------------------------------------------------- -// OnExit() -//------------------------------------------------------------------------- -void MainFrame::OnExit(wxCommandEvent& event) -{ - fprintf(stderr, "MainFrame::OnExit\n"); - wxUnusedVar(event); -#ifdef _USE_TIMER - m_plotTimer.Stop(); -#endif // _USE_TIMER - if(g_sfPlayFile != NULL) - { - sf_close(g_sfPlayFile); - g_sfPlayFile = NULL; - } - if(g_sfRecFile != NULL) - { - sf_close(g_sfRecFile); - g_sfRecFile = NULL; - } - if(m_RxRunning) - { - stopRxStream(); - } - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - //m_togBtnALC->Disable(); - //m_btnTogPTT->Disable(); - Pa_Terminate(); - Destroy(); -} - -//------------------------------------------------------------------------- -// OnExitClick() -//------------------------------------------------------------------------- -void MainFrame::OnExitClick(wxCommandEvent& event) -{ - OnExit(event); -} - -//------------------------------------------------------------------------- -// OnToolsAudio() -//------------------------------------------------------------------------- -void MainFrame::OnToolsAudio(wxCommandEvent& event) -{ - wxUnusedVar(event); - int rv = 0; - AudioOptsDialog *dlg = new AudioOptsDialog(NULL); - rv = dlg->ShowModal(); - if(rv == wxID_OK) - { - dlg->ExchangeData(EXCHANGE_DATA_OUT); - } - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsAudioUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsAudioUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning); -} - -//------------------------------------------------------------------------- -// OnToolsFilter() -//------------------------------------------------------------------------- -void MainFrame::OnToolsFilter(wxCommandEvent& event) -{ - wxUnusedVar(event); - FilterDlg *dlg = new FilterDlg(NULL, m_RxRunning, &m_newMicInFilter, &m_newSpkOutFilter); - dlg->ShowModal(); - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsOptions() -//------------------------------------------------------------------------- -void MainFrame::OnToolsOptions(wxCommandEvent& event) -{ - wxUnusedVar(event); - g_modal = true; - //fprintf(stderr,"g_modal: %d\n", g_modal); - optionsDlg->Show(); -} - -//------------------------------------------------------------------------- -// OnToolsOptionsUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsOptionsUI(wxUpdateUIEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnToolsComCfg() -//------------------------------------------------------------------------- -void MainFrame::OnToolsComCfg(wxCommandEvent& event) -{ - wxUnusedVar(event); - - ComPortsDlg *dlg = new ComPortsDlg(NULL); - - dlg->ShowModal(); - - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsComCfgUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsComCfgUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning); -} - -//------------------------------------------------------------------------- -// OnToolsPlugInCfg() -//------------------------------------------------------------------------- -void MainFrame::OnToolsPlugInCfg(wxCommandEvent& event) -{ - wxUnusedVar(event); - PlugInDlg *dlg = new PlugInDlg(wxGetApp().m_plugInName, wxGetApp().m_numPlugInParam, wxGetApp().m_plugInParamName); - dlg->ShowModal(); - delete dlg; -} - -void MainFrame::OnToolsPlugInCfgUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning && wxGetApp().m_plugIn); -} - - -//------------------------------------------------------------------------- -// OnHelpCheckUpdates() -//------------------------------------------------------------------------- -void MainFrame::OnHelpCheckUpdates(wxCommandEvent& event) -{ - wxMessageBox("Got Click!", "OnHelpCheckUpdates", wxOK); - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnHelpCheckUpdatesUI() -//------------------------------------------------------------------------- -void MainFrame::OnHelpCheckUpdatesUI(wxUpdateUIEvent& event) -{ - event.Enable(false); -} - -//------------------------------------------------------------------------- -//OnHelpAbout() -//------------------------------------------------------------------------- -void MainFrame::OnHelpAbout(wxCommandEvent& event) -{ - wxUnusedVar(event); - wxString msg; - msg.Printf( wxT("FreeDV %s\n\n") - wxT("Open Source Digital Voice\n\n") - wxT("For Help and Support visit: http://freedv.org\n\n") - - wxT("GNU Public License V2.1\n\n") - wxT("Copyright (c) David Witten KD0EAG and David Rowe VK5DGR\n\n") - wxT("svn revision: %s\n"), FREEDV_VERSION, SVN_REVISION); - - wxMessageBox(msg, wxT("About"), wxOK | wxICON_INFORMATION, this); -} - - -// Attempt to talk to rig using Hamlib - -bool MainFrame::OpenHamlibRig() { - if (wxGetApp().m_boolHamlibUseForPTT != true) - return false; - if (wxGetApp().m_intHamlibRig == 0) - return false; - if (wxGetApp().m_hamlib == NULL) - return false; - - int rig = wxGetApp().m_intHamlibRig; - wxString port = wxGetApp().m_strHamlibSerialPort; - int serial_rate = wxGetApp().m_intHamlibSerialRate; - bool status = wxGetApp().m_hamlib->connect(rig, port.mb_str(wxConvUTF8), serial_rate); - if (status == false) - wxMessageBox("Couldn't connect to Radio with hamlib", wxT("Error"), wxOK | wxICON_ERROR, this); - - return status; -} - -//------------------------------------------------------------------------- -// OnTogBtnOnOff() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnOnOff(wxCommandEvent& event) -{ - wxString startStop = m_togBtnOnOff->GetLabel(); - - // we are attempting to start - - if (startStop.IsSameAs("Start")) - { - // - // Start Running ------------------------------------------------- - // - - // modify some button states when running - - m_togBtnSplit->Enable(); - m_togBtnAnalog->Enable(); - m_togBtnOnOff->SetLabel(wxT("Stop")); - m_btnTogPTT->Enable(); - m_togBtnVoiceKeyer->Enable(); - vk_state = VK_IDLE; - - m_rb1600->Disable(); - //m_rb700b->Disable(); - m_rb700c->Disable(); - m_rb800xa->Disable(); - if (m_rbPlugIn != NULL) - m_rbPlugIn->Disable(); - - // determine what mode we are using - - if (m_rb1600->GetValue()) { - g_mode = FREEDV_MODE_1600; - g_Nc = 16; - m_panelScatter->setNc(g_Nc+1); /* +1 for BPSK pilot */ - } - #ifdef DISABLED - if (m_rb700b->GetValue()) { - g_mode = FREEDV_MODE_700B; - g_Nc = 14; - if (wxGetApp().m_FreeDV700Combine) { - m_panelScatter->setNc(g_Nc/2); /* diversity combnation */ - } - else { - m_panelScatter->setNc(g_Nc); - } - } - #endif - if (m_rb700c->GetValue()) { - g_mode = FREEDV_MODE_700C; - g_Nc = 14; - if (wxGetApp().m_FreeDV700Combine) { - m_panelScatter->setNc(g_Nc/2); /* diversity combnation */ - } - else { - m_panelScatter->setNc(g_Nc); - } - } - if (m_rb800xa->GetValue()) { - g_mode = FREEDV_MODE_800XA; - } - if (m_rbPlugIn != NULL) { - if (m_rbPlugIn->GetValue()) { - g_mode = -1; /* TODO; a better way of handling (enumarating?) non-freedv modes */ - - /* scale plots assuming Fs = 8000 Hz for now */ - - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ)/8000.0); - m_panelWaterfall->setFs(8000.0); - - (wxGetApp().m_plugin_startfp)(wxGetApp().m_plugInStates); - } - } - - if (g_mode != -1) { - // init freedv states - - g_pfreedv = freedv_open(g_mode); - freedv_set_callback_txt(g_pfreedv, &my_put_next_rx_char, &my_get_next_tx_char, NULL); - - freedv_set_callback_error_pattern(g_pfreedv, my_freedv_put_error_pattern, (void*)m_panelTestFrameErrors); - g_error_pattern_fifo = fifo_create(2*freedv_get_sz_error_pattern(g_pfreedv)+1); - g_error_hist = new short[FDMDV_NC_MAX*2]; - g_error_histn = new short[FDMDV_NC_MAX*2]; - int i; - for(i=0; i<2*FDMDV_NC_MAX; i++) { - g_error_hist[i] = 0; - g_error_histn[i] = 0; - } - - assert(g_pfreedv != NULL); - - // init Codec 2 LPC Post Filter - - codec2_set_lpc_post_filter(freedv_get_codec2(g_pfreedv), - wxGetApp().m_codec2LPCPostFilterEnable, - wxGetApp().m_codec2LPCPostFilterBassBoost, - wxGetApp().m_codec2LPCPostFilterBeta, - wxGetApp().m_codec2LPCPostFilterGamma); - - // Init Speex pre-processor states - // by inspecting Speex source it seems that only denoiser is on be default - - g_speex_st = speex_preprocess_state_init(freedv_get_n_speech_samples(g_pfreedv), FS); - - // adjust spectrum and waterfall freq scaling base on mode - - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(freedv_get_modem_sample_rate(g_pfreedv)/2))); - m_panelWaterfall->setFs(freedv_get_modem_sample_rate(g_pfreedv)); - - // Init text msg decoding - - freedv_set_varicode_code_num(g_pfreedv, wxGetApp().m_textEncoding); - } - - modem_stats_open(&g_stats); - g_State = g_prev_State = 0; - g_snr = 0.0; - g_half_duplex = wxGetApp().m_boolHalfDuplex; - - if (g_mode == FREEDV_MODE_800XA) { - m_panelScatter->setEyeScatter(PLOT_SCATTER_MODE_EYE); - } - else { - m_panelScatter->setEyeScatter(PLOT_SCATTER_MODE_SCATTER); - } - - m_pcallsign = m_callsign; - memset(m_callsign, 0, sizeof(m_callsign)); -#ifdef __UDP_EXPERIMENTAL__ - m_checksumGood = m_checksumBad = 0; - wxString s; - s.Printf("%d", m_checksumGood); - m_txtChecksumGood->SetLabel(s); - s.Printf("%d", m_checksumBad); - m_txtChecksumBad->SetLabel(s); -#endif - - m_maxLevel = 0; - m_textLevel->SetLabel(wxT("")); - m_gaugeLevel->SetValue(0); - - //printf("m_textEncoding = %d\n", wxGetApp().m_textEncoding); - //printf("g_stats.snr: %f\n", g_stats.snr_est); - - // attempt to start PTT ...... - - if (wxGetApp().m_boolHamlibUseForPTT) - OpenHamlibRig(); - if (wxGetApp().m_boolUseSerialPTT) { - OpenSerialPort(); - } - - // attempt to start sound cards and tx/rx processing - - startRxStream(); - - if (m_RxRunning) - { -#ifdef _USE_TIMER - m_plotTimer.Start(_REFRESH_TIMER_PERIOD, wxTIMER_CONTINUOUS); -#endif // _USE_TIMER - } -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"start"); processTxtEvent(e); -#endif - } - - // Stop was pressed or start up failed - - if (startStop.IsSameAs("Stop") || !m_RxRunning ) { - - // - // Stop Running ------------------------------------------------- - // - -#ifdef __UDP_EXPERIMENTAL__ - optionsDlg->SetSpamTimerLight(false); -#endif - -#ifdef _USE_TIMER - m_plotTimer.Stop(); -#endif // _USE_TIMER - - // ensure we are not transmitting and shut down audio processing - - if (wxGetApp().m_boolHamlibUseForPTT) { - Hamlib *hamlib = wxGetApp().m_hamlib; - wxString hamlibError; - if (wxGetApp().m_boolHamlibUseForPTT && hamlib != NULL) { - if (hamlib->ptt(false, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - hamlib->close(); - } - } - - if (wxGetApp().m_boolUseSerialPTT) { - CloseSerialPort(); - } - - m_btnTogPTT->SetValue(false); - VoiceKeyerProcessEvent(VK_SPACE_BAR); - - stopRxStream(); - modem_stats_close(&g_stats); - - // free up states, clean up - - if (g_mode == -1) { - // PlugIn clean up - (wxGetApp().m_plugin_stopfp)(wxGetApp().m_plugInStates); - } - else { - // FreeDV clean up - delete g_error_hist; - delete g_error_histn; - fifo_destroy(g_error_pattern_fifo); - freedv_close(g_pfreedv); - speex_preprocess_state_destroy(g_speex_st); - } - - m_newMicInFilter = m_newSpkOutFilter = true; - - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - m_btnTogPTT->Disable(); - m_togBtnVoiceKeyer->Disable(); - m_togBtnOnOff->SetLabel(wxT("Start")); - m_rb1600->Enable(); - //m_rb700b->Enable(); - m_rb700c->Enable(); - m_rb800xa->Enable(); - if (m_rbPlugIn != NULL) - m_rbPlugIn->Enable(); - -#ifdef DISABLED_FEATURE - m_rb700->Enable(); - m_rb1400old->Enable(); - m_rb1600Wide->Enable(); - m_rb1400->Enable(); - m_rb2000->Enable(); -#endif -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"stop"); processTxtEvent(e); -#endif - } -} - -//------------------------------------------------------------------------- -// stopRxStream() -//------------------------------------------------------------------------- -void MainFrame::stopRxStream() -{ - if(m_RxRunning) - { - m_RxRunning = false; - - fprintf(stderr, "waiting for thread to stop\n"); - m_txRxThread->m_run = 0; - m_txRxThread->Wait(); - fprintf(stderr, "thread stopped\n"); - - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(m_rxOutPa != m_rxInPa) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - - if (g_nSoundCards == 2) { - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - if(m_txInPa != m_txOutPa) { - m_txOutPa->stop(); - m_txOutPa->streamClose(); - delete m_txOutPa; - } - } - - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - } -} - -void MainFrame::destroy_fifos(void) -{ - fifo_destroy(g_rxUserdata->infifo1); - fifo_destroy(g_rxUserdata->outfifo1); - fifo_destroy(g_rxUserdata->infifo2); - fifo_destroy(g_rxUserdata->outfifo2); - fifo_destroy(g_rxUserdata->rxinfifo); - fifo_destroy(g_rxUserdata->rxoutfifo); -} - -void MainFrame::destroy_src(void) -{ - src_delete(g_rxUserdata->insrc1); - src_delete(g_rxUserdata->outsrc1); - src_delete(g_rxUserdata->insrc2); - src_delete(g_rxUserdata->outsrc2); - src_delete(g_rxUserdata->insrcsf); -} - -void MainFrame::initPortAudioDevice(PortAudioWrap *pa, int inDevice, int outDevice, - int soundCard, int sampleRate, int inputChannels) -{ - // Note all of the wrapper functions below just set values in a - // portaudio struct so can't return any errors. So no need to trap - // any errors in this function. - - // init input params - - pa->setInputDevice(inDevice); - if(inDevice != paNoDevice) { - pa->setInputChannelCount(inputChannels); // stereo input - pa->setInputSampleFormat(PA_SAMPLE_TYPE); - pa->setInputLatency(pa->getInputDefaultLowLatency()); - pa->setInputHostApiStreamInfo(NULL); - } - - pa->setOutputDevice(paNoDevice); - - // init output params - - pa->setOutputDevice(outDevice); - if(outDevice != paNoDevice) { - pa->setOutputChannelCount(2); // stereo output - pa->setOutputSampleFormat(PA_SAMPLE_TYPE); - pa->setOutputLatency(pa->getOutputDefaultLowLatency()); - pa->setOutputHostApiStreamInfo(NULL); - } - - // init params that affect input and output - - /* - On Linux, setting this to wxGetApp().m_framesPerBuffer caused - intermittant break up on the audio from my IC7200 on Ubuntu 14. - After a day of bug hunting I found that 0, as recommended by the - PortAudio docunmentation, fixed the problem. - */ - - //pa->setFramesPerBuffer(wxGetApp().m_framesPerBuffer); - pa->setFramesPerBuffer(0); - - pa->setSampleRate(sampleRate); - pa->setStreamFlags(paClipOff); -} - -//------------------------------------------------------------------------- -// startRxStream() -//------------------------------------------------------------------------- -void MainFrame::startRxStream() -{ - int src_error; - const PaDeviceInfo *deviceInfo1 = NULL, *deviceInfo2 = NULL; - int inputChannels1, inputChannels2; - bool two_rx=false; - bool two_tx=false; - - if(!m_RxRunning) { - m_RxRunning = true; - - if(Pa_Initialize()) - { - wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK); - } - - m_rxInPa = new PortAudioWrap(); - if(g_soundCard1InDeviceNum != g_soundCard1OutDeviceNum) - two_rx=true; - if(g_soundCard2InDeviceNum != g_soundCard2OutDeviceNum) - two_tx=true; - - fprintf(g_logfile, "two_rx: %d two_tx: %d\n", two_rx, two_tx); - if(two_rx) - m_rxOutPa = new PortAudioWrap(); - else - m_rxOutPa = m_rxInPa; - - if (g_nSoundCards == 0) { - wxMessageBox(wxT("No Sound Cards configured, use Tools - Audio Config to configure"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - - // Init Sound card 1 ---------------------------------------------- - // sanity check on sound card device numbers - - if ((m_rxInPa->getDeviceCount() <= g_soundCard1InDeviceNum) || - (m_rxOutPa->getDeviceCount() <= g_soundCard1OutDeviceNum)) { - wxMessageBox(wxT("Sound Card 1 not present"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - - // work out how many input channels this device supports. - - deviceInfo1 = Pa_GetDeviceInfo(g_soundCard1InDeviceNum); - if (deviceInfo1 == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card 1"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - if (deviceInfo1->maxInputChannels == 1) - inputChannels1 = 1; - else - inputChannels1 = 2; - - if(two_rx) { - initPortAudioDevice(m_rxInPa, g_soundCard1InDeviceNum, paNoDevice, 1, - g_soundCard1SampleRate, inputChannels1); - initPortAudioDevice(m_rxOutPa, paNoDevice, g_soundCard1OutDeviceNum, 1, - g_soundCard1SampleRate, inputChannels1); - } - else - initPortAudioDevice(m_rxInPa, g_soundCard1InDeviceNum, g_soundCard1OutDeviceNum, 1, - g_soundCard1SampleRate, inputChannels1); - - // Init Sound Card 2 ------------------------------------------------ - - if (g_nSoundCards == 2) { - - m_txInPa = new PortAudioWrap(); - if(two_tx) - m_txOutPa = new PortAudioWrap(); - else - m_txOutPa = m_txInPa; - - // sanity check on sound card device numbers - - //printf("m_txInPa->getDeviceCount(): %d\n", m_txInPa->getDeviceCount()); - //printf("g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - //printf("g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - - if ((m_txInPa->getDeviceCount() <= g_soundCard2InDeviceNum) || - (m_txOutPa->getDeviceCount() <= g_soundCard2OutDeviceNum)) { - wxMessageBox(wxT("Sound Card 2 not present"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - m_RxRunning = false; - return; - } - - deviceInfo2 = Pa_GetDeviceInfo(g_soundCard2InDeviceNum); - if (deviceInfo2 == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card 1"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - m_RxRunning = false; - return; - } - if (deviceInfo2->maxInputChannels == 1) - inputChannels2 = 1; - else - inputChannels2 = 2; - - if(two_tx) { - initPortAudioDevice(m_txInPa, g_soundCard2InDeviceNum, paNoDevice, 2, - g_soundCard2SampleRate, inputChannels2); - initPortAudioDevice(m_txOutPa, paNoDevice, g_soundCard2OutDeviceNum, 2, - g_soundCard2SampleRate, inputChannels2); - } - else - initPortAudioDevice(m_txInPa, g_soundCard2InDeviceNum, g_soundCard2OutDeviceNum, 2, - g_soundCard2SampleRate, inputChannels2); - } - - // Init call back data structure ---------------------------------------------- - - g_rxUserdata = new paCallBackData; - g_rxUserdata->inputChannels1 = inputChannels1; - if (deviceInfo2 != NULL) - g_rxUserdata->inputChannels2 = inputChannels2; - - // init sample rate conversion states - - g_rxUserdata->insrc1 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrc1 != NULL); - g_rxUserdata->outsrc1 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->outsrc1 != NULL); - g_rxUserdata->insrc2 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrc2 != NULL); - g_rxUserdata->outsrc2 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->outsrc2 != NULL); - - g_rxUserdata->insrcsf = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrcsf != NULL); - - // create FIFOs used to interface between different buffer sizes - - g_rxUserdata->infifo1 = fifo_create(8*N48); - g_rxUserdata->outfifo1 = fifo_create(10*N48); - g_rxUserdata->outfifo2 = fifo_create(8*N48); - g_rxUserdata->infifo2 = fifo_create(8*N48); - printf("N48: %d\n", N48); - - g_rxUserdata->rxinfifo = fifo_create(10 * N8); - g_rxUserdata->rxoutfifo = fifo_create(10 * N8); - - // Init Equaliser Filters ------------------------------------------------------ - - m_newMicInFilter = m_newSpkOutFilter = true; - designEQFilters(g_rxUserdata); - g_rxUserdata->micInEQEnable = wxGetApp().m_MicInEQEnable; - g_rxUserdata->spkOutEQEnable = wxGetApp().m_SpkOutEQEnable; - - // optional tone in left channel to reliably trigger vox - - g_rxUserdata->leftChannelVoxTone = wxGetApp().m_leftChannelVoxTone; - g_rxUserdata->voxTonePhase = 0; - - // Start sound card 1 ---------------------------------------------------------- - - m_rxInPa->setUserData(g_rxUserdata); - m_rxErr = m_rxInPa->setCallback(rxCallback); - - m_rxErr = m_rxInPa->streamOpen(); - - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Open/Setup error."), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - m_rxErr = m_rxInPa->streamStart(); - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Stream Start Error."), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - // Start separate output stream if needed - - if(two_rx) { - m_rxOutPa->setUserData(g_rxUserdata); - m_rxErr = m_rxOutPa->setCallback(rxCallback); - - m_rxErr = m_rxOutPa->streamOpen(); - - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Second Stream Open/Setup error."), wxT("Error"), wxOK); - delete m_rxInPa; - delete m_rxOutPa; - delete m_txOutPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - m_rxErr = m_rxOutPa->streamStart(); - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Second Stream Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - delete m_rxOutPa; - delete m_txOutPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - } - - // Start sound card 2 ---------------------------------------------------------- - - if (g_nSoundCards == 2) { - - // question: can we use same callback data - // (g_rxUserdata)or both sound card callbacks? Is there a - // chance of them both being called at the same time? We - // could need a mutex ... - - m_txInPa->setUserData(g_rxUserdata); - m_txErr = m_txInPa->setCallback(txCallback); - m_txErr = m_txInPa->streamOpen(); - - if(m_txErr != paNoError) { - fprintf(stderr, "Err: %d\n", m_txErr); - wxMessageBox(wxT("Sound Card 2 Open/Setup error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - m_txErr = m_txInPa->streamStart(); - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - // Start separate output stream if needed - - if (two_tx) { - - // question: can we use same callback data - // (g_rxUserdata)or both sound card callbacks? Is there a - // chance of them both being called at the same time? We - // could need a mutex ... - - m_txOutPa->setUserData(g_rxUserdata); - m_txErr = m_txOutPa->setCallback(txCallback); - m_txErr = m_txOutPa->streamOpen(); - - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Second Stream Open/Setup error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - m_txErr = m_txOutPa->streamStart(); - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Second Stream Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - } - } - - // start tx/rx processing thread - - m_txRxThread = new txRxThread; - - if ( m_txRxThread->Create() != wxTHREAD_NO_ERROR ) - { - wxLogError(wxT("Can't create thread!")); - } - - m_txRxThread->SetPriority(WXTHREAD_MAX_PRIORITY); - - if ( m_txRxThread->Run() != wxTHREAD_NO_ERROR ) - { - wxLogError(wxT("Can't start thread!")); - } - - } -} - - -void MainFrame::processTxtEvent(char event[]) { - int rule = 0; - - //printf("processTxtEvent:\n"); - //printf(" event: %s\n", event); - - // process with regexp and issue system command - - // Each regexp in our list is separated by a newline. We want to try all of them. - - wxString event_str(event); - int match_end, replace_end; - match_end = replace_end = 0; - wxString regexp_match_list = wxGetApp().m_events_regexp_match; - wxString regexp_replace_list = wxGetApp().m_events_regexp_replace; - - bool found_match = false; - - while (((match_end = regexp_match_list.Find('\n')) != wxNOT_FOUND) && (rule < MAX_EVENT_RULES)) { - //printf("match_end: %d\n", match_end); - if ((replace_end = regexp_replace_list.Find('\n')) != wxNOT_FOUND) { - //printf("replace_end = %d\n", replace_end); - - // candidate match and replace regexps strings exist, so lets try them - - wxString regexp_match = regexp_match_list.SubString(0, match_end-1); - wxString regexp_replace = regexp_replace_list.SubString(0, replace_end-1); - //printf("match: %s replace: %s\n", (const char *)regexp_match.c_str(), (const char *)regexp_replace.c_str()); - wxRegEx re(regexp_match); - //printf(" checking for match against: %s\n", (const char *)regexp_match.c_str()); - - // if we found a match, lets run the replace regexp and issue the system command - - wxString event_str_rep = event_str; - - if (re.Replace(&event_str_rep, regexp_replace) != 0) { - fprintf(stderr, " found match! event_str: %s\n", (const char *)event_str.c_str()); - found_match = true; - - bool enableSystem = false; - if (wxGetApp().m_events) - enableSystem = true; - - // no syscall if spam timer still running - - if (spamTimer[rule].IsRunning()) { - enableSystem = false; - fprintf(stderr, " spam timer running\n"); - } - - const char *event_out = event_str_rep.ToUTF8(); - wxString event_out_with_return_code; - - if (enableSystem) { - int ret = wxExecute(event_str_rep); - event_out_with_return_code.Printf(_T("%s -> returned %d"), event_out, ret); - spamTimer[rule].Start((wxGetApp().m_events_spam_timer)*1000, wxTIMER_ONE_SHOT); - } - else - event_out_with_return_code.Printf(_T("%s T: %d"), event_out, spamTimer[rule].IsRunning()); - - // update event log GUI if currently displayed - - if (optionsDlg != NULL) { - optionsDlg->updateEventLog(wxString(event), event_out_with_return_code); - } - } - } - regexp_match_list = regexp_match_list.SubString(match_end+1, regexp_match_list.length()); - regexp_replace_list = regexp_replace_list.SubString(replace_end+1, regexp_replace_list.length()); - - rule++; - } - - if ((optionsDlg != NULL) && !found_match) { - optionsDlg->updateEventLog(wxString(event), _("")); - } -} - - -#define SBQ_MAX_ARGS 4 - -void *MainFrame::designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q) -{ - char *arg[SBQ_MAX_ARGS]; - char argstorage[SBQ_MAX_ARGS][80]; - void *sbq; - int i, argc; - - assert((strcmp(filterType, "bass") == 0) || - (strcmp(filterType, "treble") == 0) || - (strcmp(filterType, "equalizer") == 0)); - - for(i=0; isbqMicInBass = designAnEQFilter("bass", wxGetApp().m_MicInBassFreqHz, wxGetApp().m_MicInBassGaindB); - cb->sbqMicInTreble = designAnEQFilter("treble", wxGetApp().m_MicInTrebleFreqHz, wxGetApp().m_MicInTrebleGaindB); - cb->sbqMicInMid = designAnEQFilter("equalizer", wxGetApp().m_MicInMidFreqHz, wxGetApp().m_MicInMidGaindB, wxGetApp().m_MicInMidQ); - } - - // init Spk Out Equaliser Filters - - if (m_newSpkOutFilter) { - //printf("designing new Spk Out filters\n"); - //printf("designEQFilters: wxGetApp().m_SpkOutBassFreqHz: %f\n",wxGetApp().m_SpkOutBassFreqHz); - cb->sbqSpkOutBass = designAnEQFilter("bass", wxGetApp().m_SpkOutBassFreqHz, wxGetApp().m_SpkOutBassGaindB); - cb->sbqSpkOutTreble = designAnEQFilter("treble", wxGetApp().m_SpkOutTrebleFreqHz, wxGetApp().m_SpkOutTrebleGaindB); - cb->sbqSpkOutMid = designAnEQFilter("equalizer", wxGetApp().m_SpkOutMidFreqHz, wxGetApp().m_SpkOutMidGaindB, wxGetApp().m_SpkOutMidQ); - } -} - -void MainFrame::deleteEQFilters(paCallBackData *cb) -{ - if (m_newMicInFilter) { - sox_biquad_destroy(cb->sbqMicInBass); - sox_biquad_destroy(cb->sbqMicInTreble); - sox_biquad_destroy(cb->sbqMicInMid); - } - if (m_newSpkOutFilter) { - sox_biquad_destroy(cb->sbqSpkOutBass); - sox_biquad_destroy(cb->sbqSpkOutTreble); - sox_biquad_destroy(cb->sbqSpkOutMid); - } -} - -// returns number of output samples generated by resampling -int resample(SRC_STATE *src, - short output_short[], - short input_short[], - int output_sample_rate, - int input_sample_rate, - int length_output_short, // maximum output array length in samples - int length_input_short - ) -{ - SRC_DATA src_data; - float input[N48*4]; - float output[N48*4]; - int ret; - - assert(src != NULL); - assert(length_input_short <= N48*4); - assert(length_output_short <= N48*4); - - src_short_to_float_array(input_short, input, length_input_short); - - src_data.data_in = input; - src_data.data_out = output; - src_data.input_frames = length_input_short; - src_data.output_frames = length_output_short; - src_data.end_of_input = 0; - src_data.src_ratio = (float)output_sample_rate/input_sample_rate; - - ret = src_process(src, &src_data); - assert(ret == 0); - - assert(src_data.output_frames_gen <= length_output_short); - src_float_to_short_array(output, output_short, src_data.output_frames_gen); - - return src_data.output_frames_gen; -} - - -// Decimates samples using an algorithm that produces nice plots of -// speech signals at a low sample rate. We want a low sample rate so -// we don't hammer the graphics system too hard. Saves decimated data -// to a fifo for plotting on screen. -void resample_for_plot(struct FIFO *plotFifo, short buf[], int length, int fs) -{ - int decimation = fs/WAVEFORM_PLOT_FS; - int nSamples, sample; - int i, st, en, max, min; - short dec_samples[length]; - - nSamples = length/decimation; - - for(sample = 0; sample < nSamples; sample += 2) - { - st = decimation*sample; - en = decimation*(sample+2); - max = min = 0; - for(i=st; i buf[i]) min = buf[i]; - } - dec_samples[sample] = max; - dec_samples[sample+1] = min; - } - fifo_write(plotFifo, dec_samples, nSamples); -} - -void txRxProcessing() -{ - - paCallBackData *cbData = g_rxUserdata; - - // Buffers re-used by tx and rx processing - // signals in in48k/out48k are at a maximum sample rate of 48k, could be 44.1kHz - // depending on sound hardware. - - short in8k_short[4*N8]; - short in48k_short[4*N48]; - short out8k_short[4*N8]; - short out48k_short[4*N48]; - int nout, samplerate, n_samples; - - //fprintf(g_logfile, "start infifo1: %5d outfifo2: %5d\n", fifo_used(cbData->infifo1), fifo_used(cbData->outfifo2)); - - // FreeDV 700 uses a modem sample rate of 7500 Hz which requires some special treatment - - if (g_analog || g_mode == -1) - samplerate = FS; - else - samplerate = freedv_get_modem_sample_rate(g_pfreedv); - - // - // RX side processing -------------------------------------------- - // - - // while we have enough input samples available ... - - int nsam = g_soundCard1SampleRate * (float)N8/FS; - assert(nsam <= N48); - g_mutexProtectingCallbackData.Lock(); - while ((fifo_read(cbData->infifo1, in48k_short, nsam) == 0) && ((g_half_duplex && !g_tx) || !g_half_duplex)) - { - g_mutexProtectingCallbackData.Unlock(); - unsigned int n8k; - - n8k = resample(cbData->insrc1, in8k_short, in48k_short, samplerate, g_soundCard1SampleRate, N8, nsam); - assert(n8k <= N8); - - // optionally save "from radio" signal (write demod input to file) - // Really useful for testing and development as it allows us - // to repeat tests using off air signals - - g_mutexProtectingCallbackData.Lock(); - if (g_recFileFromRadio && (g_sfRecFile != NULL)) { - //printf("g_recFromRadioSamples: %d n8k: %d \n", g_recFromRadioSamples); - if (g_recFromRadioSamples < n8k) { - sf_write_short(g_sfRecFile, in8k_short, g_recFromRadioSamples); - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_recFileFromRadioEventId ); - // call stop/start record menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - g_recFromRadioSamples = 0; - //printf("finished recording g_recFromRadioSamples: %d n8k: %d!\n", g_recFileFromRadio, n8k); - } - else { - sf_write_short(g_sfRecFile, in8k_short, n8k); - g_recFromRadioSamples -= n8k; - } - } - g_mutexProtectingCallbackData.Unlock(); - - // optionally read "from radio" signal from file (read demod input from file) - - g_mutexProtectingCallbackData.Lock(); - if (g_playFileFromRadio && (g_sfPlayFileFromRadio != NULL)) { - unsigned int nsf = n8k*g_sfFs/samplerate; - short insf_short[nsf]; - unsigned int n = sf_read_short(g_sfPlayFileFromRadio, insf_short, nsf); - n8k = resample(cbData->insrcsf, in8k_short, insf_short, samplerate, g_sfFs, N8, nsf); - //fprintf(g_logfile, "n: %d nsf: %d n8k: %d samplerate: %d\n", n, nsf, n8k, samplerate); - assert(n8k <= N8); - - if (n == 0) { - if (g_loopPlayFileFromRadio) - sf_seek(g_sfPlayFileFromRadio, 0, SEEK_SET); - else { - printf("playFileFromRadio finished, issuing event!\n"); - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_playFileFromRadioEventId ); - // call stop/start play menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - } - } - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotDemodInFifo, in8k_short, n8k, samplerate); - - if (g_mode != -1) { - // send latest squelch level to FreeDV API, as it handles squelch internally - - freedv_set_squelch_en(g_pfreedv, g_SquelchActive); - freedv_set_snr_squelch_thresh(g_pfreedv, g_SquelchLevel); - } - - // Optional tone interferer - - if (wxGetApp().m_tone) { - float w = 2.0*M_PI*wxGetApp().m_tone_freq_hz/freedv_get_modem_sample_rate(g_pfreedv); - float s; - unsigned int i; - for(i=0; isnr_squelch_thresh); - - // compute rx spectrum - do here so update rate in constant - - COMP rx_fdm[n8k]; - float rx_spec[MODEM_STATS_NSPEC]; - unsigned int i; - - for(i=0; irxinfifo, in8k_short, n8k); - per_frame_rx_processing(cbData->rxoutfifo, cbData->rxinfifo); - memset(out8k_short, 0, sizeof(short)*N8); - fifo_read(cbData->rxoutfifo, out8k_short, N8); - //printf("out8k_short: %d\n", out8k_short[0]); - } - - - // Optional Spk Out EQ Filtering, need mutex as filter can change at run time - g_mutexProtectingCallbackData.Lock(); - if (cbData->spkOutEQEnable) { - sox_biquad_filter(cbData->sbqSpkOutBass, out8k_short, out8k_short, N8); - sox_biquad_filter(cbData->sbqSpkOutTreble, out8k_short, out8k_short, N8); - sox_biquad_filter(cbData->sbqSpkOutMid, out8k_short, out8k_short, N8); - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotSpeechOutFifo, out8k_short, N8, FS); - - g_mutexProtectingCallbackData.Lock(); - if (g_nSoundCards == 1) { - nout = resample(cbData->outsrc2, out48k_short, out8k_short, g_soundCard1SampleRate, FS, N48, N8); - fifo_write(cbData->outfifo1, out48k_short, nout); - } - else { - nout = resample(cbData->outsrc2, out48k_short, out8k_short, g_soundCard2SampleRate, FS, N48, N8); - fifo_write(cbData->outfifo2, out48k_short, nout); - } - } - g_mutexProtectingCallbackData.Unlock(); - - // - // TX side processing -------------------------------------------- - // - - if ((g_mode != -1) && ((g_nSoundCards == 2) && ((g_half_duplex && g_tx) || !g_half_duplex))) { - int ret; - - // Make sure we have q few frames of modulator output - // samples. This also locks the modulator to the sample rate - // of sound card 1. We want to make sure that modulator - // samples are uninterrupted by differences in sample rate - // between this sound card and sound card 2. - - g_mutexProtectingCallbackData.Lock(); - while((unsigned)fifo_used(cbData->outfifo1) < 6*N48) - { - g_mutexProtectingCallbackData.Unlock(); - - int nsam = g_soundCard2SampleRate * freedv_get_n_speech_samples(g_pfreedv)/FS; - assert(nsam <= 4*N48); - - // infifo2 is written to by another sound card so it may - // over or underflow, but we don't realy care. It will - // just result in a short interruption in audio being fed - // to codec2_enc, possibly making a click every now and - // again in the decoded audio at the other end. - - // zero speech input just in case infifo2 underflows - memset(in48k_short, 0, nsam*sizeof(short)); - fifo_read(cbData->infifo2, in48k_short, nsam); - - nout = resample(cbData->insrc2, in8k_short, in48k_short, FS, g_soundCard2SampleRate, 4*N8, nsam); - - // optionally use file for mic input signal - - g_mutexProtectingCallbackData.Lock(); - if (g_playFileToMicIn && (g_sfPlayFile != NULL)) { - int n = sf_read_short(g_sfPlayFile, in8k_short, nout); - //fprintf(stderr, "n: %d nout: %d\n", n, nout); - if (n != nout) { - if (g_loopPlayFileToMicIn) - sf_seek(g_sfPlayFile, 0, SEEK_SET); - else { - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_playFileToMicInEventId ); - // call stop/start play menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - } - } - } - g_mutexProtectingCallbackData.Unlock(); - - // Optional Speex pre-processor for acoustic noise reduction - - if (wxGetApp().m_speexpp_enable) { - speex_preprocess_run(g_speex_st, in8k_short); - } - - // Optional Mic In EQ Filtering, need mutex as filter can change at run time - - g_mutexProtectingCallbackData.Lock(); - if (cbData->micInEQEnable) { - sox_biquad_filter(cbData->sbqMicInBass, in8k_short, in8k_short, nout); - sox_biquad_filter(cbData->sbqMicInTreble, in8k_short, in8k_short, nout); - sox_biquad_filter(cbData->sbqMicInMid, in8k_short, in8k_short, nout); - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotSpeechInFifo, in8k_short, nout, FS); - - n_samples = freedv_get_n_nom_modem_samples(g_pfreedv); - - if (g_analog) { - n_samples = freedv_get_n_speech_samples(g_pfreedv); - - // Boost the "from mic" -> "to radio" audio in analog - // mode. The need for the gain was found by - // experiment - analog SSB sounded too quiet compared - // to digital. With digital voice we generally drive - // the "to radio" (SSB radio mic input) at about 25% - // of the peak level for normal SSB voice. So we - // introduce 6dB gain to make analog SSB sound the - // same level as the digital. Watch out for clipping. - for(int i=0; i 32767) out = 32767.0; - if (out < -32767) out = -32767.0; - out8k_short[i] = out; - } - } - else { - COMP tx_fdm[freedv_get_n_nom_modem_samples(g_pfreedv)]; - COMP tx_fdm_offset[freedv_get_n_nom_modem_samples(g_pfreedv)]; - int i; - - if (g_mode == FREEDV_MODE_800XA) { - /* 800XA doesn't support complex output just yet */ - freedv_tx(g_pfreedv, out8k_short, in8k_short); - } - else { - freedv_comptx(g_pfreedv, tx_fdm, in8k_short); - - freq_shift_coh(tx_fdm_offset, tx_fdm, g_TxFreqOffsetHz, freedv_get_modem_sample_rate(g_pfreedv), &g_TxFreqOffsetPhaseRect, freedv_get_n_nom_modem_samples(g_pfreedv)); - for(i=0; ioutsrc1, out48k_short, out8k_short, g_soundCard1SampleRate, samplerate, N48*4, n_samples); - g_mutexProtectingCallbackData.Lock(); - ret = fifo_write(cbData->outfifo1, out48k_short, nout); - //fprintf(stderr,"nout: %d ret: %d N48*4: %d\n", nout, ret, N48*4); - - assert(ret != -1); - } - g_mutexProtectingCallbackData.Unlock(); - } - - //fprintf(g_logfile, " end infifo1: %5d outfifo2: %5d\n", fifo_used(cbData->infifo1), fifo_used(cbData->outfifo2)); - -} - -//---------------------------------------------------------------- -// per_frame_rx_processing() -//---------------------------------------------------------------- - -void per_frame_rx_processing( - FIFO *output_fifo, // decoded speech samples - FIFO *input_fifo - ) -{ - #ifdef OLDSPEC - float rx_spec[MODEM_STATS_NSPEC]; - #endif - int i; - - if (g_mode == -1) { - // PlugIn processing --------------------------------------------------- - - int nin = 160; // TODO: hard code for now - some sort of plugin supplied param in future - short input_buf[nin]; - - while (fifo_read(input_fifo, input_buf, nin) == 0) { - (wxGetApp().m_plugin_rx_samplesfp)(wxGetApp().m_plugInStates, input_buf, nin); - } - - #ifdef OLD_SPEC - COMP rx_fdm[nin]; - - for(i=0; isnr); - - // grab extended stats so we can plot spectrum, scatter diagram etc - - freedv_get_modem_extended_stats(g_pfreedv, &g_stats); - - #ifdef OLD_SPEC - // compute rx spectrum - - modem_stats_get_rx_spectrum(&g_stats, rx_spec, rx_fdm, nin_prev); - - // Average rx spectrum data using a simple IIR low pass filter - - for(i = 0; iinputChannels1) - indata[i] = rptr[0]; - if (fifo_write(cbData->infifo1, indata, framesPerBuffer)) { - //fprintf(g_logfile, "infifo1 full\n"); - } - //fifo_write(cbData->outfifo1, indata, framesPerBuffer); - } - - // OK now set up output samples for this callback - - if(wptr) { - //fprintf(g_logfile,"out %ld %d\n", framesPerBuffer, g_out++); - if (fifo_read(cbData->outfifo1, outdata, framesPerBuffer) == 0) { - - // write signal to both channels - - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - if (cbData->leftChannelVoxTone) { - cbData->voxTonePhase += 2.0*M_PI*VOX_TONE_FREQ/g_soundCard1SampleRate; - cbData->voxTonePhase -= 2.0*M_PI*floor(cbData->voxTonePhase/(2.0*M_PI)); - wptr[0] = VOX_TONE_AMP*cos(cbData->voxTonePhase); - //printf("%f %d\n", cbData->voxTonePhase, wptr[0]); - } - else - wptr[0] = outdata[i]; - - wptr[1] = outdata[i]; - } - } - else { - //fprintf(g_logfile, "outfifo1 empty\n"); - // zero output if no data available - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = 0; - wptr[1] = 0; - } - } - } - - return paContinue; -} - - -//------------------------------------------------------------------------- -// txCallback() -//------------------------------------------------------------------------- -int MainFrame::txCallback( - const void *inputBuffer, - void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ) -{ - paCallBackData *cbData = (paCallBackData*)userData; - unsigned int i; - short *rptr = (short*)inputBuffer; - short *wptr = (short*)outputBuffer; - short indata[MAX_FPB]; - short outdata[MAX_FPB]; - - wxMutexLocker lock(g_mutexProtectingCallbackData); - - // if (statusFlags) - // printf("cb2 statusFlags: 0x%x\n", (int)statusFlags); - - // assemble a mono buffer and write to FIFO - - assert(framesPerBuffer < MAX_FPB); - - if(rptr) { - for(i = 0; i < framesPerBuffer; i++, rptr += cbData->inputChannels2) - indata[i] = rptr[0]; - } - - //#define SC2_LOOPBACK -#ifdef SC2_LOOPBACK - //TODO: This doesn't work unless using the same soundcard! - - if(wptr) { - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = indata[i]; - wptr[1] = indata[i]; - } - } - -#else - if(rptr) { - if (fifo_write(cbData->infifo2, indata, framesPerBuffer)) { - //fprintf(g_logfile, "infifo2 full\n"); - } - } - - // OK now set up output samples for this callback - - if(wptr) { - if (fifo_read(cbData->outfifo2, outdata, framesPerBuffer) == 0) { - - // write signal to both channels */ - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = outdata[i]; - wptr[1] = outdata[i]; - } - } - else { - //fprintf(g_logfile, "outfifo2 empty\n"); - // zero output if no data available - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = 0; - wptr[1] = 0; - } - } - } -#endif - return paContinue; -} - -// Callback from plot_spectrum & plot_waterfall. would be nice to -// work out a way to do this without globals. - -void fdmdv2_clickTune(float freq) { - - // The demod is hard-wired to expect a centre frequency of - // FDMDV_FCENTRE. So we want to take the signal centered on the - // click tune freq and re-centre it on FDMDV_FCENTRE. For example - // if the click tune freq is 1500Hz, and FDMDV_CENTRE is 1200 Hz, - // we need to shift the input signal centred on 1500Hz down to - // 1200Hz, an offset of -300Hz. - - // Bit of an "indent" as we are often trying to get it back - // exactly in the centre - - if (fabs(FDMDV_FCENTRE - freq) < 10.0) { - freq = FDMDV_FCENTRE; - fprintf(stderr, "indent!\n"); - } - - if (g_split) { - g_RxFreqOffsetHz = FDMDV_FCENTRE - freq; - } - else { - g_TxFreqOffsetHz = freq - FDMDV_FCENTRE; - g_RxFreqOffsetHz = FDMDV_FCENTRE - freq; - } -} - -//---------------------------------------------------------------- -// OpenSerialPort() -//---------------------------------------------------------------- - -void MainFrame::OpenSerialPort(void) -{ - Serialport *serialport = wxGetApp().m_serialport; - - if(!wxGetApp().m_strRigCtrlPort.IsEmpty()) { - serialport->openport(wxGetApp().m_strRigCtrlPort.c_str(), - wxGetApp().m_boolUseRTS, - wxGetApp().m_boolRTSPos, - wxGetApp().m_boolUseDTR, - wxGetApp().m_boolDTRPos); - if (serialport->isopen()) { - // always start PTT in Rx state - serialport->ptt(false); - } - else { - wxMessageBox("Couldn't open Serial Port", wxT("About"), wxOK | wxICON_ERROR, this); - } - } -} - - -//---------------------------------------------------------------- -// CloseSerialPort() -//---------------------------------------------------------------- - -void MainFrame::CloseSerialPort(void) -{ - Serialport *serialport = wxGetApp().m_serialport; - if (serialport->isopen()) { - // always end with PTT in rx state - - serialport->ptt(false); - serialport->closeport(); - } -} - - -#ifdef __UDP_SUPPORT__ - -//---------------------------------------------------------------- -// PollUDP() - see if any commands on UDP port -//---------------------------------------------------------------- - -// test this puppy with netcat: -// $ echo "hello" | nc -u -q1 localhost 3000 - -int MainFrame::PollUDP(void) -{ - // this will block until message received, so we put it in it's own thread - - char buf[1024]; - char reply[80]; - size_t n = m_udp_sock->RecvFrom(m_udp_addr, buf, sizeof(buf)).LastCount(); - - if (n) { - wxString bufstr = wxString::From8BitData(buf, n); - bufstr.Trim(); - wxString ipaddr = m_udp_addr.IPAddress(); - printf("Received: \"%s\" from %s:%u\n", - (const char *)bufstr.c_str(), - (const char *)ipaddr.c_str(), m_udp_addr.Service()); - - // for security only accept commands from local host - - sprintf(reply,"nope\n"); - if (ipaddr.Cmp(_("127.0.0.1")) == 0) { - - // process commands - - if (bufstr.Cmp(_("restore")) == 0) { - m_schedule_restore = true; // Make Restore happen in main thread to avoid crashing - sprintf(reply,"ok\n"); - } - - wxString itemToSet, val; - if (bufstr.StartsWith(_("set "), &itemToSet)) { - if (itemToSet.StartsWith("txtmsg ", &val)) { - // note: if options dialog is open this will get overwritten - wxGetApp().m_callSign = val; - } - sprintf(reply,"ok\n"); - } - if (bufstr.StartsWith(_("ptton"), &itemToSet)) { - // note: if options dialog is open this will get overwritten - m_btnTogPTT->SetValue(true); - togglePTT(); - sprintf(reply,"ok\n"); - } - if (bufstr.StartsWith(_("pttoff"), &itemToSet)) { - // note: if options dialog is open this will get overwritten - m_btnTogPTT->SetValue(false); - togglePTT(); - sprintf(reply,"ok\n"); - } - - } - else { - printf("We only accept messages from locahost!\n"); - } - - if ( m_udp_sock->SendTo(m_udp_addr, reply, strlen(reply)).LastCount() != strlen(reply)) { - printf("ERROR: failed to send data\n"); - } - } - - return n; -} - -void MainFrame::startUDPThread(void) { - fprintf(stderr, "starting UDP thread!\n"); - m_UDPThread = new UDPThread; - m_UDPThread->mf = this; - if (m_UDPThread->Create() != wxTHREAD_NO_ERROR ) { - wxLogError(wxT("Can't create thread!")); - } - if (m_UDPThread->Run() != wxTHREAD_NO_ERROR ) { - wxLogError(wxT("Can't start thread!")); - delete m_UDPThread; - } -} - -void MainFrame::stopUDPThread(void) { - printf("stopping UDP thread!\n"); - if ((m_UDPThread != NULL) && m_UDPThread->m_run) { - m_UDPThread->m_run = 0; - m_UDPThread->Wait(); - m_UDPThread = NULL; - } -} - -void *UDPThread::Entry() { - printf("UDP thread started!\n"); - while (m_run) { - if (wxGetApp().m_udp_enable) { - printf("m_udp_enable\n"); - mf->m_udp_addr.Service(wxGetApp().m_udp_port); - mf->m_udp_sock = new wxDatagramSocket(mf->m_udp_addr, wxSOCKET_NOWAIT); - - while (m_run && wxGetApp().m_udp_enable) { - if (mf->PollUDP() == 0) { - wxThread::Sleep(20); - } - } - - delete mf->m_udp_sock; - } - wxThread::Sleep(20); - } - return NULL; -} - -#endif - -char my_get_next_tx_char(void *callback_state) { - short ch = 0; - - fifo_read(g_txDataInFifo, &ch, 1); - //fprintf(stderr, "get_next_tx_char: %c\n", (char)ch); - return (char)ch; -} - -void my_put_next_rx_char(void *callback_state, char c) { - short ch = (short)c; - //fprintf(stderr, "put_next_rx_char: %c\n", (char)c); - fifo_write(g_rxDataOutFifo, &ch, 1); -} - -// Callback from FreeDv API to update error plots - -void my_freedv_put_error_pattern(void *state, short error_pattern[], int sz_error_pattern) { - fifo_write(g_error_pattern_fifo, error_pattern, sz_error_pattern); - //fprintf(stderr, "my_freedv_put_error_pattern: sz_error_pattern: %d ret: %d used: %d\n", - // sz_error_pattern, ret, fifo_used(g_error_pattern_fifo) ); -} - -void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin) -{ - COMP foff_rect; - float mag; - int i; - - foff_rect.real = cosf(2.0*M_PI*foff/Fs); - foff_rect.imag = sinf(2.0*M_PI*foff/Fs); - for(i=0; ireal /= mag; - foff_phase_rect->imag /= mag; -} - -int plugin_get_persistant(char name[], char value[]) { - wxString n,v; - int i; - - for(i=0; i. -// -//========================================================================== -#ifndef __FDMDV2_MAIN__ -#define __FDMDV2_MAIN__ - -#include "version.h" -#ifndef _NO_AUTOTOOLS_ -#include "../config.h" -#endif -#include - -#include -#include -#include "wx/rawbmp.h" -#include "wx/file.h" -#include "wx/filename.h" -#include "wx/config.h" -#include -#include "wx/graphics.h" -#include "wx/mstream.h" -#include "wx/wfstream.h" -#include "wx/quantize.h" -#include "wx/scopedptr.h" -#include "wx/stopwatch.h" -#include "wx/versioninfo.h" -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -#include "codec2.h" -#include "codec2_fifo.h" -#include "modem_stats.h" - -#include "topFrame.h" -#include "dlg_ptt.h" -#include "dlg_options.h" -#include "fdmdv2_plot.h" -#include "fdmdv2_plot_scalar.h" -#include "fdmdv2_plot_scatter.h" -#include "fdmdv2_plot_waterfall.h" -#include "fdmdv2_plot_spectrum.h" -#include "fdmdv2_pa_wrapper.h" -#include "sndfile.h" -#include "portaudio.h" -#include "dlg_audiooptions.h" -#include "dlg_filter.h" -#include "dlg_options.h" -#include "varicode.h" -#include "sox_biquad.h" -#include "comp_prim.h" -#include "dlg_plugin.h" -#include "hamlib.h" -#include "serialport.h" - -#define _USE_TIMER 1 -#define _USE_ONIDLE 1 -#define _DUMMY_DATA 1 -//#define _AUDIO_PASSTHROUGH 1 -#define _REFRESH_TIMER_PERIOD (DT*1000) -//#define _USE_ABOUT_DIALOG 1 - -enum { - ID_START = wxID_HIGHEST, - ID_TIMER_WATERFALL, - ID_TIMER_SPECTRUM, - ID_TIMER_SCATTER, - ID_TIMER_SCALAR - }; - -#define EXCHANGE_DATA_IN 0 -#define EXCHANGE_DATA_OUT 1 - - -extern int g_nSoundCards; -extern int g_soundCard1InDeviceNum; -extern int g_soundCard1OutDeviceNum; -extern int g_soundCard1SampleRate; -extern int g_soundCard2InDeviceNum; -extern int g_soundCard2OutDeviceNum; -extern int g_soundCard2SampleRate; - -// Voice Keyer Constants - -#define VK_SYNC_WAIT_TIME 5.0 - -// Voice Keyer States - -#define VK_IDLE 0 -#define VK_TX 1 -#define VK_RX 2 -#define VK_SYNC_WAIT 3 - -// Voice Keyer Events - -#define VK_START 0 -#define VK_SPACE_BAR 1 -#define VK_PLAY_FINISHED 2 -#define VK_DT 3 -#define VK_SYNC 4 - -class MainFrame; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainApp -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MainApp : public wxApp -{ - public: - virtual bool OnInit(); - virtual int OnExit(); - - wxString m_strVendName; - wxString m_StrAppName; - - wxString m_textNumChOut; - wxString m_textNumChIn; - - wxString m_strRxInAudio; - wxString m_strRxOutAudio; - wxString m_textVoiceInput; - wxString m_textVoiceOutput; - wxString m_strSampleRate; - wxString m_strBitrate; - - // PTT ----------------------------------- - - bool m_boolHalfDuplex; - - wxString m_txtVoiceKeyerWaveFilePath; - wxString m_txtVoiceKeyerWaveFile; - int m_intVoiceKeyerRxPause; - int m_intVoiceKeyerRepeats; - - bool m_boolHamlibUseForPTT; - unsigned int m_intHamlibRig; - wxString m_strHamlibSerialPort; - unsigned int m_intHamlibSerialRate; - Hamlib *m_hamlib; - - bool m_boolUseSerialPTT; - wxString m_strRigCtrlPort; - bool m_boolUseRTS; - bool m_boolRTSPos; - bool m_boolUseDTR; - bool m_boolDTRPos; - Serialport *m_serialport; - - // Play/Rec files - - wxString m_playFileToMicInPath; - wxString m_recFileFromRadioPath; - unsigned int m_recFileFromRadioSecs; - wxString m_playFileFromRadioPath; - - // Options dialog - - wxString m_callSign; - bool m_events; - int m_events_spam_timer; - unsigned int m_textEncoding; - bool m_enable_checksum; - wxString m_events_regexp_match; - wxString m_events_regexp_replace; - - bool m_snrSlow; - - // LPC Post Filter - bool m_codec2LPCPostFilterEnable; - bool m_codec2LPCPostFilterBassBoost; - float m_codec2LPCPostFilterGamma; - float m_codec2LPCPostFilterBeta; - - // Speex Pre-Processor - bool m_speexpp_enable; - - // Mic In Equaliser - float m_MicInBassFreqHz; - float m_MicInBassGaindB; - float m_MicInTrebleFreqHz; - float m_MicInTrebleGaindB; - float m_MicInMidFreqHz; - float m_MicInMidGaindB; - float m_MicInMidQ; - bool m_MicInEQEnable; - - // Spk Out Equaliser - float m_SpkOutBassFreqHz; - float m_SpkOutBassGaindB; - float m_SpkOutTrebleFreqHz; - float m_SpkOutTrebleGaindB; - float m_SpkOutMidFreqHz; - float m_SpkOutMidGaindB; - float m_SpkOutMidQ; - bool m_SpkOutEQEnable; - - // Flags for displaying windows - int m_show_wf; - int m_show_spect; - int m_show_scatter; - int m_show_timing; - int m_show_freq; - int m_show_speech_in; - int m_show_speech_out; - int m_show_demod_in; - int m_show_test_frame_errors; - int m_show_test_frame_errors_hist; - - // optional vox trigger tone - bool m_leftChannelVoxTone; - - // UDP control port - bool m_udp_enable; - int m_udp_port; - - // notebook display after tx->rxtransition - int m_rxNbookCtrl; - - wxRect m_rTopWindow; - - int m_framesPerBuffer; - - bool loadConfig(); - bool saveConfig(); - - // Plugins ----------------------------------- - - wxString m_txtPlugInParam[PLUGIN_MAX_PARAMS]; - - // plugin details - - void *m_plugInHandle; - bool m_plugIn; - wxString m_plugInName; - int m_numPlugInParam; - wxString m_plugInParamName[PLUGIN_MAX_PARAMS]; - void *m_plugInStates; - void (*m_plugin_startfp)(void *); - void (*m_plugin_stopfp)(void *); - void (*m_plugin_rx_samplesfp)(void *, short *, int); - - // misc - - bool m_testFrames; - bool m_channel_noise; - float m_channel_snr_dB; - - int FilterEvent(wxEvent& event); - MainFrame *frame; - - // 700 options - - bool m_FreeDV700txClip; - bool m_FreeDV700Combine; - - // Noise simulation - - int m_noise_snr; - - // carrier attenuation - - bool m_attn_carrier_en; - int m_attn_carrier; - - // tone interferer simulation - - bool m_tone; - int m_tone_freq_hz; - int m_tone_amplitude; - - // Windows debug console - - bool m_debug_console; - protected: -}; - -// declare global static function wxGetApp() -DECLARE_APP(MainApp) - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// paCallBackData -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -typedef struct -{ - // libresample states for 48 to 8 kHz conversions - - SRC_STATE *insrc1; - SRC_STATE *outsrc1; - SRC_STATE *insrc2; - SRC_STATE *outsrc2; - SRC_STATE *insrcsf; - - // FIFOs attached to first sound card - - struct FIFO *infifo1; - struct FIFO *outfifo1; - - // FIFOs attached to second sound card - struct FIFO *infifo2; - struct FIFO *outfifo2; - - // FIFOs for rx process - struct FIFO *rxinfifo; - struct FIFO *rxoutfifo; - - int inputChannels1, inputChannels2; - - // EQ filter states - void *sbqMicInBass; - void *sbqMicInTreble; - void *sbqMicInMid; - void *sbqSpkOutBass; - void *sbqSpkOutTreble; - void *sbqSpkOutMid; - - bool micInEQEnable; - bool spkOutEQEnable; - - // optional loud tone on left channel to reliably trigger vox - bool leftChannelVoxTone; - float voxTonePhase; - -} paCallBackData; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// panel with custom loop checkbox for play file dialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MyExtraPlayFilePanel : public wxPanel -{ -public: - MyExtraPlayFilePanel(wxWindow *parent); - void setLoopPlayFileToMicIn(bool checked) { m_cb->SetValue(checked); } - bool getLoopPlayFileToMicIn(void) { return m_cb->GetValue(); } -private: - wxCheckBox *m_cb; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// panel with custom Seconds-to-record control for record file dialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MyExtraRecFilePanel : public wxPanel -{ -public: - MyExtraRecFilePanel(wxWindow *parent); - ~MyExtraRecFilePanel() - { - wxLogDebug("Destructor\n"); - } - void setSecondsToRecord(wxString value) { m_secondsToRecord->SetValue(value); } - wxString getSecondsToRecord(void) - { - wxLogDebug("getSecondsToRecord: %s\n",m_secondsToRecord->GetValue()); - return m_secondsToRecord->GetValue(); - } -private: - wxTextCtrl *m_secondsToRecord; -}; - -class txRxThread; -class UDPThread; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainFrame -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MainFrame : public TopFrame -{ - public: - MainFrame(wxString plugInName, wxWindow *parent); - virtual ~MainFrame(); - - PlotSpectrum* m_panelSpectrum; - PlotWaterfall* m_panelWaterfall; - PlotScatter* m_panelScatter; - PlotScalar* m_panelTimeOffset; - PlotScalar* m_panelFreqOffset; - PlotScalar* m_panelSpeechIn; - PlotScalar* m_panelSpeechOut; - PlotScalar* m_panelDemodIn; - PlotScalar* m_panelTestFrameErrors; - PlotScalar* m_panelTestFrameErrorsHist; - - bool m_RxRunning; - - PortAudioWrap *m_rxInPa; - PortAudioWrap *m_rxOutPa; - PortAudioWrap *m_txInPa; - PortAudioWrap *m_txOutPa; - - PaError m_rxErr; - PaError m_txErr; - - txRxThread* m_txRxThread; - - bool OpenHamlibRig(); - void OpenSerialPort(void); - void CloseSerialPort(void); - void SerialPTTRx(void); - - bool m_modal; - -#ifdef _USE_TIMER - wxTimer m_plotTimer; -#endif - - void destroy_fifos(void); - void destroy_src(void); - void autoDetectSoundCards(PortAudioWrap *pa); - - static int rxCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); - - static int txCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); - - - void initPortAudioDevice(PortAudioWrap *pa, int inDevice, int outDevice, - int soundCard, int sampleRate, int inputChannels); - - void togglePTT(void); - - wxIPV4address m_udp_addr; - wxDatagramSocket *m_udp_sock; - UDPThread *m_UDPThread; - void startUDPThread(void); - void stopUDPThread(void); - int PollUDP(); - bool m_schedule_restore; - - int vk_state; - void VoiceKeyerProcessEvent(int vk_event); - - protected: - - void setsnrBeta(bool snrSlow); - - // protected event handlers - virtual void OnCloseFrame(wxCloseEvent& event); - void OnExitClick(wxCommandEvent& event); - - void startTxStream(); - void startRxStream(); - void stopTxStream(); - void stopRxStream(); - void abortTxStream(); - void abortRxStream(); - - void OnTop(wxCommandEvent& event); - void OnExit( wxCommandEvent& event ); - - void OnToolsAudio( wxCommandEvent& event ); - void OnToolsAudioUI( wxUpdateUIEvent& event ); - void OnToolsComCfg( wxCommandEvent& event ); - void OnToolsComCfgUI( wxUpdateUIEvent& event ); - void OnToolsFilter( wxCommandEvent& event ); - void OnToolsOptions(wxCommandEvent& event); - void OnToolsOptionsUI(wxUpdateUIEvent& event); - - void OnToolsPlugInCfg( wxCommandEvent& event ); - void OnToolsPlugInCfgUI( wxUpdateUIEvent& event ); - - void OnPlayFileToMicIn( wxCommandEvent& event ); - void StopPlayFileToMicIn(void); - void OnRecFileFromRadio( wxCommandEvent& event ); - void OnPlayFileFromRadio( wxCommandEvent& event ); - - void OnHelpCheckUpdates( wxCommandEvent& event ); - void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ); - void OnHelpAbout( wxCommandEvent& event ); - void OnCmdSliderScroll( wxScrollEvent& event ); -// void OnSliderScrollBottom( wxScrollEvent& event ); -// void OnCmdSliderScrollChanged( wxScrollEvent& event ); -// void OnSliderScrollTop( wxScrollEvent& event ); - void OnCheckSQClick( wxCommandEvent& event ); - void OnCheckSNRClick( wxCommandEvent& event ); - - // Toggle Buttons - void OnTogBtnSplitClick(wxCommandEvent& event); - void OnTogBtnAnalogClick(wxCommandEvent& event); - void OnTogBtnRxID( wxCommandEvent& event ); - void OnTogBtnTxID( wxCommandEvent& event ); - void OnTogBtnPTT( wxCommandEvent& event ); - void OnTogBtnVoiceKeyerClick (wxCommandEvent& event); - void OnTogBtnOnOff( wxCommandEvent& event ); - - void OnCallSignReset( wxCommandEvent& event ); - void OnBerReset( wxCommandEvent& event ); - - //System Events - void OnPaint(wxPaintEvent& event); - void OnSize( wxSizeEvent& event ); - void OnUpdateUI( wxUpdateUIEvent& event ); - void OnDeleteConfig(wxCommandEvent&); -#ifdef _USE_TIMER - void OnTimer(wxTimerEvent &evt); -#endif -#ifdef _USE_ONIDLE - void OnIdle(wxIdleEvent &evt); -#endif - - int VoiceKeyerStartTx(void); - - private: - bool m_useMemory; - wxTextCtrl* m_tc; - int m_zoom; - float m_snrBeta; - - // Callsign/text messaging - char m_callsign[MAX_CALLSIGN]; - char *m_pcallsign; - unsigned int m_checksumGood; - unsigned int m_checksumBad; - - // Events - void processTxtEvent(char event[]); - class OptionsDlg *optionsDlg; - wxTimer spamTimer[MAX_EVENT_RULES]; - - // level Gauge - float m_maxLevel; - - // flags to indicate when new EQ filters need to be designed - - bool m_newMicInFilter; - bool m_newSpkOutFilter; - - void* designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q = 0.0); - void designEQFilters(paCallBackData *cb); - void deleteEQFilters(paCallBackData *cb); - - // Voice Keyer States - - int vk_rx_pause; - int vk_repeats, vk_repeat_counter; - float vk_rx_time; -}; - -void txRxProcessing(); - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class txRxThread - experimental tx/rx processing thread -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class txRxThread : public wxThread -{ -public: - txRxThread(void) : wxThread(wxTHREAD_JOINABLE) { m_run = 1; } - - // thread execution starts here - void *Entry() - { - while (m_run) - { - txRxProcessing(); - wxThread::Sleep(20); - } - return NULL; - } - - // called when the thread exits - whether it terminates normally or is - // stopped with Delete() (but not when it is Kill()ed!) - void OnExit() { } - -public: - bool m_run; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class UDPThread - waits for UDP messages -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class UDPThread : public wxThread -{ -public: - UDPThread(void) : wxThread(wxTHREAD_JOINABLE) { m_run = 1; } - - // thread execution starts here - void *Entry(); - - // called when the thread exits - whether it terminates normally or is - // stopped with Delete() (but not when it is Kill()ed!) - void OnExit() { } - -public: - MainFrame *mf; - bool m_run; -}; - -void resample_for_plot(struct FIFO *plotFifo, short buf[], int length, int fs); - -int resample(SRC_STATE *src, - short output_short[], - short input_short[], - int output_sample_rate, - int input_sample_rate, - int length_output_short, // maximum output array length in samples - int length_input_short - ); -void txRxProcessing(); -void per_frame_rx_processing( - FIFO *output_fifo, // decoded speech samples - FIFO *input_fifo // modem samples input to demod - ); - -// FreeDv API calls this when there is a test frame that needs a-plottin' - -void my_freedv_put_error_pattern(void *state, short error_pattern[], int sz_error_pattern); - -// FreeDv API calls these puppies when it needs/receives a text char - -char my_get_next_tx_char(void *callback_state); -void my_put_next_rx_char(void *callback_state, char c); - -// helper complex freq shift function - -void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin); - -// Helper function called by plugin - -int plugin_get_persistant(char name[], char value[]); - -#endif //__FDMDV2_MAIN__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_pa_wrapper.cpp b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_pa_wrapper.cpp deleted file mode 100644 index 2f67ca26..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_pa_wrapper.cpp +++ /dev/null @@ -1,324 +0,0 @@ -//========================================================================== -// Name: fdmdv2_pa_wrapper.cpp -// Purpose: Implements a wrapper class around the PortAudio library. -// Created: August 12, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "fdmdv2_pa_wrapper.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// PortAudioWrap() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PortAudioWrap::PortAudioWrap() -{ - m_pStream = NULL; - m_pUserData = NULL; - m_samplerate = 0; - m_framesPerBuffer = 0; - m_statusFlags = 0; - m_pStreamCallback = NULL; - m_pStreamFinishedCallback = NULL; - m_pTimeInfo = 0; - m_newdata = false; - -// loadData(); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// ~PortAudioWrap() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PortAudioWrap::~PortAudioWrap() -{ -} - -//---------------------------------------------------------------- -// streamOpen() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamOpen() -{ - return Pa_OpenStream( - &m_pStream, - m_inputBuffer.device == paNoDevice ? NULL : &m_inputBuffer, - m_outputBuffer.device == paNoDevice ? NULL : &m_outputBuffer, - m_samplerate, - m_framesPerBuffer, - m_statusFlags, - *m_pStreamCallback, - m_pUserData - ); -} - -//---------------------------------------------------------------- -// streamStart() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamStart() -{ - return Pa_StartStream(m_pStream); -} - -//---------------------------------------------------------------- -// streamClose() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamClose() -{ - if(isOpen()) - { - PaError rv = Pa_CloseStream(m_pStream); - return rv; - } - else - { - return paNoError; - } -} - -//---------------------------------------------------------------- -// terminate() -//---------------------------------------------------------------- -void PortAudioWrap::terminate() -{ - if(Pa_IsStreamStopped(m_pStream) != paNoError) - { - Pa_StopStream(m_pStream); - } - Pa_Terminate(); -} - -//---------------------------------------------------------------- -// stop() -//---------------------------------------------------------------- -void PortAudioWrap::stop() -{ - Pa_StopStream(m_pStream); -} - -//---------------------------------------------------------------- -// abort() -//---------------------------------------------------------------- -void PortAudioWrap::abort() -{ - Pa_AbortStream(m_pStream); -} - -//---------------------------------------------------------------- -// isStopped() -//---------------------------------------------------------------- -bool PortAudioWrap::isStopped() const -{ - PaError ret = Pa_IsStreamStopped(m_pStream); - return ret; -} - -//---------------------------------------------------------------- -// isActive() -//---------------------------------------------------------------- -bool PortAudioWrap::isActive() const -{ - PaError ret = Pa_IsStreamActive(m_pStream); - return ret; -} - -//---------------------------------------------------------------- -// isOpen() -//---------------------------------------------------------------- -bool PortAudioWrap::isOpen() const -{ - return (m_pStream != NULL); -} - -//---------------------------------------------------------------- -// getDefaultInputDevice() -//---------------------------------------------------------------- -PaDeviceIndex PortAudioWrap::getDefaultInputDevice() -{ - return Pa_GetDefaultInputDevice(); -} - -//---------------------------------------------------------------- -// getDefaultOutputDevice() -//---------------------------------------------------------------- -PaDeviceIndex PortAudioWrap::getDefaultOutputDevice() -{ - return Pa_GetDefaultOutputDevice(); -} - -//---------------------------------------------------------------- -// setInputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputChannelCount(int count) -{ - m_inputBuffer.channelCount = count; - return paNoError; -} - -//---------------------------------------------------------------- -// getInputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::getInputChannelCount() -{ - return m_inputBuffer.channelCount; -} - -//---------------------------------------------------------------- -// setInputSampleFormat() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputSampleFormat(PaSampleFormat format) -{ - m_inputBuffer.sampleFormat = format; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputLatency() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputLatency(PaTime latency) -{ - m_inputBuffer.suggestedLatency = latency; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputHostApiStreamInfo() -//---------------------------------------------------------------- -void PortAudioWrap::setInputHostApiStreamInfo(void *info) -{ - m_inputBuffer.hostApiSpecificStreamInfo = info; -} - -//---------------------------------------------------------------- -// getInputDefaultLowLatency() -//---------------------------------------------------------------- -PaTime PortAudioWrap::getInputDefaultLowLatency() -{ - return Pa_GetDeviceInfo(m_inputBuffer.device)->defaultLowInputLatency; -} - -//---------------------------------------------------------------- -// setOutputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputChannelCount(int count) -{ - m_outputBuffer.channelCount = count; - return paNoError; -} - -//---------------------------------------------------------------- -// getOutputChannelCount() -//---------------------------------------------------------------- -const int PortAudioWrap::getOutputChannelCount() -{ - return m_outputBuffer.channelCount; -} - -//---------------------------------------------------------------- -// getDeviceName() -//---------------------------------------------------------------- -const char *PortAudioWrap::getDeviceName(PaDeviceIndex dev) -{ - const PaDeviceInfo *info; - info = Pa_GetDeviceInfo(dev); - return info->name; -} - -//---------------------------------------------------------------- -// setOutputSampleFormat() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputSampleFormat(PaSampleFormat format) -{ - m_outputBuffer.sampleFormat = format; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputLatency() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputLatency(PaTime latency) -{ - m_outputBuffer.suggestedLatency = latency; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputHostApiStreamInfo() -//---------------------------------------------------------------- -void PortAudioWrap::setOutputHostApiStreamInfo(void *info) -{ - m_outputBuffer.hostApiSpecificStreamInfo = info; -} - -//---------------------------------------------------------------- -// getOutputDefaultLowLatency() -//---------------------------------------------------------------- -PaTime PortAudioWrap::getOutputDefaultLowLatency() -{ - return Pa_GetDeviceInfo(m_outputBuffer.device)->defaultLowOutputLatency; -} - -//---------------------------------------------------------------- -// setFramesPerBuffer() -//---------------------------------------------------------------- -PaError PortAudioWrap::setFramesPerBuffer(unsigned long size) -{ - m_framesPerBuffer = size; - return paNoError; -} - -//---------------------------------------------------------------- -// setSampleRate() -//---------------------------------------------------------------- -PaError PortAudioWrap::setSampleRate(unsigned long rate) -{ - m_samplerate = rate; - return paNoError; -} - -//---------------------------------------------------------------- -// setStreamFlags() -//---------------------------------------------------------------- -PaError PortAudioWrap::setStreamFlags(PaStreamFlags flags) -{ - m_statusFlags = flags; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputDevice() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputDevice(PaDeviceIndex index) -{ - m_inputBuffer.device = index; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputDevice() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputDevice(PaDeviceIndex index) -{ - m_outputBuffer.device = index; - return paNoError; -} - -//---------------------------------------------------------------- -// setCallback() -//---------------------------------------------------------------- -PaError PortAudioWrap::setCallback(PaStreamCallback *callback) -{ - m_pStreamCallback = callback; - return paNoError; -} - diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_pa_wrapper.h b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_pa_wrapper.h deleted file mode 100644 index 3d216c0d..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_pa_wrapper.h +++ /dev/null @@ -1,115 +0,0 @@ -//========================================================================== -// Name: fdmdv2_pa_wrapper.h -// Purpose: Defines a wrapper class around PortAudio -// Created: August 12, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include -#include "fdmdv2_defines.h" -#include "codec2_fdmdv.h" -#include "codec2.h" -#include "portaudio.h" - -#define PA_SAMPLE_TYPE paInt16 //paFloat32 -#define FRAMES_PER_BUFFER (64) - -typedef float SAMPLE; - -class PortAudioWrap -{ - public: - PortAudioWrap(); - ~PortAudioWrap(); - -// float m_av_mag[FDMDV_NSPEC]; - - private: - PaStream *m_pStream; - void *m_pUserData; - PaStreamCallback *m_pStreamCallback; - PaStreamFinishedCallback *m_pStreamFinishedCallback; - const PaStreamCallbackTimeInfo *m_pTimeInfo; - struct FDMDV *m_pFDMDV_state; - PaStreamParameters m_inputBuffer; - PaStreamParameters m_outputBuffer; - int m_samplerate; - unsigned long m_framesPerBuffer; - PaStreamCallbackFlags m_statusFlags; - bool m_newdata; - - public: - - void averageData(float mag_dB[]); - - int getDeviceCount() { return Pa_GetDeviceCount(); } - PaDeviceIndex getDefaultInputDevice(); - PaDeviceIndex getDefaultOutputDevice(); - PaStreamParameters *getDeviceInfo(PaDeviceIndex idx); - - PaError setFramesPerBuffer(unsigned long size); - PaError setSampleRate(unsigned long size); - - PaError setStreamFlags(PaStreamFlags flags); - PaError setCallback(PaStreamCallback *m_pStreamCallback); - PaError setStreamCallback(PaStream *stream, PaStreamCallback* callback) { m_pStreamCallback = callback; return 0;} - PaError setStreamFinishedCallback(PaStream *stream, PaStreamFinishedCallback* m_pStreamFinishedCallback); - - void setInputBuffer(const PaStreamParameters& inputBuffer) {this->m_inputBuffer = inputBuffer;} - PaError setInputDevice(PaDeviceIndex dev); - PaError setInputChannelCount(int count); - int getInputChannelCount(); - PaError setInputSampleFormat(PaSampleFormat format); - PaError setInputSampleRate(PaSampleFormat format); - PaError setInputLatency(PaTime latency); - void setInputHostApiStreamInfo(void *info = NULL); - PaTime getInputDefaultLowLatency(); - const char *getDeviceName(PaDeviceIndex dev); - - PaError setOutputDevice(PaDeviceIndex dev); - PaError setOutputChannelCount(int count); - const int getOutputChannelCount(); - PaError setOutputSampleFormat(PaSampleFormat format); - PaError setOutputLatency(PaTime latency); - void setOutputHostApiStreamInfo(void *info = NULL); - PaTime getOutputDefaultLowLatency(); - - void setFdmdvState(FDMDV* fdmdv_state) {this->m_pFDMDV_state = fdmdv_state;} - void setOutputBuffer(const PaStreamParameters& outputBuffer) {this->m_outputBuffer = outputBuffer;} - void setTimeInfo(PaStreamCallbackTimeInfo* timeInfo) {this->m_pTimeInfo = timeInfo;} - void setUserData(void* userData) {this->m_pUserData = userData;} - unsigned long getFramesPerBuffer() const {return m_framesPerBuffer;} - const PaStreamParameters& getInputBuffer() const {return m_inputBuffer;} - const PaStreamParameters& getOutputBuffer() const {return m_outputBuffer;} - const PaStreamCallbackFlags& getStatusFlags() const {return m_statusFlags;} - - FDMDV* getFdmdvState() {return m_pFDMDV_state;} - int getSamplerate() const {return m_samplerate;} - PaStream* getStream() {return m_pStream;} - void *getUserData() {return m_pUserData;} - bool getDataAvail() {return m_newdata;} - PaError streamStart(); - PaError streamClose(); - PaError streamOpen(); - void terminate(); - void stop(); - void abort(); - bool isOpen() const; - bool isStopped() const; - bool isActive() const; -// void loadData(); -}; diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot.cpp b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot.cpp deleted file mode 100644 index 1b85cd90..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot.cpp +++ /dev/null @@ -1,283 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot.cpp -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "fdmdv2_plot.h" - -BEGIN_EVENT_TABLE(PlotPanel, wxPanel) - EVT_PAINT (PlotPanel::OnPaint) - EVT_MOTION (PlotPanel::OnMouseMove) - EVT_LEFT_DOWN (PlotPanel::OnMouseLeftDown) - EVT_LEFT_UP (PlotPanel::OnMouseLeftUp) - EVT_RIGHT_DOWN (PlotPanel::OnMouseRightDown) - EVT_MOUSEWHEEL (PlotPanel::OnMouseWheelMoved) - EVT_SIZE (PlotPanel::OnSize) - EVT_SHOW (PlotPanel::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotPanel(wxFrame* parent) : wxPanel(parent) -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotPanel::PlotPanel(wxFrame* parent) : wxPanel(parent) -{ - m_pNoteBook = (wxAuiNotebook *) parent; - m_pTopFrame = (MainFrame *)m_pNoteBook->GetParent(); - m_zoomFactor = 1.0; - m_pBmp = NULL; - m_pPix = NULL; - m_firstPass = true; - m_line_color = 0; - m_newdata = false; - m_clip = false; - m_use_bitmap = true; - m_rubberBand = false; - m_mouseDown = false; - m_penShortDash = wxPen(wxColor(0xA0, 0xA0, 0xA0), 1, wxPENSTYLE_SHORT_DASH); - m_penDotDash = wxPen(wxColor(0xD0, 0xD0, 0xD0), 1, wxPENSTYLE_DOT_DASH); - m_penSolid = wxPen(wxColor(0x00, 0x00, 0x00), 1, wxPENSTYLE_SOLID); - SetBackgroundStyle(wxBG_STYLE_PAINT); - SetLabelSize(10.0); -} - -//------------------------------------------------------------------------- -// ~PlotPanel() -//------------------------------------------------------------------------- -PlotPanel::~PlotPanel() -{ - if(m_pBmp != NULL) - { - delete m_pBmp; - } -} - -//------------------------------------------------------------------------- -// GetLabelSize() -//------------------------------------------------------------------------- -double PlotPanel::GetLabelSize() -{ - return m_label_size; -} - -//------------------------------------------------------------------------- -// SetLabelSize() -//------------------------------------------------------------------------- -void PlotPanel::SetLabelSize(double size) -{ - m_label_size = size; -} - -//------------------------------------------------------------------------- -// OnShow() -//------------------------------------------------------------------------- -void PlotPanel::OnShow(wxShowEvent& event) -{ - this->Refresh(); -} - -//------------------------------------------------------------------------- -// OnErase() -//------------------------------------------------------------------------- -void PlotPanel::OnErase(wxEraseEvent& event) -{ - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnSize() -//------------------------------------------------------------------------- -void PlotPanel::OnSize(wxSizeEvent& event) -{ - m_rCtrlPrev = m_rCtrl; - m_rCtrl = GetClientRect(); - if(m_use_bitmap) - { - if(!m_oImage.IsOk()) - { - m_oImage.Create(m_rCtrl.GetWidth(), m_rCtrl.GetHeight(), true); - } - else - { - m_oImage.Rescale(m_rCtrl.GetWidth(), m_rCtrl.GetHeight()); - } - m_pBmp = new wxBitmap(m_oImage, wxBITMAP_SCREEN_DEPTH); - m_firstPass = true; - } - this->Refresh(); -} - -//------------------------------------------------------------------------- -// OnMouseMove() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseMove(wxMouseEvent& event) -{ -// if(m_mouseDown) -// { -// paintNow(); -// } -} - -//------------------------------------------------------------------------- -// OnMouseLeftDown() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseLeftDown(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseRightDown() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseRightDown(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseWheelMoved() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseWheelMoved(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseLeftUp() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseLeftUp(wxMouseEvent& event) -{ - m_mouseDown = false; -} - -//------------------------------------------------------------------------- -// SetZoomFactor() -//------------------------------------------------------------------------- -double PlotPanel::SetZoomFactor(double zf) -{ - if((zf > 0) && (zf < 5.0)) - { - m_zoomFactor = zf; - } - return zf; -} - -//------------------------------------------------------------------------- -// GetZoomFactor() -//------------------------------------------------------------------------- -double PlotPanel::GetZoomFactor(double zf) -{ - return m_zoomFactor; -} - -//------------------------------------------------------------------------- -// draw() -//------------------------------------------------------------------------- -void PlotPanel::draw(wxAutoBufferedPaintDC& pDC) -{ - printf("PlotPanel::draw()"); - wxMemoryDC m_mDC; - m_mDC.SelectObject(*m_pBmp); - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - m_rGrid.Offset(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER); - - pDC.Clear(); - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - if(m_firstPass) - { - m_firstPass = false; - m_mDC.FloodFill(0, 0, VERY_LTGREY_COLOR); - - // Draw a filled rectangle with aborder - wxBrush ltGraphBkgBrush = wxBrush(DARK_BLUE_COLOR); - m_mDC.SetBrush(ltGraphBkgBrush); - m_mDC.SetPen(wxPen(BLACK_COLOR, 0)); - m_mDC.DrawRectangle(m_rPlot); - } - if(m_newdata) - { - m_newdata = false; - int t = m_rPlot.GetTop(); - int l = m_rPlot.GetLeft(); -// int r = m_rPlot.GetRight(); - int h = m_rPlot.GetHeight(); - int w = m_rPlot.GetWidth(); - pDC.Blit(l, t, w, h, &m_mDC, l, t); - } - drawGraticule(pDC); - m_mDC.SetBrush(wxNullBrush); - m_mDC.SelectObject(wxNullBitmap); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotPanel::drawGraticule(wxAutoBufferedPaintDC& pDC) -{ - int p; - char buf[15]; - wxString s; - - // Vertical gridlines - pDC.SetPen(m_penShortDash); - for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < ((m_rGrid.GetWidth() - XLEFT_OFFSET) + GRID_INCREMENT); p += GRID_INCREMENT) - { - pDC.DrawLine(p, (m_rGrid.GetHeight() + PLOT_BORDER), p, PLOT_BORDER); - } - // Horizontal gridlines - pDC.SetPen(m_penDotDash); - for(p = (m_rGrid.GetHeight() - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT) - { - pDC.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (p + PLOT_BORDER), (m_rGrid.GetWidth() + PLOT_BORDER + XLEFT_OFFSET), (p + PLOT_BORDER)); - } - // Label the X-Axis - pDC.SetPen(wxPen(GREY_COLOR, 1)); - for(p = GRID_INCREMENT; p < (m_rGrid.GetWidth() - YBOTTOM_OFFSET); p += GRID_INCREMENT) - { - sprintf(buf, "%1.1f Hz",(double)(p / 10)); - pDC.DrawText(buf, p - PLOT_BORDER + XLEFT_OFFSET, m_rGrid.GetHeight() + YBOTTOM_OFFSET/2); - } - // Label the Y-Axis - //for(p = GRID_INCREMENT; p < (h - YBOTTOM_OFFSET); p += GRID_INCREMENT) - for(p = (m_rGrid.GetHeight() - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT) - { - sprintf(buf, "%1.0f", (double)((m_rGrid.GetHeight() - p) * -10)); - pDC.DrawText(buf, XLEFT_TEXT_OFFSET, p); - } -} - -//------------------------------------------------------------------------- -// paintEvent() -// -// Called by the system of by wxWidgets when the panel needs -// to be redrawn. You can also trigger this call by calling -// Refresh()/Update(). -//------------------------------------------------------------------------- -void PlotPanel::OnPaint(wxPaintEvent & evt) -{ - wxAutoBufferedPaintDC pdc(this); - draw(pdc); -} - diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot.h b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot.h deleted file mode 100644 index 25309d3a..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot.h +++ /dev/null @@ -1,150 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot.h -// Purpose: Declares simple wxWidgets application with GUI -// Created: Apr. 10, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -//#include "fdmdv2_main.h" -#ifndef __FDMDV2_PLOT__ -#define __FDMDV2_PLOT__ -#include -#include -#include -#include -#include - -#define MAX_ZOOM 7 -#define MAX_BMP_X (400 * MAX_ZOOM) -#define MAX_BMP_Y (400 * MAX_ZOOM) -#define DATA_LINE_HEIGHT 10 -#define TEXT_BASELINE_OFFSET_Y -5 - - -#define wxUSE_FILEDLG 1 -#define wxUSE_LIBPNG 1 -#define wxUSE_LIBJPEG 1 -#define wxUSE_GIF 1 -#define wxUSE_PCX 1 -#define wxUSE_LIBTIFF 1 - -#define PLOT_BORDER 12 -#define XLEFT_OFFSET 40 -#define XLEFT_TEXT_OFFSET 6 -#define YBOTTOM_OFFSET 20 -#define YBOTTOM_TEXT_OFFSET 15 -#define GRID_INCREMENT 50 - -#define BLACK_COLOR wxColor(0x00, 0x00, 0x00) -#define GREY_COLOR wxColor(0x80, 0x80, 0x80) -#define DARK_GREY_COLOR wxColor(0x40, 0x40, 0x40) -#define MEDIUM_GREY_COLOR wxColor(0xC0, 0xC0, 0xC0) -#define LIGHT_GREY_COLOR wxColor(0xE0, 0xE0, 0xE0) -#define VERY_LTGREY_COLOR wxColor(0xF8, 0xF8, 0xF8) -#define WHITE_COLOR wxColor(0xFF, 0xFF, 0xFF) - -#define DARK_BLUE_COLOR wxColor(0x00, 0x00, 0x60) -#define BLUE_COLOR wxColor(0x00, 0x00, 0xFF) -#define LIGHT_BLUE_COLOR wxColor(0x80, 0x80, 0xFF) - -#define RED_COLOR wxColor(0xFF, 0x5E, 0x5E) -#define LIGHT_RED_COLOR wxColor(0xFF, 0xE0, 0xE0) -#define DARK_RED_COLOR wxColor(0xFF, 0x00, 0x00) -#define PINK_COLOR wxColor(0xFF, 0x80, 0xFF) - -#define LIGHT_GREEN_COLOR wxColor(0xE3, 0xFF, 0xE0) -#define GREEN_COLOR wxColor(0x95, 0xFF, 0x8A) -#define DARK_GREEN_COLOR wxColor(0x20, 0xFF, 0x08) -#define VERY_GREEN_COLOR wxColor(0x00, 0xFF, 0x00) - -#define YELLOW_COLOR wxColor(0xFF, 0xFF, 0x5E) -#define LIGHT_YELLOW_COLOR wxColor(0xFF, 0xFF, 0xB5) -#define DARK_YELLOW_COLOR wxColor(0xFF, 0xFF, 0x08) - -class MainFrame; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotPanel -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotPanel : public wxPanel -{ - public: - PlotPanel(wxFrame* parent); - ~PlotPanel(); - wxPen m_penShortDash; - wxPen m_penDotDash; - wxPen m_penSolid; - wxRect m_rCtrlPrev; - wxRect m_rCtrl; - wxRect m_rGrid; - wxRect m_rPlot; - MainFrame *m_pTopFrame; - wxAuiNotebook *m_pNoteBook; - double m_label_size; - wxSize m_Bufsz; - bool m_newdata; - wxImage m_oImage; - wxBitmap *m_pBmp; - wxNativePixelData *m_pPix; - - // some useful events - void OnMouseMove(wxMouseEvent& event); - virtual void OnMouseLeftDown(wxMouseEvent& event); - void OnMouseLeftUp(wxMouseEvent& event); - virtual void OnMouseRightDown(wxMouseEvent& event); - void OnMouseWheelMoved(wxMouseEvent& event); - void OnClose(wxCloseEvent& event ){ event.Skip(); } - void OnSize( wxSizeEvent& event ); - void OnErase(wxEraseEvent& event); - void OnPaint(wxPaintEvent& event); - //void OnUpdateUI( wxUpdateUIEvent& event ){ event.Skip(); } - - void paintEvent(wxPaintEvent & evt); - virtual void draw(wxAutoBufferedPaintDC& pdc); - virtual void drawGraticule(wxAutoBufferedPaintDC& pdc); - virtual double SetZoomFactor(double zf); - virtual double GetZoomFactor(double zf); - virtual void OnShow(wxShowEvent& event); - virtual double GetLabelSize(); - virtual void SetLabelSize(double size); - - protected: - int m_x; - int m_y; - int m_left; - int m_top; - int m_prev_w; - int m_prev_h; - int m_prev_x; - int m_prev_y; - bool m_use_bitmap; - bool m_clip; - bool m_rubberBand; - bool m_mouseDown; - bool m_firstPass; - double m_zoomFactor; - int m_greyscale; - int m_line_color; - DECLARE_EVENT_TABLE() -}; -#endif //__FDMDV2_PLOT__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scalar.cpp b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scalar.cpp deleted file mode 100644 index 9a794f53..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scalar.cpp +++ /dev/null @@ -1,344 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_scalar.cpp -// Purpose: Plots scalar amplitude against time -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_main.h" -#include "fdmdv2_plot_scalar.h" - -BEGIN_EVENT_TABLE(PlotScalar, PlotPanel) - EVT_PAINT (PlotScalar::OnPaint) - EVT_MOTION (PlotScalar::OnMouseMove) - EVT_MOUSEWHEEL (PlotScalar::OnMouseWheelMoved) - EVT_SIZE (PlotScalar::OnSize) - EVT_SHOW (PlotScalar::OnShow) -// EVT_ERASE_BACKGROUND(PlotScalar::OnErase) -END_EVENT_TABLE() - -//---------------------------------------------------------------- -// PlotScalar() -//---------------------------------------------------------------- -PlotScalar::PlotScalar(wxFrame* parent, - int channels, // number on channels to plot - float t_secs, // time covered by entire x axis in seconds - float sample_period_secs, // time between each sample in seconds - float a_min, // min ampltude of samples being plotted - float a_max, // max ampltude of samples being plotted - float graticule_t_step, // time step of x (time) axis graticule in seconds - float graticule_a_step, // step of amplitude axis graticule - const char a_fmt[], // printf format string for amplitude axis labels - int mini // true for mini-plot - don't draw graticule - ): PlotPanel(parent) -{ - int i; - - m_rCtrl = GetClientRect(); - - m_channels = channels; - m_t_secs = t_secs; - m_sample_period_secs = sample_period_secs; - m_a_min = a_min; - m_a_max = a_max; - m_graticule_t_step = graticule_t_step; - m_graticule_a_step = graticule_a_step; - assert(strlen(a_fmt) < 15); - strcpy(m_a_fmt, a_fmt); - m_mini = mini; - m_bar_graph = 0; - m_logy = 0; - - // work out number of samples we will store and allocate storage - - m_samples = m_t_secs/m_sample_period_secs; - m_mem = new float[m_samples*m_channels]; - - for(i = 0; i < m_samples*m_channels; i++) - { - m_mem[i] = 0.0; - } -} - -//---------------------------------------------------------------- -// ~PlotScalar() -//---------------------------------------------------------------- -PlotScalar::~PlotScalar() -{ - delete m_mem; -} - -//---------------------------------------------------------------- -// add_new_sample() -//---------------------------------------------------------------- -void PlotScalar::add_new_sample(int channel, float sample) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-1; i++) - { - m_mem[offset+i] = m_mem[offset+i+1]; - } - m_mem[offset+m_samples-1] = sample; -} - -//---------------------------------------------------------------- -// add_new_samples() -//---------------------------------------------------------------- -void PlotScalar::add_new_samples(int channel, float samples[], int length) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-length; i++) - m_mem[offset+i] = m_mem[offset+i+length]; - for(; i < m_samples; i++) - m_mem[offset+i] = *samples++; -} - -//---------------------------------------------------------------- -// add_new_short_samples() -//---------------------------------------------------------------- -void PlotScalar::add_new_short_samples(int channel, short samples[], int length, float scale_factor) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-length; i++) - m_mem[offset+i] = m_mem[offset+i+length]; - for(; i < m_samples; i++) - m_mem[offset+i] = (float)*samples++/scale_factor; -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotScalar::draw(wxAutoBufferedPaintDC& dc) -{ - float index_to_px; - float a_to_py; - int i; - int prev_x, prev_y; - float a; - - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - if (!m_mini) - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - //printf("h %d w %d\n", m_rCtrl.GetWidth(), m_rCtrl.GetHeight()); - //printf("h %d w %d\n", m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - // black background - - dc.Clear(); - if (m_mini) - m_rPlot = wxRect(0, 0, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - else - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - index_to_px = (float)m_rGrid.GetWidth()/m_samples; - a_to_py = (float)m_rGrid.GetHeight()/(m_a_max - m_a_min); - - wxPen pen; - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - // draw all samples - - prev_x = prev_y = 0; // stop warning - - // plot each channel - - int offset, x, y; - for(offset=0; offset m_a_max) a = m_a_max; - - // invert y axis and offset by minimum - - y = m_rGrid.GetHeight() - a_to_py * a + m_a_min*a_to_py; - - // regular point-point line graph - - x = index_to_px * i; - - // put inside plot window - - if (!m_mini) { - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - } - - if (m_bar_graph) { - - if (m_logy) { - - // can't take log(0) - - assert(m_a_min > 0.0); - assert(m_a_max > 0.0); - - float norm = (log10(a) - log10(m_a_min))/(log10(m_a_max) - log10(m_a_min)); - y = m_rGrid.GetHeight()*(1.0 - norm); - } else { - y = m_rGrid.GetHeight() - a_to_py * a + m_a_min*a_to_py; - } - - // use points to make a bar graph - - int x1, x2, y1; - - x1 = index_to_px * ((float)i - 0.5); - x2 = index_to_px * ((float)i + 0.5); - y1 = m_rGrid.GetHeight(); - x1 += PLOT_BORDER + XLEFT_OFFSET; x2 += PLOT_BORDER + XLEFT_OFFSET; - y1 += PLOT_BORDER; - dc.DrawLine(x1, y1, x1, y); dc.DrawLine(x1, y, x2, y); dc.DrawLine(x2, y, x2, y1); - } - else { - if (i) - dc.DrawLine(x, y, prev_x, prev_y); - prev_x = x; prev_y = y; - } - } - } - - drawGraticule(dc); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotScalar::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - float t, a; - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float sec_to_px; - float a_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - sec_to_px = (float)m_rGrid.GetWidth()/m_t_secs; - a_to_py = (float)m_rGrid.GetHeight()/(m_a_max - m_a_min); - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Vertical gridlines - - dc.SetPen(m_penShortDash); - for(t=0; t<=m_t_secs; t+=m_graticule_t_step) { - x = t*sec_to_px; - if (m_mini) { - dc.DrawLine(x, m_rGrid.GetHeight(), x, 0); - } - else { - x += PLOT_BORDER + XLEFT_OFFSET; - dc.DrawLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, PLOT_BORDER); - } - if (!m_mini) { - sprintf(buf, "%2.1fs", t); - GetTextExtent(buf, &text_w, &text_h); - dc.DrawText(buf, x - text_w/2, m_rGrid.GetHeight() + PLOT_BORDER + YBOTTOM_TEXT_OFFSET); - } - } - - // Horizontal gridlines - - dc.SetPen(m_penDotDash); - for(a=m_a_min; a. -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SCALAR__ -#define __FDMDV2_PLOT_SCALAR__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotScalar -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotScalar: public PlotPanel -{ - public: - - PlotScalar(wxFrame* parent, - int channels, - float t_secs, - float sample_period_secs, - float a_min, - float a_max, - float graticule_t_step, - float graticule_a_step, - const char a_fmt[], - int mini - ); - ~PlotScalar(); - void add_new_sample(int channel, float sample); - void add_new_samples(int channel, float samples[], int length); - void add_new_short_samples(int channel, short samples[], int length, float scale_factor); - void setBarGraph(int bar_graph) { m_bar_graph = bar_graph; } - void setLogY(int logy) { m_logy = logy; } - - protected: - - int m_channels; - float m_t_secs; - float m_sample_period_secs; - float m_a_min; - float m_a_max; - float m_graticule_t_step; - float m_graticule_a_step; - char m_a_fmt[15]; - int m_mini; - int m_samples; - float *m_mem; - int m_bar_graph; // non zero to plot bar graphs - int m_logy; // plot graph on log scale - - void draw(wxAutoBufferedPaintDC& dc); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - - DECLARE_EVENT_TABLE() -}; - -#endif // __FDMDV2_PLOT_SCALAR__ - diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scatter.cpp b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scatter.cpp deleted file mode 100644 index d7e88c70..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_scatter.cpp +++ /dev/null @@ -1,289 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_scatter.cpp -// Purpose: A scatter plot derivative of fdmdv2_plot. -// Created: June 24, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_plot_scatter.h" - -BEGIN_EVENT_TABLE(PlotScatter, PlotPanel) - EVT_PAINT (PlotScatter::OnPaint) - EVT_MOTION (PlotScatter::OnMouseMove) - EVT_MOUSEWHEEL (PlotScatter::OnMouseWheelMoved) - EVT_SIZE (PlotScatter::OnSize) - EVT_SHOW (PlotScatter::OnShow) -// EVT_ERASE_BACKGROUND(PlotScatter::OnErase) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// PlotScatter -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotScatter::PlotScatter(wxFrame* parent) : PlotPanel(parent) -{ - int i; - - for(i=0; i < SCATTER_MEM_SYMS_MAX; i++) - { - m_mem[i].real = 0.0; - m_mem[i].imag = 0.0; - } - - m_filter_max_xy = m_filter_max_y = 0.1; - - // defaults so we start off with something sensible - - Nsym = 14+1; - scatterMemSyms = ((int)(SCATTER_MEM_SECS*(Nsym/DT))); - assert(scatterMemSyms <= SCATTER_MEM_SYMS_MAX); - - Ncol = 0; - memset(eye_mem, 0, sizeof(eye_mem)); - - mode = PLOT_SCATTER_MODE_SCATTER; -} - -// changing number of carriers changes number of symbols to plot -void PlotScatter::setNc(int Nc) { - Nsym = Nc; - assert(Nsym <= (MODEM_STATS_NC_MAX+1)); - scatterMemSyms = ((int)(SCATTER_MEM_SECS*(Nsym/DT))); - assert(scatterMemSyms <= SCATTER_MEM_SYMS_MAX); -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotScatter::draw(wxAutoBufferedPaintDC& dc) -{ - float x_scale; - float y_scale; - int i,j; - int x; - int y; - wxColour sym_to_colour[] = {wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255), - wxColor(255,255,0), - wxColor(255,255,255), - wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255), - wxColor(255,255,0), - wxColor(255,255,255), - wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255) - }; - - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - // black background - - dc.Clear(); - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - wxPen pen; - pen.SetWidth(1); // note this is ignored by DrawPoint - - if (mode == PLOT_SCATTER_MODE_SCATTER) { - - // automatically scale, first measure the maximum value - - float max_xy = 1E-12; - float real,imag; - for(i=0; i< scatterMemSyms; i++) { - real = fabs(m_mem[i].real); - imag = fabs(m_mem[i].imag); - if (real > max_xy) - max_xy = real; - if (imag > max_xy) - max_xy = imag; - } - - // smooth it out and set a lower limit to prevent divide by 0 issues - - m_filter_max_xy = BETA*m_filter_max_xy + (1 - BETA)*2.5*max_xy; - if (m_filter_max_xy < 0.001) - m_filter_max_xy = 0.001; - - // quantise to log steps to prevent scatter scaling bobbing about too - // much as scaling varies - - float quant_m_filter_max_xy = exp(floor(0.5+log(m_filter_max_xy))); - //printf("max_xy: %f m_filter_max_xy: %f quant_m_filter_max_xy: %f\n", max_xy, m_filter_max_xy, quant_m_filter_max_xy); - - x_scale = (float)m_rGrid.GetWidth()/quant_m_filter_max_xy; - y_scale = (float)m_rGrid.GetHeight()/quant_m_filter_max_xy; - - // draw all samples - - for(i = 0; i < scatterMemSyms; i++) { - x = x_scale * m_mem[i].real + m_rGrid.GetWidth()/2; - y = y_scale * m_mem[i].imag + m_rGrid.GetHeight()/2; - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - pen.SetColour(sym_to_colour[i%Nsym]); - dc.SetPen(pen); - dc.DrawPoint(x, y); - } - } - - if (mode == PLOT_SCATTER_MODE_EYE) { - - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - // automatically scale, first measure the maximum Y value - - float max_y = 1E-12; - float min_y = 1E+12; - for(i=0; i max_y) { - max_y = eye_mem[i][j]; - } - if (eye_mem[i][j] < min_y) { - min_y = eye_mem[i][j]; - } - } - } - - // smooth it out and set a lower limit to prevent divide by 0 issues - - m_filter_max_y = BETA*m_filter_max_y + (1 - BETA)*2.5*max_y; - if (m_filter_max_y < 0.001) - m_filter_max_y = 0.001; - - // quantise to log steps to prevent scatter scaling bobbing about too - // much as scaling varies - - float quant_m_filter_max_y = exp(floor(0.5+log(m_filter_max_y))); - //printf("min_y: %4.3f max_y: %4.3f quant_m_filter_max_y: %4.3f\n", min_y, max_y, quant_m_filter_max_y); - - x_scale = (float)m_rGrid.GetWidth()/Ncol; - y_scale = (float)m_rGrid.GetHeight()/quant_m_filter_max_y; - //printf("GetWidth(): %d GetHeight(): %d\n", m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - // plot eye traces row by row - - int prev_x, prev_y; - prev_x = prev_y = 0; - for(i=0; i. -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SCATTER__ -#define __FDMDV2_PLOT_SCATTER__ - -#include "comp.h" -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -#define PLOT_SCATTER_MODE_SCATTER 0 -#define PLOT_SCATTER_MODE_EYE 1 -#define PLOT_SCATTER_EYE_MAX_SAMPLES_ROW 80 - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotScatter -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotScatter : public PlotPanel -{ - public: - PlotScatter(wxFrame* parent); - ~PlotScatter(){}; - void add_new_samples_scatter(COMP samples[]); - void add_new_samples_eye(float samples[], int n); - void setNc(int Nc); - void setEyeScatter(int eye_mode) {mode = eye_mode;} - - protected: - int mode; - COMP m_mem[SCATTER_MEM_SYMS_MAX]; - COMP m_new_samples[MODEM_STATS_NC_MAX+1]; - float eye_mem[SCATTER_EYE_MEM_ROWS][PLOT_SCATTER_EYE_MAX_SAMPLES_ROW]; - - void draw(wxAutoBufferedPaintDC& dc); - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - - DECLARE_EVENT_TABLE() - - private: - int Nsym; - int Ncol; - int scatterMemSyms; - float m_filter_max_xy, m_filter_max_y; -}; - -#endif //__FDMDV2_PLOT_SCATTER__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_spectrum.cpp b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_spectrum.cpp deleted file mode 100644 index 1f5be59b..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_spectrum.cpp +++ /dev/null @@ -1,267 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.cpp -// Purpose: Implements a waterfall plot derivative of fdmdv2_plot. -// Created: June 23, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" - -#include "fdmdv2_main.h" - -extern float g_avmag[]; // average mag data passed to draw() -void fdmdv2_clickTune(float frequency); // callback to pass new click freq - -BEGIN_EVENT_TABLE(PlotSpectrum, PlotPanel) - EVT_MOTION (PlotSpectrum::OnMouseMove) - EVT_LEFT_DOWN (PlotSpectrum::OnMouseLeftDown) - EVT_LEFT_DCLICK (PlotSpectrum::OnMouseLeftDoubleClick) - EVT_LEFT_UP (PlotSpectrum::OnMouseLeftUp) - EVT_MOUSEWHEEL (PlotSpectrum::OnMouseWheelMoved) - EVT_PAINT (PlotSpectrum::OnPaint) - EVT_SHOW (PlotSpectrum::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotSpectrum -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotSpectrum::PlotSpectrum(wxFrame* parent, float *magdB, int n_magdB, - float min_mag_db, float max_mag_db, bool clickTune): PlotPanel(parent) -{ - m_greyscale = 0; - m_Bufsz = GetMaxClientSize(); - m_newdata = false; - m_firstPass = true; - m_line_color = 0; - SetLabelSize(10.0); - - m_magdB = magdB; - m_n_magdB = n_magdB; // number of points in magdB that covers 0 ... MAX_F_HZ of spectrum - m_max_mag_db = max_mag_db; - m_min_mag_db = min_mag_db; - m_rxFreq = 0.0; - m_clickTune = clickTune; -} - -//---------------------------------------------------------------- -// ~PlotSpectrum() -//---------------------------------------------------------------- -PlotSpectrum::~PlotSpectrum() -{ -} - -//---------------------------------------------------------------- -// OnSize() -//---------------------------------------------------------------- -void PlotSpectrum::OnSize(wxSizeEvent& event) { -} - -//---------------------------------------------------------------- -// OnPaint() -//---------------------------------------------------------------- -void PlotSpectrum::OnPaint(wxPaintEvent& event) -{ - wxAutoBufferedPaintDC dc(this); - draw(dc); -} - -//---------------------------------------------------------------- -// OnShow() -//---------------------------------------------------------------- -void PlotSpectrum::OnShow(wxShowEvent& event) -{ -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotSpectrum::draw(wxAutoBufferedPaintDC& dc) -{ - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. We need to work this out every time we draw - // as OnSize() may not be called before OnPaint(), for example when a new tab - // is selected - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - dc.Clear(); - - // black background - - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - // draw spectrum - - int x, y, prev_x, prev_y, index; - float index_to_px, mag_dB_to_py, mag; - - m_newdata = false; - - wxPen pen; - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - index_to_px = (float)m_rGrid.GetWidth()/m_n_magdB; - mag_dB_to_py = (float)m_rGrid.GetHeight()/(m_max_mag_db - m_min_mag_db); - - prev_x = PLOT_BORDER + XLEFT_OFFSET; - prev_y = PLOT_BORDER; - for(index = 0; index < m_n_magdB; index++) - { - x = index*index_to_px; - mag = m_magdB[index]; - if (mag > m_max_mag_db) mag = m_max_mag_db; - if (mag < m_min_mag_db) mag = m_min_mag_db; - y = -(mag - m_max_mag_db) * mag_dB_to_py; - - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - - if (index) - dc.DrawLine(x, y, prev_x, prev_y); - prev_x = x; prev_y = y; - } - - // and finally draw Graticule - - drawGraticule(dc); - -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotSpectrum::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float f, mag, freq_hz_to_px, mag_dB_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - mag_dB_to_py = (float)m_rGrid.GetHeight()/(m_max_mag_db - m_min_mag_db); - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Check if small screen size means text will overlap - - int textXStep = STEP_F_HZ*freq_hz_to_px; - int textYStep = STEP_MAG_DB*mag_dB_to_py; - sprintf(buf, "%4.0fHz", (float)MAX_F_HZ - STEP_F_HZ); - GetTextExtent(buf, &text_w, &text_h); - int overlappedText = (text_w > textXStep) || (text_h > textYStep); - //printf("text_w: %d textXStep: %d text_h: %d textYStep: %d overlappedText: %d\n", text_w, textXStep, - // text_h, textYStep, overlappedText); - - // Vertical gridlines - - for(f=STEP_F_HZ; f= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0) && m_clickTune) { - float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - float clickFreq = (float)pt.x/freq_hz_to_px; - - // see PlotWaterfall::OnMouseDown() - - fdmdv2_clickTune(clickFreq); - } -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_spectrum.h b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_spectrum.h deleted file mode 100644 index 271eeb98..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_spectrum.h +++ /dev/null @@ -1,58 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_spectrum.h -// Purpose: Defines a spectrum plot derived from fdmdv2_plot class. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SPECTRUM__ -#define __FDMDV2_PLOT_SPECTRUM__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class Waterfall -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotSpectrum : public PlotPanel -{ - public: - PlotSpectrum(wxFrame* parent, float *magdB, int n_magdB, - float min_mag_db=MIN_MAG_DB, float max_mag_db=MAX_MAG_DB, bool clickTune=true); - ~PlotSpectrum(); - void setRxFreq(float rxFreq) { m_rxFreq = rxFreq; } - void setFreqScale(int n_magdB) { m_n_magdB = n_magdB; } - - protected: - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void draw(wxAutoBufferedPaintDC& dc); - void OnMouseLeftDoubleClick(wxMouseEvent& event); - - private: - float m_rxFreq; - float m_max_mag_db; - float m_min_mag_db; - float *m_magdB; - int m_n_magdB; - bool m_clickTune; - - DECLARE_EVENT_TABLE() -}; - -#endif //__FDMDV2_PLOT_SPECTRUM__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_waterfall.cpp b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_waterfall.cpp deleted file mode 100644 index cdbe01e0..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_waterfall.cpp +++ /dev/null @@ -1,483 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.cpp -// Purpose: Implements a waterfall plot derivative of fdmdv2_plot. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_main.h" - -extern float g_avmag[]; // av mag spec passed in to draw() -void fdmdv2_clickTune(float frequency); // callback to pass new click freq - -BEGIN_EVENT_TABLE(PlotWaterfall, PlotPanel) - EVT_PAINT (PlotWaterfall::OnPaint) - EVT_MOTION (PlotWaterfall::OnMouseMove) - EVT_LEFT_DCLICK (PlotWaterfall::OnMouseLeftDoubleClick) - EVT_RIGHT_DOWN (PlotWaterfall::OnMouseRightDown) - EVT_LEFT_UP (PlotWaterfall::OnMouseLeftUp) - EVT_MOUSEWHEEL (PlotWaterfall::OnMouseWheelMoved) - EVT_SIZE (PlotWaterfall::OnSize) - EVT_SHOW (PlotWaterfall::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class WaterfallPlot -// -// @class WaterfallPlot -// @author David Witten -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotWaterfall::PlotWaterfall(wxFrame* parent, bool graticule, int colour): PlotPanel(parent) -{ - - for(int i = 0; i < 255; i++) - { - m_heatmap_lut[i] = heatmap((float)i, 0.0, 255.0); - } - m_graticule = graticule; - m_colour = colour; - m_Bufsz = GetMaxClientSize(); - m_newdata = false; - m_firstPass = true; - m_line_color = 0; - m_modem_stats_max_f_hz = MODEM_STATS_MAX_F_HZ; - - SetLabelSize(10.0); - - m_pBmp = NULL; - m_max_mag = MAX_MAG_DB; - m_min_mag = MIN_MAG_DB; -} - -// When the window size gets set we can work outthe size of the window -// we plot in and allocate a bit map of the correct size -void PlotWaterfall::OnSize(wxSizeEvent& event) -{ - // resize bit map - - delete m_pBmp; - - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - // we want a bit map the size of m_rGrid - - m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24); - - m_dT = DT; -} - -//---------------------------------------------------------------- -// paintEvent() -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -// Called by the system of by wxWidgets when the panel needs -// to be redrawn. You can also trigger this call by calling -// Refresh()/Update(). -//---------------------------------------------------------------- -void PlotWaterfall::OnPaint(wxPaintEvent & evt) -{ - wxAutoBufferedPaintDC dc(this); - draw(dc); -} - -//---------------------------------------------------------------- -// OnShow() -//---------------------------------------------------------------- -void PlotWaterfall::OnShow(wxShowEvent& event) -{ -} - -//---------------------------------------------------------------- -// ~PlotWaterfall() -//---------------------------------------------------------------- -PlotWaterfall::~PlotWaterfall() -{ -} - -//---------------------------------------------------------------- -// heatmap() -// map val to a rgb colour -// from http://eddiema.ca/2011/01/21/c-sharp-heatmaps/ -//---------------------------------------------------------------- -unsigned PlotWaterfall::heatmap(float val, float min, float max) -{ - unsigned r = 0; - unsigned g = 0; - unsigned b = 0; - - val = (val - min) / (max - min); - if(val <= 0.2) - { - b = (unsigned)((val / 0.2) * 255); - } - else if(val > 0.2 && val <= 0.7) - { - b = (unsigned)((1.0 - ((val - 0.2) / 0.5)) * 255); - } - if(val >= 0.2 && val <= 0.6) - { - g = (unsigned)(((val - 0.2) / 0.4) * 255); - } - else if(val > 0.6 && val <= 0.9) - { - g = (unsigned)((1.0 - ((val - 0.6) / 0.3)) * 255); - } - if(val >= 0.5) - { - r = (unsigned)(((val - 0.5) / 0.5) * 255); - } - //printf("%f %x %x %x\n", val, r, g, b); - return (b << 16) + (g << 8) + r; -} - -bool PlotWaterfall::checkDT(void) -{ - // Check dY is > 1 pixel before proceeding. For small screens - // and large WATERFALL_SECS_Y we might have less than one - // block per pixel. In this case increase m_dT and perform draw - // less often - - float px_per_sec = (float)m_rGrid.GetHeight() / WATERFALL_SECS_Y; - float dy = m_dT * px_per_sec; - - if (dy < 1.0) { - m_dT += DT; - return false; - } - else - return true; -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotWaterfall::draw(wxAutoBufferedPaintDC& dc) -{ - - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - if (m_pBmp == NULL) - { - // we want a bit map the size of m_rGrid - m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24); - } - - dc.Clear(); - - if(m_newdata) - { - m_newdata = false; - plotPixelData(); - dc.DrawBitmap(*m_pBmp, PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER); - m_dT = DT; - } - else - { - - // no data to plot so just erase to black. Blue looks nicer - // but is same colour as low amplitude signal - - // Bug on Linux: When Stop is pressed this code doesn't erase - // the lower 25% of the Waterfall Window - - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - } - drawGraticule(dc); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotWaterfall::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float f, time, freq_hz_to_px, time_s_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - time_s_to_py = (float)m_rGrid.GetHeight()/WATERFALL_SECS_Y; - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Check if small screen size means text will overlap - - int textXStep = STEP_F_HZ*freq_hz_to_px; - int textYStep = WATERFALL_SECS_STEP*time_s_to_py; - sprintf(buf, "%4.0fHz", (float)MAX_F_HZ - STEP_F_HZ); - GetTextExtent(buf, &text_w, &text_h); - int overlappedText = (text_w > textXStep) || (text_h > textYStep); - - // Major Vertical gridlines and legend - //dc.SetPen(m_penShortDash); - for(f=STEP_F_HZ; f max_mag) - { - max_mag = g_avmag[i]; - } - } - - m_max_mag = BETA*m_max_mag + (1 - BETA)*max_mag; - m_min_mag = max_mag - 20.0; - //printf("max_mag: %f m_max_mag: %f\n", max_mag, m_max_mag); - //intensity_per_dB = (float)256 /(MAX_MAG_DB - MIN_MAG_DB); - intensity_per_dB = (float)256 /(m_max_mag - m_min_mag); - spec_index_per_px = ((float)(MAX_F_HZ)/(float)m_modem_stats_max_f_hz)*(float)MODEM_STATS_NSPEC / (float) m_rGrid.GetWidth(); - - /* - printf("h %d w %d px_per_sec %d dy %d dy_blocks %d spec_index_per_px: %f\n", - m_rGrid.GetHeight(), m_rGrid.GetWidth(), px_per_sec, - dy, dy_blocks, spec_index_per_px); - */ - - // Shift previous bit map up one row of blocks ---------------------------- - wxNativePixelData data(*m_pBmp); - wxNativePixelData::Iterator bitMapStart(data); - wxNativePixelData::Iterator p = bitMapStart; - - for(b = 0; b < dy_blocks - 1; b++) - { - wxNativePixelData::Iterator psrc = bitMapStart; - wxNativePixelData::Iterator pdest = bitMapStart; - pdest.OffsetY(data, dy * b); - psrc.OffsetY(data, dy * (b+1)); - - // copy one line of blocks - - for(py = 0; py < dy; py++) - { - wxNativePixelData::Iterator pdestRowStart = pdest; - wxNativePixelData::Iterator psrcRowStart = psrc; - - for(px = 0; px < m_rGrid.GetWidth(); px++) - { - pdest.Red() = psrc.Red(); - pdest.Green() = psrc.Green(); - pdest.Blue() = psrc.Blue(); - pdest++; - psrc++; - } - pdest = pdestRowStart; - pdest.OffsetY(data, 1); - psrc = psrcRowStart; - psrc.OffsetY(data, 1); - } - } - - // Draw last line of blocks using latest amplitude data ------------------ - p = bitMapStart; - p.OffsetY(data, dy *(dy_blocks - 1)); - for(py = 0; py < dy; py++) - { - wxNativePixelData::Iterator rowStart = p; - - for(px = 0; px < m_rGrid.GetWidth(); px++) - { - index = px * spec_index_per_px; - assert(index < MODEM_STATS_NSPEC); - - intensity = intensity_per_dB * (g_avmag[index] - m_min_mag); - if(intensity > 255) intensity = 255; - if (intensity < 0) intensity = 0; - //printf("%d %f %d \n", index, g_avmag[index], intensity); - - switch (m_colour) { - case 0: - p.Red() = m_heatmap_lut[intensity] & 0xff; - p.Green() = (m_heatmap_lut[intensity] >> 8) & 0xff; - p.Blue() = (m_heatmap_lut[intensity] >> 16) & 0xff; - break; - case 1: - p.Red() = intensity; - p.Green() = intensity; - p.Blue() = intensity; - break; - case 2: - p.Red() = intensity; - p.Green() = intensity; - if (intensity < 127) - p.Blue() = intensity*2; - else - p.Blue() = 255; - - break; - } - ++p; - } - p = rowStart; - p.OffsetY(data, 1); - } - -} - -//------------------------------------------------------------------------- -// OnMouseLeftDown() -//------------------------------------------------------------------------- -void PlotWaterfall::OnMouseLeftDoubleClick(wxMouseEvent& event) -{ - m_mouseDown = true; - wxClientDC dc(this); - - wxPoint pt(event.GetLogicalPosition(dc)); - - // map x coord to edges of actual plot - pt.x -= PLOT_BORDER + XLEFT_OFFSET; - pt.y -= PLOT_BORDER; - - // valid click if inside of plot - if ((pt.x >= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0)) - { - float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - float clickFreq = (float)pt.x/freq_hz_to_px; - - // communicate back to other threads - fdmdv2_clickTune(clickFreq); - } -} - -//------------------------------------------------------------------------- -// OnMouseRightDown() -//------------------------------------------------------------------------- -void PlotWaterfall::OnMouseRightDown(wxMouseEvent& event) -{ - m_colour++; - if (m_colour == 3) - m_colour = 0; -} - diff --git a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_waterfall.h b/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_waterfall.h deleted file mode 100644 index f4896c6b..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/fdmdv2_plot_waterfall.h +++ /dev/null @@ -1,73 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.h -// Purpose: Defines a waterfall plot derivative of fdmdv2_plot. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_PLOT_WATERFALL__ -#define __FDMDV2_PLOT_WATERFALL__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotWaterfall -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotWaterfall : public PlotPanel -{ - public: - PlotWaterfall(wxFrame* parent, bool graticule, int colour); - ~PlotWaterfall(); - bool checkDT(void); - void setGreyscale(bool greyscale) { m_greyscale = greyscale; } - void setRxFreq(float rxFreq) { m_rxFreq = rxFreq; } - void setFs(int fs) { m_modem_stats_max_f_hz = fs/2; } - - protected: - unsigned m_heatmap_lut[256]; - - unsigned heatmap(float val, float min, float max); - - void OnPaint(wxPaintEvent & evt); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void draw(wxAutoBufferedPaintDC& dc); - void plotPixelData(); - void OnMouseLeftDoubleClick(wxMouseEvent& event); - void OnMouseRightDown(wxMouseEvent& event); - - private: - float m_dT; - float m_rxFreq; - bool m_graticule; - float m_min_mag; - float m_max_mag; - int m_colour; - int m_modem_stats_max_f_hz; - - DECLARE_EVENT_TABLE() -}; - -#endif //__FDMDV2_PLOT_WATERFALL__ diff --git a/freedv/tags/1.2.2/freedv-dev/src/freedv.icns b/freedv/tags/1.2.2/freedv-dev/src/freedv.icns deleted file mode 100644 index 5190e7951ffd9152576d6569f0c7ca64927649ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92136 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

-_`&2*Z5cR;kk!g) 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 bcU%5HHl?3v;lGWz00000NkvXXu0mjfpGTzJ diff --git a/freedv/tags/1.2.2/freedv-dev/src/hamlib.cpp b/freedv/tags/1.2.2/freedv-dev/src/hamlib.cpp deleted file mode 100644 index ff80b24a..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/hamlib.cpp +++ /dev/null @@ -1,160 +0,0 @@ -//========================================================================== -// Name: hamlib.cpp -// -// Purpose: Hamlib integration for FreeDV -// Created: May 2013 -// Authors: Joel Stanley -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include - -#include -#include - -using namespace std; - -typedef std::vector riglist_t; - -static bool rig_cmp(const struct rig_caps *rig1, const struct rig_caps *rig2); -static int build_list(const struct rig_caps *rig, rig_ptr_t); - -Hamlib::Hamlib() : m_rig(NULL) { - /* Stop hamlib from spewing info to stderr. */ - rig_set_debug(RIG_DEBUG_NONE); - - /* Create sorted list of rigs. */ - rig_load_all_backends(); - rig_list_foreach(build_list, &m_rigList); - sort(m_rigList.begin(), m_rigList.end(), rig_cmp); - - /* Reset debug output. */ - rig_set_debug(RIG_DEBUG_VERBOSE); - - m_rig = NULL; -} - -Hamlib::~Hamlib() { - if(m_rig) - close(); -} - -static int build_list(const struct rig_caps *rig, rig_ptr_t rigList) { - ((riglist_t *)rigList)->push_back(rig); - return 1; -} - -static bool rig_cmp(const struct rig_caps *rig1, const struct rig_caps *rig2) { - /* Compare manufacturer. */ - int r = strcasecmp(rig1->mfg_name, rig2->mfg_name); - if (r != 0) - return r < 0; - - /* Compare model. */ - r = strcasecmp(rig1->model_name, rig2->model_name); - if (r != 0) - return r < 0; - - /* Compare rig ID. */ - return rig1->rig_model < rig2->rig_model; -} - -void Hamlib::populateComboBox(wxComboBox *cb) { - - riglist_t::const_iterator rig = m_rigList.begin(); - for (; rig !=m_rigList.end(); rig++) { - char name[128]; - snprintf(name, 128, "%s %s", (*rig)->mfg_name, (*rig)->model_name); - cb->Append(name); - } -} - -bool Hamlib::connect(unsigned int rig_index, const char *serial_port, const int serial_rate) { - /* Look up model from index. */ - if (rig_index >= m_rigList.size()) { - return false; - } - fprintf(stderr, "rig: %s %s (%d)\n", m_rigList[rig_index]->mfg_name, - m_rigList[rig_index]->model_name, m_rigList[rig_index]->rig_model); - - if(m_rig) { - printf("Closing old hamlib instance!\n"); - close(); - } - - /* Initialise, configure and open. */ - - m_rig = rig_init(m_rigList[rig_index]->rig_model); - - if (!m_rig) - return false; - - /* TODO we may also need civaddr for Icom */ - - strncpy(m_rig->state.rigport.pathname, serial_port, FILPATHLEN - 1); - if (serial_rate) { - m_rig->state.rigport.parm.serial.rate = serial_rate; - } - fprintf(stderr, "hamlib: setting serial rate: %d\n", m_rig->state.rigport.parm.serial.rate); - - if (rig_open(m_rig) == RIG_OK) { - return true; - } - - return false; -} - -int Hamlib::get_serial_rate(void) { - return m_rig->state.rigport.parm.serial.rate; -} - -int Hamlib::get_data_bits(void) { - return m_rig->state.rigport.parm.serial.data_bits; -} - -int Hamlib::get_stop_bits(void) { - return m_rig->state.rigport.parm.serial.stop_bits; -} - -bool Hamlib::ptt(bool press, wxString &hamlibError) { - fprintf(stderr,"Hamlib::ptt: %d\n", press); - hamlibError = ""; - - if(!m_rig) - return false; - - /* TODO(Joel): make ON_DATA and ON configurable. */ - - ptt_t on = press ? RIG_PTT_ON : RIG_PTT_OFF; - - /* TODO(Joel): what should the VFO option be? */ - - int retcode = rig_set_ptt(m_rig, RIG_VFO_CURR, on); - fprintf(stderr,"Hamlib::ptt: rig_set_ptt returned: %d\n", retcode); - if (retcode != RIG_OK ) { - fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(retcode)); - hamlibError = rigerror(retcode); - } - - return retcode == RIG_OK; -} - -void Hamlib::close(void) { - if(m_rig) { - rig_close(m_rig); - rig_cleanup(m_rig); - m_rig = NULL; - } -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/hamlib.h b/freedv/tags/1.2.2/freedv-dev/src/hamlib.h deleted file mode 100644 index 65af2d46..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/hamlib.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef HAMLIB_H -#define HAMLIB_H - -extern "C" { -#include -} -#include -#include - -class Hamlib { - - public: - Hamlib(); - ~Hamlib(); - void populateComboBox(wxComboBox *cb); - bool connect(unsigned int rig_index, const char *serial_port, const int serial_rate); - bool ptt(bool press, wxString &hamlibError); - void close(void); - int get_serial_rate(void); - int get_data_bits(void); - int get_stop_bits(void); - - typedef std::vector riglist_t; - - private: - RIG *m_rig; - /* Sorted list of rigs. */ - riglist_t m_rigList; -}; - -#endif /*HAMLIB_H*/ diff --git a/freedv/tags/1.2.2/freedv-dev/src/info.plist b/freedv/tags/1.2.2/freedv-dev/src/info.plist deleted file mode 100644 index 8f0d4c34..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/info.plist +++ /dev/null @@ -1,104 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - CFBundleIconFile - freedv - NSPrincipalClass - NSApplication - - - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - NSPrincipalClass - NSApplication - - - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - NSPrincipalClass - NSApplication - - \ No newline at end of file diff --git a/freedv/tags/1.2.2/freedv-dev/src/serialport.cpp b/freedv/tags/1.2.2/freedv-dev/src/serialport.cpp deleted file mode 100644 index 59dd0c93..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/serialport.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include -#include -#include -#include "serialport.h" - -Serialport::Serialport() { - com_handle = COM_HANDLE_INVALID; -} - -Serialport::~Serialport() { - if (isopen()) { - closeport(); - } -} - -// returns true if comm port opened OK, false if there was a problem - -bool Serialport::openport(const char name[], bool useRTS, bool RTSPos, bool useDTR, bool DTRPos) -{ - if (com_handle != COM_HANDLE_INVALID) { - closeport(); - } - - m_useRTS = useRTS; - m_RTSPos = RTSPos; - m_useDTR = useDTR; - m_DTRPos = DTRPos; - -#ifdef _WIN32 - { - COMMCONFIG CC; - DWORD CCsize=sizeof(CC); - COMMTIMEOUTS timeouts; - DCB dcb; - - if(GetDefaultCommConfigA(name, &CC, &CCsize)) { - CC.dcb.fOutxCtsFlow = FALSE; - CC.dcb.fOutxDsrFlow = FALSE; - CC.dcb.fDtrControl = DTR_CONTROL_DISABLE; - CC.dcb.fDsrSensitivity = FALSE; - CC.dcb.fRtsControl = RTS_CONTROL_DISABLE; - SetDefaultCommConfigA(name, &CC, CCsize); - } - - if((com_handle=CreateFileA(name - ,GENERIC_READ|GENERIC_WRITE /* Access */ - ,0 /* Share mode */ - ,NULL /* Security attributes */ - ,OPEN_EXISTING /* Create access */ - ,FILE_ATTRIBUTE_NORMAL /* File attributes */ - ,NULL /* Template */ - ))==INVALID_HANDLE_VALUE) - return false; - - if(GetCommTimeouts(com_handle, &timeouts)) { - timeouts.ReadIntervalTimeout=MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier=0; - timeouts.ReadTotalTimeoutConstant=0; // No-wait read timeout - timeouts.WriteTotalTimeoutMultiplier=0; - timeouts.WriteTotalTimeoutConstant=5000; // 5 seconds - SetCommTimeouts(com_handle,&timeouts); - } - - /* Force N-8-1 mode: */ - if(GetCommState(com_handle, &dcb)==TRUE) { - dcb.ByteSize = 8; - dcb.Parity = NOPARITY; - dcb.StopBits = ONESTOPBIT; - dcb.DCBlength = sizeof(DCB); - dcb.fBinary = TRUE; - dcb.fOutxCtsFlow = FALSE; - dcb.fOutxDsrFlow = FALSE; - dcb.fDtrControl = DTR_CONTROL_DISABLE; - dcb.fDsrSensitivity = FALSE; - dcb.fTXContinueOnXoff= TRUE; - dcb.fOutX = FALSE; - dcb.fInX = FALSE; - dcb.fRtsControl = RTS_CONTROL_DISABLE; - dcb.fAbortOnError = FALSE; - SetCommState(com_handle, &dcb); - } - } -#else - { - struct termios t; - - if((com_handle=open(name, O_NONBLOCK|O_RDWR))== COM_HANDLE_INVALID) - return false; - - if(tcgetattr(com_handle, &t)==-1) { - close(com_handle); - com_handle = COM_HANDLE_INVALID; - return false; - } - - t.c_iflag = ( - IGNBRK /* ignore BREAK condition */ - | IGNPAR /* ignore (discard) parity errors */ - ); - t.c_oflag = 0; /* No output processing */ - t.c_cflag = ( - CS8 /* 8 bits */ - | CREAD /* enable receiver */ - - /* - Fun snippet from the FreeBSD manpage: - - If CREAD is set, the receiver is enabled. Otherwise, no character is - received. Not all hardware supports this bit. In fact, this flag is - pretty silly and if it were not part of the termios specification it - would be omitted. - */ - | CLOCAL /* ignore modem status lines */ - ); - - t.c_lflag = 0; /* No local modes */ - if(tcsetattr(com_handle, TCSANOW, &t)==-1) { - close(com_handle); - com_handle = COM_HANDLE_INVALID; - return false; - } - - } -#endif - return true; -} - - -// fixme: this takes about one second to close under Linux - -void Serialport::closeport() -{ -#ifdef _WIN32 - CloseHandle(com_handle); -#else - close(com_handle); -#endif - com_handle = COM_HANDLE_INVALID; -} - -//---------------------------------------------------------------- -// (raise|lower)(RTS|DTR)() -// -// Raises/lowers the specified signal -//---------------------------------------------------------------- - -void Serialport::raiseDTR(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, SETDTR); -#else - { // For C89 happiness - int flags = TIOCM_DTR; - ioctl(com_handle, TIOCMBIS, &flags); - } -#endif -} - -void Serialport::raiseRTS(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, SETRTS); -#else - { // For C89 happiness - int flags = TIOCM_RTS; - ioctl(com_handle, TIOCMBIS, &flags); - } -#endif -} - -void Serialport::lowerDTR(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, CLRDTR); -#else - { // For C89 happiness - int flags = TIOCM_DTR; - ioctl(com_handle, TIOCMBIC, &flags); - } -#endif -} - -void Serialport::lowerRTS(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, CLRRTS); -#else - { // For C89 happiness - int flags = TIOCM_RTS; - ioctl(com_handle, TIOCMBIC, &flags); - } -#endif -} - -void Serialport::ptt(bool tx) { - - /* Truth table: - - g_tx RTSPos RTS - ------------------- - 0 1 0 - 1 1 1 - 0 0 1 - 1 0 0 - - exclusive NOR - */ - - if (com_handle != COM_HANDLE_INVALID) { - if (m_useRTS) { - //fprintf(stderr, "g_tx: %d m_boolRTSPos: %d serialLine: %d\n", g_tx, wxGetApp().m_boolRTSPos, g_tx == wxGetApp().m_boolRTSPos); - if (tx == m_RTSPos) - raiseRTS(); - else - lowerRTS(); - } - if (m_useDTR) { - //fprintf(stderr, "g_tx: %d m_boolDTRPos: %d serialLine: %d\n", g_tx, wxGetApp().m_boolDTRPos, g_tx == wxGetApp().m_boolDTRPos); - if (tx == m_DTRPos) - raiseDTR(); - else - lowerDTR(); - } - - } -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/serialport.h b/freedv/tags/1.2.2/freedv-dev/src/serialport.h deleted file mode 100644 index e5db10b4..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/serialport.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef SERIALPORT_H -#define SERIALPORT_H - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -// Serial ports called com port for historic reasons, especially on Windows machines - -#ifdef _WIN32 -#define COM_HANDLE_INVALID INVALID_HANDLE_VALUE -typedef HANDLE com_handle_t; -#else -#define COM_HANDLE_INVALID -1 -typedef int com_handle_t; -#endif - -class Serialport { - - public: - Serialport(); - ~Serialport(); - bool openport(const char port[], bool useRTS, bool RTSPos, bool useDTR, bool DTRPos); - bool isopen() {return (com_handle != COM_HANDLE_INVALID);} - void closeport(); - void ptt(bool tx); - - private: - com_handle_t com_handle; - bool m_useRTS, m_RTSPos, m_useDTR, m_DTRPos; - - void raiseDTR(void); - void lowerDTR(void); - void raiseRTS(void); - void lowerRTS(void); -}; - -#endif /* SERIALPORT_H */ diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/band.h b/freedv/tags/1.2.2/freedv-dev/src/sox/band.h deleted file mode 100644 index 5398ff45..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/band.h +++ /dev/null @@ -1,47 +0,0 @@ -/* libSoX Bandpass effect file. July 5, 1991 - * Copyright 1991 Lance Norskog And Sundry Contributors - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Lance Norskog And Sundry Contributors are not responsible for - * the consequences of using this software. - * - * Algorithm: 2nd order recursive filter. - * Formula stolen from MUSIC56K, a toolkit of 56000 assembler stuff. - * Quote: - * This is a 2nd order recursive band pass filter of the form. - * y(n)= a * x(n) - b * y(n-1) - c * y(n-2) - * where : - * x(n) = "IN" - * "OUT" = y(n) - * c = EXP(-2*pi*cBW/S_RATE) - * b = -4*c/(1+c)*COS(2*pi*cCF/S_RATE) - * if cSCL=2 (i.e. noise input) - * a = SQT(((1+c)*(1+c)-b*b)*(1-c)/(1+c)) - * else - * a = SQT(1-b*b/(4*c))*(1-c) - * endif - * note : cCF is the center frequency in Hertz - * cBW is the band width in Hertz - * cSCL is a scale factor, use 1 for pitched sounds - * use 2 for noise. - * - * - * July 1, 1999 - Jan Paul Schmidt - * - * This looks like the resonator band pass in SPKit. It's a - * second order all-pole (IIR) band-pass filter described - * at the pages 186 - 189 in - * Dodge, Charles & Jerse, Thomas A. 1985: - * Computer Music -- Synthesis, Composition and Performance. - * New York: Schirmer Books. - * Reference from the SPKit manual. - */ - - p->a2 = exp(-2 * M_PI * bw_Hz / effp->in_signal.rate); - p->a1 = -4 * p->a2 / (1 + p->a2) * cos(2 * M_PI * p->fc / effp->in_signal.rate); - p->b0 = sqrt(1 - p->a1 * p->a1 / (4 * p->a2)) * (1 - p->a2); - if (p->filter_type == filter_BPF_SPK_N) { - mult = sqrt(((1+p->a2) * (1+p->a2) - p->a1*p->a1) * (1-p->a2) / (1+p->a2)) / p->b0; - p->b0 *= mult; - } diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/biquad.c b/freedv/tags/1.2.2/freedv-dev/src/sox/biquad.c deleted file mode 100644 index c57f1902..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/biquad.c +++ /dev/null @@ -1,178 +0,0 @@ -/* libSoX Biquad filter common functions (c) 2006-7 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "biquad.h" -#include - -typedef biquad_t priv_t; - -static char const * const width_str[] = { - "band-width(Hz)", - "band-width(kHz)", - "band-width(Hz, no warp)", /* deprecated */ - "band-width(octaves)", - "Q", - "slope", -}; -static char const all_width_types[] = "hkboqs"; - - -int lsx_biquad_getopts(sox_effect_t * effp, int argc, char **argv, - int min_args, int max_args, int fc_pos, int width_pos, int gain_pos, - char const * allowed_width_types, filter_t filter_type) -{ - priv_t * p = (priv_t *)effp->priv; - char width_type = *allowed_width_types; - char dummy, * dummy_p; /* To check for extraneous chars. */ - --argc, ++argv; - - p->filter_type = filter_type; - if (argc < min_args || argc > max_args || - (argc > fc_pos && ((p->fc = lsx_parse_frequency(argv[fc_pos], &dummy_p)) <= 0 || *dummy_p)) || - (argc > width_pos && ((unsigned)(sscanf(argv[width_pos], "%lf%c %c", &p->width, &width_type, &dummy)-1) > 1 || p->width <= 0)) || - (argc > gain_pos && sscanf(argv[gain_pos], "%lf %c", &p->gain, &dummy) != 1) || - !strchr(allowed_width_types, width_type) || (width_type == 's' && p->width > 1)) - return lsx_usage(effp); - p->width_type = strchr(all_width_types, width_type) - all_width_types; - if ((size_t)p->width_type >= strlen(all_width_types)) - p->width_type = 0; - if (p->width_type == width_bw_kHz) { - p->width *= 1000; - p->width_type = width_bw_Hz; - } - return SOX_SUCCESS; -} - - -static int start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - /* Simplify: */ - p->b2 /= p->a0; - p->b1 /= p->a0; - p->b0 /= p->a0; - p->a2 /= p->a0; - p->a1 /= p->a0; - - p->o2 = p->o1 = p->i2 = p->i1 = 0; - return SOX_SUCCESS; -} - - -int lsx_biquad_start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - - start(effp); - - if (effp->global_info->plot == sox_plot_octave) { - printf( - "%% GNU Octave file (may also work with MATLAB(R) )\n" - "Fs=%g;minF=10;maxF=Fs/2;\n" - "sweepF=logspace(log10(minF),log10(maxF),200);\n" - "[h,w]=freqz([%.15e %.15e %.15e],[1 %.15e %.15e],sweepF,Fs);\n" - "semilogx(w,20*log10(h))\n" - "title('SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)')\n" - "xlabel('Frequency (Hz)')\n" - "ylabel('Amplitude Response (dB)')\n" - "axis([minF maxF -35 25])\n" - "grid on\n" - "disp('Hit return to continue')\n" - "pause\n" - , effp->in_signal.rate, p->b0, p->b1, p->b2, p->a1, p->a2 - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate); - return SOX_EOF; - } - if (effp->global_info->plot == sox_plot_gnuplot) { - printf( - "# gnuplot file\n" - "set title 'SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)'\n" - "set xlabel 'Frequency (Hz)'\n" - "set ylabel 'Amplitude Response (dB)'\n" - "Fs=%g\n" - "b0=%.15e; b1=%.15e; b2=%.15e; a1=%.15e; a2=%.15e\n" - "o=2*pi/Fs\n" - "H(f)=sqrt((b0*b0+b1*b1+b2*b2+2.*(b0*b1+b1*b2)*cos(f*o)+2.*(b0*b2)*cos(2.*f*o))/(1.+a1*a1+a2*a2+2.*(a1+a1*a2)*cos(f*o)+2.*a2*cos(2.*f*o)))\n" - "set logscale x\n" - "set samples 250\n" - "set grid xtics ytics\n" - "set key off\n" - "plot [f=10:Fs/2] [-35:25] 20*log10(H(f))\n" - "pause -1 'Hit return to continue'\n" - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate, effp->in_signal.rate - , p->b0, p->b1, p->b2, p->a1, p->a2); - return SOX_EOF; - } - if (effp->global_info->plot == sox_plot_data) { - printf("# SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)\n" - "# IIR filter\n" - "# rate: %g\n" - "# name: b\n" - "# type: matrix\n" - "# rows: 3\n" - "# columns: 1\n" - "%24.16e\n%24.16e\n%24.16e\n" - "# name: a\n" - "# type: matrix\n" - "# rows: 3\n" - "# columns: 1\n" - "%24.16e\n%24.16e\n%24.16e\n" - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate, effp->in_signal.rate - , p->b0, p->b1, p->b2, 1. /* a0 */, p->a1, p->a2); - return SOX_EOF; - } - return SOX_SUCCESS; -} - - -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, - sox_sample_t *obuf, size_t *isamp, size_t *osamp) -{ - priv_t * p = (priv_t *)effp->priv; - size_t len = *isamp = *osamp = min(*isamp, *osamp); - while (len--) { - double o0 = *ibuf*p->b0 + p->i1*p->b1 + p->i2*p->b2 - p->o1*p->a1 - p->o2*p->a2; - p->i2 = p->i1, p->i1 = *ibuf++; - p->o2 = p->o1, p->o1 = o0; - *obuf++ = SOX_ROUND_CLIP_COUNT(o0, effp->clips); - } - return SOX_SUCCESS; -} - -static int create(sox_effect_t * effp, int argc, char * * argv) -{ - priv_t * p = (priv_t *)effp->priv; - double * d = &p->b0; - char c; - - --argc, ++argv; - if (argc == 6) - for (; argc && sscanf(*argv, "%lf%c", d, &c) == 1; --argc, ++argv, ++d); - return argc? lsx_usage(effp) : SOX_SUCCESS; -} - -sox_effect_handler_t const * lsx_biquad_effect_fn(void) -{ - static sox_effect_handler_t handler = { - "biquad", "b0 b1 b2 a0 a1 a2", 0, - create, lsx_biquad_start, lsx_biquad_flow, NULL, NULL, NULL, sizeof(priv_t) - }; - return &handler; -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/biquad.h b/freedv/tags/1.2.2/freedv-dev/src/sox/biquad.h deleted file mode 100644 index 8786ac83..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/biquad.h +++ /dev/null @@ -1,78 +0,0 @@ -/* libSoX Biquad filter common definitions (c) 2006-7 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef biquad_included -#define biquad_included - -#define LSX_EFF_ALIAS -#include "sox_i.h" - -typedef enum { - filter_LPF, - filter_HPF, - filter_BPF_CSG, - filter_BPF, - filter_notch, - filter_APF, - filter_peakingEQ, - filter_lowShelf, - filter_highShelf, - filter_LPF_1, - filter_HPF_1, - filter_BPF_SPK, - filter_BPF_SPK_N, - filter_AP1, - filter_AP2, - filter_deemph, - filter_riaa -} filter_t; - -typedef enum { - width_bw_Hz, - width_bw_kHz, - /* The old, non-RBJ, non-freq-warped band-pass/reject response; - * leaving here for now just in case anybody misses it: */ - width_bw_old, - width_bw_oct, - width_Q, - width_slope -} width_t; - -/* Private data for the biquad filter effects */ -typedef struct { - double gain; /* For EQ filters */ - double fc; /* Centre/corner/cutoff frequency */ - double width; /* Filter width; interpreted as per width_type */ - width_t width_type; - - filter_t filter_type; - - double b0, b1, b2; /* Filter coefficients */ - double a0, a1, a2; /* Filter coefficients */ - - sox_sample_t i1, i2; /* Filter memory */ - double o1, o2; /* Filter memory */ -} biquad_t; - -int lsx_biquad_getopts(sox_effect_t * effp, int n, char **argv, - int min_args, int max_args, int fc_pos, int width_pos, int gain_pos, - char const * allowed_width_types, filter_t filter_type); -int lsx_biquad_start(sox_effect_t * effp); -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf, - size_t *isamp, size_t *osamp); - -#endif diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/biquads.c b/freedv/tags/1.2.2/freedv-dev/src/sox/biquads.c deleted file mode 100644 index 19793a6d..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/biquads.c +++ /dev/null @@ -1,400 +0,0 @@ -/* libSoX Biquad filter effects (c) 2006-8 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * - * 2-pole filters designed by Robert Bristow-Johnson - * see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt - * - * 1-pole filters based on code (c) 2000 Chris Bagwell - * Algorithms: Recursive single pole low/high pass filter - * Reference: The Scientist and Engineer's Guide to Digital Signal Processing - * - * low-pass: output[N] = input[N] * A + output[N-1] * B - * X = exp(-2.0 * pi * Fc) - * A = 1 - X - * B = X - * Fc = cutoff freq / sample rate - * - * Mimics an RC low-pass filter: - * - * ---/\/\/\/\-----------> - * | - * --- C - * --- - * | - * | - * V - * - * high-pass: output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1] - * X = exp(-2.0 * pi * Fc) - * A0 = (1 + X) / 2 - * A1 = -(1 + X) / 2 - * B1 = X - * Fc = cutoff freq / sample rate - * - * Mimics an RC high-pass filter: - * - * || C - * ----||---------> - * || | - * < - * > R - * < - * | - * V - */ - - -#include "biquad.h" -#include -#include - -typedef biquad_t priv_t; - - -static int hilo1_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 1, 1, 0, 1, 2, "", - *effp->handler.name == 'l'? filter_LPF_1 : filter_HPF_1); -} - - -static int hilo2_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - if (argc > 1 && strcmp(argv[1], "-1") == 0) - return hilo1_getopts(effp, argc - 1, argv + 1); - if (argc > 1 && strcmp(argv[1], "-2") == 0) - ++argv, --argc; - p->width = sqrt(0.5); /* Default to Butterworth */ - return lsx_biquad_getopts(effp, argc, argv, 1, 2, 0, 1, 2, "qohk", - *effp->handler.name == 'l'? filter_LPF : filter_HPF); -} - - -static int bandpass_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_BPF; - if (argc > 1 && strcmp(argv[1], "-c") == 0) - ++argv, --argc, type = filter_BPF_CSG; - return lsx_biquad_getopts(effp, argc, argv, 2, 2, 0, 1, 2, "hkqob", type); -} - - -static int bandrej_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 2, 2, 0, 1, 2, "hkqob", filter_notch); -} - - -static int allpass_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_APF; - int m; - if (argc > 1 && strcmp(argv[1], "-1") == 0) - ++argv, --argc, type = filter_AP1; - else if (argc > 1 && strcmp(argv[1], "-2") == 0) - ++argv, --argc, type = filter_AP2; - m = 1 + (type == filter_APF); - return lsx_biquad_getopts(effp, argc, argv, m, m, 0, 1, 2, "hkqo", type); -} - - -static int tone_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->width = 0.5; - p->fc = *effp->handler.name == 'b'? 100 : 3000; - return lsx_biquad_getopts(effp, argc, argv, 1, 3, 1, 2, 0, "shkqo", - *effp->handler.name == 'b'? filter_lowShelf: filter_highShelf); -} - - -static int equalizer_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 3, 3, 0, 1, 2, "qohk", filter_peakingEQ); -} - - -static int band_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_BPF_SPK; - if (argc > 1 && strcmp(argv[1], "-n") == 0) - ++argv, --argc, type = filter_BPF_SPK_N; - return lsx_biquad_getopts(effp, argc, argv, 1, 2, 0, 1, 2, "hkqo", type); -} - - -static int deemph_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->fc = 5283; - p->width = 0.4845; - p->gain = -9.477; - return lsx_biquad_getopts(effp, argc, argv, 0, 0, 0, 1, 2, "s", filter_deemph); -} - - -static int riaa_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->filter_type = filter_riaa; - (void)argv; - return --argc? lsx_usage(effp) : SOX_SUCCESS; -} - - -static void make_poly_from_roots( - double const * roots, size_t num_roots, double * poly) -{ - size_t i, j; - poly[0] = 1; - poly[1] = -roots[0]; - memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly)); - for (i = 1; i < num_roots; ++i) - for (j = num_roots; j > 0; --j) - poly[j] -= poly[j - 1] * roots[i]; -} - -static int start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - double w0 = 2 * M_PI * p->fc / effp->in_signal.rate; - double A = exp(p->gain / 40 * log(10.)); - double alpha = 0, mult = dB_to_linear(max(p->gain, 0)); - - if (w0 > M_PI) { - lsx_fail("frequency must be less than half the sample-rate (Nyquist rate)"); - return SOX_EOF; - } - - /* Set defaults: */ - p->b0 = p->b1 = p->b2 = p->a1 = p->a2 = 0; - p->a0 = 1; - - if (p->width) switch (p->width_type) { - case width_slope: - alpha = sin(w0)/2 * sqrt((A + 1/A)*(1/p->width - 1) + 2); - break; - - case width_Q: - alpha = sin(w0)/(2*p->width); - break; - - case width_bw_oct: - alpha = sin(w0)*sinh(log(2.)/2 * p->width * w0/sin(w0)); - break; - - case width_bw_Hz: - alpha = sin(w0)/(2*p->fc/p->width); - break; - - case width_bw_kHz: assert(0); /* Shouldn't get here */ - - case width_bw_old: - alpha = tan(M_PI * p->width / effp->in_signal.rate); - break; - } - switch (p->filter_type) { - case filter_LPF: /* H(s) = 1 / (s^2 + s/Q + 1) */ - p->b0 = (1 - cos(w0))/2; - p->b1 = 1 - cos(w0); - p->b2 = (1 - cos(w0))/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_HPF: /* H(s) = s^2 / (s^2 + s/Q + 1) */ - p->b0 = (1 + cos(w0))/2; - p->b1 = -(1 + cos(w0)); - p->b2 = (1 + cos(w0))/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_BPF_CSG: /* H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q) */ - p->b0 = sin(w0)/2; - p->b1 = 0; - p->b2 = -sin(w0)/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_BPF: /* H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain) */ - p->b0 = alpha; - p->b1 = 0; - p->b2 = -alpha; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_notch: /* H(s) = (s^2 + 1) / (s^2 + s/Q + 1) */ - p->b0 = 1; - p->b1 = -2*cos(w0); - p->b2 = 1; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_APF: /* H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1) */ - p->b0 = 1 - alpha; - p->b1 = -2*cos(w0); - p->b2 = 1 + alpha; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_peakingEQ: /* H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1) */ - if (A == 1) - return SOX_EFF_NULL; - p->b0 = 1 + alpha*A; - p->b1 = -2*cos(w0); - p->b2 = 1 - alpha*A; - p->a0 = 1 + alpha/A; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha/A; - break; - - case filter_lowShelf: /* H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1) */ - if (A == 1) - return SOX_EFF_NULL; - p->b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha ); - p->b1 = 2*A*( (A-1) - (A+1)*cos(w0) ); - p->b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha ); - p->a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha; - p->a1 = -2*( (A-1) + (A+1)*cos(w0) ); - p->a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha; - break; - - case filter_deemph: /* See deemph.plt for documentation */ - if (effp->in_signal.rate != 44100) { - lsx_fail("Sample rate must be 44100 (audio-CD)"); - return SOX_EOF; - } - /* Falls through... */ - - case filter_highShelf: /* H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A) */ - if (!A) - return SOX_EFF_NULL; - p->b0 = A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha ); - p->b1 = -2*A*( (A-1) + (A+1)*cos(w0) ); - p->b2 = A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha ); - p->a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha; - p->a1 = 2*( (A-1) - (A+1)*cos(w0) ); - p->a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha; - break; - - case filter_LPF_1: /* single-pole */ - p->a1 = -exp(-w0); - p->b0 = 1 + p->a1; - break; - - case filter_HPF_1: /* single-pole */ - p->a1 = -exp(-w0); - p->b0 = (1 - p->a1)/2; - p->b1 = -p->b0; - break; - - case filter_BPF_SPK: case filter_BPF_SPK_N: { - double bw_Hz; - if (!p->width) - p->width = p->fc / 2; - bw_Hz = p->width_type == width_Q? p->fc / p->width : - p->width_type == width_bw_Hz? p->width : - p->fc * (pow(2., p->width) - 1) * pow(2., -0.5 * p->width); /* bw_oct */ - #include "band.h" /* Has different licence */ - break; - } - - case filter_AP1: /* Experimental 1-pole all-pass from Tom Erbe @ UCSD */ - p->b0 = exp(-w0); - p->b1 = -1; - p->a1 = -exp(-w0); - break; - - case filter_AP2: /* Experimental 2-pole all-pass from Tom Erbe @ UCSD */ - p->b0 = 1 - sin(w0); - p->b1 = -2 * cos(w0); - p->b2 = 1 + sin(w0); - p->a0 = 1 + sin(w0); - p->a1 = -2 * cos(w0); - p->a2 = 1 - sin(w0); - break; - - case filter_riaa: /* http://www.dsprelated.com/showmessage/73300/3.php */ - if (effp->in_signal.rate == 44100) { - static const double zeros[] = {-0.2014898, 0.9233820}; - static const double poles[] = {0.7083149, 0.9924091}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 48000) { - static const double zeros[] = {-0.1766069, 0.9321590}; - static const double poles[] = {0.7396325, 0.9931330}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 88200) { - static const double zeros[] = {-0.1168735, 0.9648312}; - static const double poles[] = {0.8590646, 0.9964002}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 96000) { - static const double zeros[] = {-0.1141486, 0.9676817}; - static const double poles[] = {0.8699137, 0.9966946}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else { - lsx_fail("Sample rate must be 44.1k, 48k, 88.2k, or 96k"); - return SOX_EOF; - } - { /* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */ - double y = 2 * M_PI * 1000 / effp->in_signal.rate; - double b_re = p->b0 + p->b1 * cos(-y) + p->b2 * cos(-2 * y); - double a_re = p->a0 + p->a1 * cos(-y) + p->a2 * cos(-2 * y); - double b_im = p->b1 * sin(-y) + p->b2 * sin(-2 * y); - double a_im = p->a1 * sin(-y) + p->a2 * sin(-2 * y); - double g = 1 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im))); - p->b0 *= g; p->b1 *= g; p->b2 *= g; - } - mult = (p->b0 + p->b1 + p->b2) / (p->a0 + p->a1 + p->a2); - lsx_debug("gain=%f", linear_to_dB(mult)); - break; - } - if (effp->in_signal.mult) - *effp->in_signal.mult /= mult; - return lsx_biquad_start(effp); -} - - -#define BIQUAD_EFFECT(name,group,usage,flags) \ -sox_effect_handler_t const * lsx_##name##_effect_fn(void) { \ - static sox_effect_handler_t handler = { \ - #name, usage, flags, \ - group##_getopts, start, lsx_biquad_flow, 0, 0, 0, sizeof(biquad_t)\ - }; \ - return &handler; \ -} - -BIQUAD_EFFECT(highpass, hilo2, "[-1|-2] frequency [width[q|o|h|k](0.707q)]", 0) -BIQUAD_EFFECT(lowpass, hilo2, "[-1|-2] frequency [width[q|o|h|k]](0.707q)", 0) -BIQUAD_EFFECT(bandpass, bandpass, "[-c] frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(bandreject,bandrej, "frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(allpass, allpass, "frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(bass, tone, "gain [frequency(100) [width[s|h|k|q|o]](0.5s)]", 0) -BIQUAD_EFFECT(treble, tone, "gain [frequency(3000) [width[s|h|k|q|o]](0.5s)]", 0) -BIQUAD_EFFECT(equalizer, equalizer,"frequency width[q|o|h|k] gain", 0) -BIQUAD_EFFECT(band, band, "[-n] center [width[h|k|q|o]]", 0) -BIQUAD_EFFECT(deemph, deemph, NULL, 0) -BIQUAD_EFFECT(riaa, riaa, NULL, 0) diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/effects.c b/freedv/tags/1.2.2/freedv-dev/src/sox/effects.c deleted file mode 100644 index 435412fa..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/effects.c +++ /dev/null @@ -1,544 +0,0 @@ -/* SoX Effects chain (c) 2007 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define LSX_EFF_ALIAS -#include "sox_i.h" -#include -#include -#ifdef HAVE_STRINGS_H - #include -#endif - -#define DEBUG_EFFECTS_CHAIN 0 - -/* Default effect handler functions for do-nothing situations: */ - -static int default_function(sox_effect_t * effp UNUSED) -{ - return SOX_SUCCESS; -} - -/* Pass through samples verbatim */ -int lsx_flow_copy(sox_effect_t * effp UNUSED, const sox_sample_t * ibuf, - sox_sample_t * obuf, size_t * isamp, size_t * osamp) -{ - *isamp = *osamp = min(*isamp, *osamp); - memcpy(obuf, ibuf, *isamp * sizeof(*obuf)); - return SOX_SUCCESS; -} - -/* Inform no more samples to drain */ -static int default_drain(sox_effect_t * effp UNUSED, sox_sample_t *obuf UNUSED, size_t *osamp) -{ - *osamp = 0; - return SOX_EOF; -} - -/* Check that no parameters have been given */ -static int default_getopts(sox_effect_t * effp, int argc, char **argv UNUSED) -{ - return --argc? lsx_usage(effp) : SOX_SUCCESS; -} - -/* Partially initialise the effect structure; signal info will come later */ -sox_effect_t * sox_create_effect(sox_effect_handler_t const * eh) -{ - sox_effect_t * effp = lsx_calloc(1, sizeof(*effp)); - effp->obuf = NULL; - - effp->global_info = sox_get_effects_globals(); - effp->handler = *eh; - if (!effp->handler.getopts) effp->handler.getopts = default_getopts; - if (!effp->handler.start ) effp->handler.start = default_function; - if (!effp->handler.flow ) effp->handler.flow = lsx_flow_copy; - if (!effp->handler.drain ) effp->handler.drain = default_drain; - if (!effp->handler.stop ) effp->handler.stop = default_function; - if (!effp->handler.kill ) effp->handler.kill = default_function; - - effp->priv = lsx_calloc(1, effp->handler.priv_size); - - return effp; -} /* sox_create_effect */ - -int sox_effect_options(sox_effect_t *effp, int argc, char * const argv[]) -{ - int result; - - char * * argv2 = lsx_malloc((argc + 1) * sizeof(*argv2)); - argv2[0] = (char *)effp->handler.name; - memcpy(argv2 + 1, argv, argc * sizeof(*argv2)); - result = effp->handler.getopts(effp, argc + 1, argv2); - free(argv2); - return result; -} /* sox_effect_options */ - -/* Effects chain: */ - -sox_effects_chain_t * sox_create_effects_chain( - sox_encodinginfo_t const * in_enc, sox_encodinginfo_t const * out_enc) -{ - sox_effects_chain_t * result = lsx_calloc(1, sizeof(sox_effects_chain_t)); - result->global_info = *sox_get_effects_globals(); - result->in_enc = in_enc; - result->out_enc = out_enc; - return result; -} /* sox_create_effects_chain */ - -void sox_delete_effects_chain(sox_effects_chain_t *ecp) -{ - if (ecp && ecp->length) - sox_delete_effects(ecp); - free(ecp->effects); - free(ecp); -} /* sox_delete_effects_chain */ - -/* Effect can call in start() or flow() to set minimum input size to flow() */ -int lsx_effect_set_imin(sox_effect_t * effp, size_t imin) -{ - if (imin > sox_globals.bufsiz / effp->flows) { - lsx_fail("sox_bufsiz not big enough"); - return SOX_EOF; - } - - effp->imin = imin; - return SOX_SUCCESS; -} - -/* Effects table to be extended in steps of EFF_TABLE_STEP */ -#define EFF_TABLE_STEP 8 - -/* Add an effect to the chain. *in is the input signal for this effect. *out is - * a suggestion as to what the output signal should be, but depending on its - * given options and *in, the effect can choose to do differently. Whatever - * output rate and channels the effect does produce are written back to *in, - * ready for the next effect in the chain. - */ -int sox_add_effect(sox_effects_chain_t * chain, sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t const * out) -{ - int ret, (*start)(sox_effect_t * effp) = effp->handler.start; - unsigned f; - sox_effect_t eff0; /* Copy of effect for flow 0 before calling start */ - - effp->global_info = &chain->global_info; - effp->in_signal = *in; - effp->out_signal = *out; - effp->in_encoding = chain->in_enc; - effp->out_encoding = chain->out_enc; - if (!(effp->handler.flags & SOX_EFF_CHAN)) - effp->out_signal.channels = in->channels; - if (!(effp->handler.flags & SOX_EFF_RATE)) - effp->out_signal.rate = in->rate; - if (!(effp->handler.flags & SOX_EFF_PREC)) - effp->out_signal.precision = (effp->handler.flags & SOX_EFF_MODIFY)? - in->precision : SOX_SAMPLE_PRECISION; - if (!(effp->handler.flags & SOX_EFF_GAIN)) - effp->out_signal.mult = in->mult; - - effp->flows = - (effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->in_signal.channels; - effp->clips = 0; - effp->imin = 0; - eff0 = *effp, eff0.priv = lsx_memdup(eff0.priv, eff0.handler.priv_size); - eff0.in_signal.mult = NULL; /* Only used in channel 0 */ - ret = start(effp); - if (ret == SOX_EFF_NULL) { - lsx_report("has no effect in this configuration"); - free(eff0.priv); - free(effp->priv); - effp->priv = NULL; - return SOX_SUCCESS; - } - if (ret != SOX_SUCCESS) { - free(eff0.priv); - return SOX_EOF; - } - if (in->mult) - lsx_debug("mult=%g", *in->mult); - - if (!(effp->handler.flags & SOX_EFF_LENGTH)) { - effp->out_signal.length = in->length; - if (effp->out_signal.length != SOX_UNKNOWN_LEN) { - if (effp->handler.flags & SOX_EFF_CHAN) - effp->out_signal.length = - effp->out_signal.length / in->channels * effp->out_signal.channels; - if (effp->handler.flags & SOX_EFF_RATE) - effp->out_signal.length = - effp->out_signal.length / in->rate * effp->out_signal.rate + .5; - } - } - - *in = effp->out_signal; - - if (chain->length == chain->table_size) { - chain->table_size += EFF_TABLE_STEP; - lsx_debug_more("sox_add_effect: extending effects table, " - "new size = %lu", (unsigned long)chain->table_size); - lsx_revalloc(chain->effects, chain->table_size); - } - - chain->effects[chain->length] = - lsx_calloc(effp->flows, sizeof(chain->effects[chain->length][0])); - chain->effects[chain->length][0] = *effp; - - for (f = 1; f < effp->flows; ++f) { - chain->effects[chain->length][f] = eff0; - chain->effects[chain->length][f].flow = f; - chain->effects[chain->length][f].priv = lsx_memdup(eff0.priv, eff0.handler.priv_size); - if (start(&chain->effects[chain->length][f]) != SOX_SUCCESS) { - free(eff0.priv); - return SOX_EOF; - } - } - - ++chain->length; - free(eff0.priv); - return SOX_SUCCESS; -} - -static int flow_effect(sox_effects_chain_t * chain, size_t n) -{ - sox_effect_t * effp1 = &chain->effects[n - 1][0]; - sox_effect_t * effp = &chain->effects[n][0]; - int effstatus = SOX_SUCCESS, f = 0; - size_t i; - const sox_sample_t *ibuf; - size_t idone = effp1->oend - effp1->obeg; - size_t obeg = sox_globals.bufsiz - effp->oend; -#if DEBUG_EFFECTS_CHAIN - size_t pre_idone = idone; - size_t pre_odone = obeg; -#endif - - if (effp->flows == 1) { /* Run effect on all channels at once */ - idone -= idone % effp->in_signal.channels; - effstatus = effp->handler.flow(effp, &effp1->obuf[effp1->obeg], - &effp->obuf[effp->oend], &idone, &obeg); - if (obeg % effp->out_signal.channels != 0) { - lsx_fail("multi-channel effect flowed asymmetrically!"); - effstatus = SOX_EOF; - } - } else { /* Run effect on each channel individually */ - sox_sample_t *obuf = &effp->obuf[effp->oend]; - size_t idone_last = 0, odone_last = 0; /* Initialised to prevent warning */ - - ibuf = &effp1->obuf[effp1->obeg]; - for (i = 0; i < idone; i += effp->flows) - for (f = 0; f < (int)effp->flows; ++f) - chain->ibufc[f][i / effp->flows] = *ibuf++; - -#ifdef HAVE_OPENMP - if (sox_globals.use_threads && effp->flows > 1) - { - #pragma omp parallel for - for (f = 0; f < (int)effp->flows; ++f) { - size_t idonec = idone / effp->flows; - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.flow(&chain->effects[n][f], - chain->ibufc[f], chain->obufc[f], &idonec, &odonec); - if (!f) { - idone_last = idonec; - odone_last = odonec; - } - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - } - else /* sox_globals.use_threads */ -#endif - { - for (f = 0; f < (int)effp->flows; ++f) { - size_t idonec = idone / effp->flows; - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.flow(&chain->effects[n][f], - chain->ibufc[f], chain->obufc[f], &idonec, &odonec); - if (f && (idonec != idone_last || odonec != odone_last)) { - lsx_fail("flowed asymmetrically!"); - effstatus = SOX_EOF; - } - idone_last = idonec; - odone_last = odonec; - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - } - - for (i = 0; i < odone_last; ++i) - for (f = 0; f < (int)effp->flows; ++f) - *obuf++ = chain->obufc[f][i]; - - idone = effp->flows * idone_last; - obeg = effp->flows * odone_last; - } -#if DEBUG_EFFECTS_CHAIN - lsx_report("flow: %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR, - pre_idone, pre_odone, idone, obeg); -#endif - effp1->obeg += idone; - if (effp1->obeg == effp1->oend) - effp1->obeg = effp1->oend = 0; - else if (effp1->oend - effp1->obeg < effp->imin ) { /* Need to refill? */ - memmove(effp1->obuf, &effp1->obuf[effp1->obeg], (effp1->oend - effp1->obeg) * sizeof(*effp1->obuf)); - effp1->oend -= effp1->obeg; - effp1->obeg = 0; - } - - effp->oend += obeg; - - return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF; -} - -/* The same as flow_effect but with no input */ -static int drain_effect(sox_effects_chain_t * chain, size_t n) -{ - sox_effect_t * effp = &chain->effects[n][0]; - int effstatus = SOX_SUCCESS; - size_t i, f; - size_t obeg = sox_globals.bufsiz - effp->oend; -#if DEBUG_EFFECTS_CHAIN - size_t pre_odone = obeg; -#endif - - if (effp->flows == 1) { /* Run effect on all channels at once */ - effstatus = effp->handler.drain(effp, &effp->obuf[effp->oend], &obeg); - if (obeg % effp->out_signal.channels != 0) { - lsx_fail("multi-channel effect drained asymmetrically!"); - effstatus = SOX_EOF; - } - } else { /* Run effect on each channel individually */ - sox_sample_t *obuf = &effp->obuf[effp->oend]; - size_t odone_last = 0; /* Initialised to prevent warning */ - - for (f = 0; f < effp->flows; ++f) { - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.drain(&chain->effects[n][f], chain->obufc[f], &odonec); - if (f && (odonec != odone_last)) { - lsx_fail("drained asymmetrically!"); - effstatus = SOX_EOF; - } - odone_last = odonec; - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - - for (i = 0; i < odone_last; ++i) - for (f = 0; f < effp->flows; ++f) - *obuf++ = chain->obufc[f][i]; - obeg = f * odone_last; - } -#if DEBUG_EFFECTS_CHAIN - lsx_report("drain: %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR, - (size_t)0, pre_odone, (size_t)0, obeg); -#endif - if (!obeg) /* This is the only thing that drain has and flow hasn't */ - effstatus = SOX_EOF; - - effp->oend += obeg; - - return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF; -} - -/* Flow data through the effects chain until an effect or callback gives EOF */ -int sox_flow_effects(sox_effects_chain_t * chain, int (* callback)(sox_bool all_done, void * client_data), void * client_data) -{ - int flow_status = SOX_SUCCESS; - size_t e, source_e = 0; /* effect indices */ - size_t f, max_flows = 0; - sox_bool draining = sox_true; - - for (e = 0; e < chain->length; ++e) { - chain->effects[e][0].obuf = lsx_realloc(chain->effects[e][0].obuf, - sox_globals.bufsiz * sizeof(chain->effects[e][0].obuf[0])); - /* Possibly there is already a buffer, if this is a used effect; - it may still contain samples in that case. */ - /* Memory will be freed by sox_delete_effect() later. */ - max_flows = max(max_flows, chain->effects[e][0].flows); - } - if (max_flows == 1) /* don't need interleave buffers */ - max_flows = 0; - chain->ibufc = lsx_calloc(max_flows, sizeof(*chain->ibufc)); - chain->obufc = lsx_calloc(max_flows, sizeof(*chain->obufc)); - for (f = 0; f < max_flows; ++f) { - chain->ibufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->ibufc[f][0])); - chain->obufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->obufc[f][0])); - } - - e = chain->length - 1; - while (source_e < chain->length) { -#define have_imin (e > 0 && e < chain->length && chain->effects[e - 1][0].oend - chain->effects[e - 1][0].obeg >= chain->effects[e][0].imin) - size_t osize = chain->effects[e][0].oend - chain->effects[e][0].obeg; - if (e == source_e && (draining || !have_imin)) { - if (drain_effect(chain, e) == SOX_EOF) { - ++source_e; - draining = sox_false; - } - } else if (have_imin && flow_effect(chain, e) == SOX_EOF) { - flow_status = SOX_EOF; - if (e == chain->length - 1) - break; - source_e = e; - draining = sox_true; - } - if (e < chain->length && chain->effects[e][0].oend - chain->effects[e][0].obeg > osize) /* False for output */ - ++e; - else if (e == source_e) - draining = sox_true; - else if ((int)--e < (int)source_e) - e = source_e; - - if (callback && callback(source_e == chain->length, client_data) != SOX_SUCCESS) { - flow_status = SOX_EOF; /* Client has requested to stop the flow. */ - break; - } - } - - for (f = 0; f < max_flows; ++f) { - free(chain->ibufc[f]); - free(chain->obufc[f]); - } - free(chain->obufc); - free(chain->ibufc); - - return flow_status; -} - -sox_uint64_t sox_effects_clips(sox_effects_chain_t * chain) -{ - unsigned i, f; - uint64_t clips = 0; - for (i = 1; i < chain->length - 1; ++i) - for (f = 0; f < chain->effects[i][0].flows; ++f) - clips += chain->effects[i][f].clips; - return clips; -} - -sox_uint64_t sox_stop_effect(sox_effect_t *effp) -{ - unsigned f; - uint64_t clips = 0; - - for (f = 0; f < effp->flows; ++f) { - effp[f].handler.stop(&effp[f]); - clips += effp[f].clips; - } - return clips; -} - -void sox_push_effect_last(sox_effects_chain_t *chain, sox_effect_t *effp) -{ - if (chain->length == chain->table_size) { - chain->table_size += EFF_TABLE_STEP; - lsx_debug_more("sox_push_effect_last: extending effects table, " - "new size = %lu", (unsigned long)chain->table_size); - lsx_revalloc(chain->effects, chain->table_size); - } - - chain->effects[chain->length++] = effp; -} /* sox_push_effect_last */ - -sox_effect_t *sox_pop_effect_last(sox_effects_chain_t *chain) -{ - if (chain->length > 0) - { - sox_effect_t *effp; - chain->length--; - effp = chain->effects[chain->length]; - chain->effects[chain->length] = NULL; - return effp; - } - else - return NULL; -} /* sox_pop_effect_last */ - -/* Free resources related to effect. - * Note: This currently closes down the effect which might - * not be obvious from name. - */ -void sox_delete_effect(sox_effect_t *effp) -{ - uint64_t clips; - unsigned f; - - if ((clips = sox_stop_effect(effp)) != 0) - lsx_warn("%s clipped %" PRIu64 " samples; decrease volume?", - effp->handler.name, clips); - if (effp->obeg != effp->oend) - lsx_debug("output buffer still held %" PRIuPTR " samples; dropped.", - (effp->oend - effp->obeg)/effp->out_signal.channels); - /* May or may not indicate a problem; it is normal if the user aborted - processing, or if an effect like "trim" stopped early. */ - effp->handler.kill(effp); /* N.B. only one kill; not one per flow */ - for (f = 0; f < effp->flows; ++f) - free(effp[f].priv); - free(effp->obuf); - free(effp); -} - -void sox_delete_effect_last(sox_effects_chain_t *chain) -{ - if (chain->length > 0) - { - chain->length--; - sox_delete_effect(chain->effects[chain->length]); - chain->effects[chain->length] = NULL; - } -} /* sox_delete_effect_last */ - -/* Remove all effects from the chain. - * Note: This currently closes down the effect which might - * not be obvious from name. - */ -void sox_delete_effects(sox_effects_chain_t * chain) -{ - size_t e; - - for (e = 0; e < chain->length; ++e) { - sox_delete_effect(chain->effects[e]); - chain->effects[e] = NULL; - } - chain->length = 0; -} - -/*----------------------------- Effects library ------------------------------*/ - -static sox_effect_fn_t s_sox_effect_fns[] = { -#define EFFECT(f) lsx_##f##_effect_fn, -#include "effects.h" -#undef EFFECT - NULL -}; - -const sox_effect_fn_t* -sox_get_effect_fns(void) -{ - return s_sox_effect_fns; -} - -/* Find a named effect in the effects library */ -sox_effect_handler_t const * sox_find_effect(char const * name) -{ - int e; - sox_effect_fn_t const * fns = sox_get_effect_fns(); - for (e = 0; fns[e]; ++e) { - const sox_effect_handler_t *eh = fns[e] (); - if (eh && eh->name && strcasecmp(eh->name, name) == 0) - return eh; /* Found it. */ - } - return NULL; -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/effects.h b/freedv/tags/1.2.2/freedv-dev/src/sox/effects.h deleted file mode 100644 index 8d7025c8..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/effects.h +++ /dev/null @@ -1,22 +0,0 @@ -/* This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* Manually edited for FreeDV to contain just the effects we need */ - - EFFECT(bass) - EFFECT(highpass) - EFFECT(treble) - EFFECT(equalizer) - diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/effects_i.c b/freedv/tags/1.2.2/freedv-dev/src/sox/effects_i.c deleted file mode 100644 index e5770a94..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/effects_i.c +++ /dev/null @@ -1,379 +0,0 @@ -/* Implements a libSoX internal interface for implementing effects. - * All public functions & data are prefixed with lsx_ . - * - * Copyright (c) 2005-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define LSX_EFF_ALIAS -#include "sox_i.h" -#include -#include - -int lsx_usage(sox_effect_t * effp) -{ - if (effp->handler.usage) - lsx_fail("usage: %s", effp->handler.usage); - else - lsx_fail("this effect takes no parameters"); - return SOX_EOF; -} - -char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n) -{ - if (!*usage) { - size_t i, len; - for (len = i = 0; i < n; len += strlen(lines[i++]) + 1); - *usage = lsx_malloc(len); /* FIXME: this memory will never be freed */ - strcpy(*usage, lines[0]); - for (i = 1; i < n; ++i) { - strcat(*usage, "\n"); - strcat(*usage, lines[i]); - } - } - return *usage; -} - -static lsx_enum_item const s_lsx_wave_enum[] = { - LSX_ENUM_ITEM(SOX_WAVE_,SINE) - LSX_ENUM_ITEM(SOX_WAVE_,TRIANGLE) - {0, 0}}; - -lsx_enum_item const * lsx_get_wave_enum(void) -{ - return s_lsx_wave_enum; -} - -void lsx_generate_wave_table( - lsx_wave_t wave_type, - sox_data_t data_type, - void *table, - size_t table_size, - double min, - double max, - double phase) -{ - uint32_t t; - uint32_t phase_offset = phase / M_PI / 2 * table_size + 0.5; - - for (t = 0; t < table_size; t++) - { - uint32_t point = (t + phase_offset) % table_size; - double d; - switch (wave_type) - { - case SOX_WAVE_SINE: - d = (sin((double)point / table_size * 2 * M_PI) + 1) / 2; - break; - - case SOX_WAVE_TRIANGLE: - d = (double)point * 2 / table_size; - switch (4 * point / table_size) - { - case 0: d = d + 0.5; break; - case 1: case 2: d = 1.5 - d; break; - case 3: d = d - 1.5; break; - } - break; - - default: /* Oops! FIXME */ - d = 0.0; /* Make sure we have a value */ - break; - } - d = d * (max - min) + min; - switch (data_type) - { - case SOX_FLOAT: - { - float *fp = (float *)table; - *fp++ = (float)d; - table = fp; - continue; - } - case SOX_DOUBLE: - { - double *dp = (double *)table; - *dp++ = d; - table = dp; - continue; - } - default: break; - } - d += d < 0? -0.5 : +0.5; - switch (data_type) - { - case SOX_SHORT: - { - short *sp = table; - *sp++ = (short)d; - table = sp; - continue; - } - case SOX_INT: - { - int *ip = table; - *ip++ = (int)d; - table = ip; - continue; - } - default: break; - } - } -} - -/* - * lsx_parsesamples - * - * Parse a string for # of samples. If string ends with a 's' - * then the string is interpreted as a user calculated # of samples. - * If string contains ':' or '.' or if it ends with a 't' then its - * treated as an amount of time. This is converted into seconds and - * fraction of seconds and then use the sample rate to calculate - * # of samples. - * Returns NULL on error, pointer to next char to parse otherwise. - */ -char const * lsx_parsesamples(sox_rate_t rate, const char *str0, uint64_t *samples, int def) -{ - int i, found_samples = 0, found_time = 0; - char const * end; - char const * pos; - sox_bool found_colon, found_dot; - char * str = (char *)str0; - - for (;*str == ' '; ++str); - for (end = str; *end && strchr("0123456789:.ets", *end); ++end); - if (end == str) - return NULL; - - pos = strchr(str, ':'); - found_colon = pos && pos < end; - - pos = strchr(str, '.'); - found_dot = pos && pos < end; - - if (found_colon || found_dot || *(end-1) == 't') - found_time = 1; - else if (*(end-1) == 's') - found_samples = 1; - - if (found_time || (def == 't' && !found_samples)) { - for (*samples = 0, i = 0; *str != '.' && i < 3; ++i) { - char * last_str = str; - long part = strtol(str, &str, 10); - if (!i && str == last_str) - return NULL; - *samples += rate * part; - if (i < 2) { - if (*str != ':') - break; - ++str; - *samples *= 60; - } - } - if (*str == '.') { - char * last_str = str; - double part = strtod(str, &str); - if (str == last_str) - return NULL; - *samples += rate * part + .5; - } - return *str == 't'? str + 1 : str; - } - { - char * last_str = str; - double part = strtod(str, &str); - if (str == last_str) - return NULL; - *samples = part + .5; - return *str == 's'? str + 1 : str; - } -} - -#if 0 - -#include - -#define TEST(st, samp, len) \ - str = st; \ - next = lsx_parsesamples(10000, str, &samples, 't'); \ - assert(samples == samp && next == str + len); - -int main(int argc, char * * argv) -{ - char const * str, * next; - uint64_t samples; - - TEST("0" , 0, 1) - TEST("1" , 10000, 1) - - TEST("0s" , 0, 2) - TEST("0s,", 0, 2) - TEST("0s/", 0, 2) - TEST("0s@", 0, 2) - - TEST("0t" , 0, 2) - TEST("0t,", 0, 2) - TEST("0t/", 0, 2) - TEST("0t@", 0, 2) - - TEST("1s" , 1, 2) - TEST("1s,", 1, 2) - TEST("1s/", 1, 2) - TEST("1s@", 1, 2) - TEST(" 01s" , 1, 4) - TEST("1e6s" , 1000000, 4) - - TEST("1t" , 10000, 2) - TEST("1t,", 10000, 2) - TEST("1t/", 10000, 2) - TEST("1t@", 10000, 2) - TEST("1.1t" , 11000, 4) - TEST("1.1t,", 11000, 4) - TEST("1.1t/", 11000, 4) - TEST("1.1t@", 11000, 4) - TEST("1e6t" , 10000, 1) - - TEST(".0", 0, 2) - TEST("0.0", 0, 3) - TEST("0:0.0", 0, 5) - TEST("0:0:0.0", 0, 7) - - TEST(".1", 1000, 2) - TEST(".10", 1000, 3) - TEST("0.1", 1000, 3) - TEST("1.1", 11000, 3) - TEST("1:1.1", 611000, 5) - TEST("1:1:1.1", 36611000, 7) - TEST("1:1", 610000, 3) - TEST("1:01", 610000, 4) - TEST("1:1:1", 36610000, 5) - TEST("1:", 600000, 2) - TEST("1::", 36000000, 3) - - TEST("0.444444", 4444, 8) - TEST("0.555555", 5556, 8) - - assert(!lsx_parsesamples(10000, "x", &samples, 't')); - return 0; -} -#endif - -/* a note is given as an int, - * 0 => 440 Hz = A - * >0 => number of half notes 'up', - * <0 => number of half notes down, - * example 12 => A of next octave, 880Hz - * - * calculated by freq = 440Hz * 2**(note/12) - */ -static double calc_note_freq(double note, int key) -{ - if (key != INT_MAX) { /* Just intonation. */ - static const int n[] = {16, 9, 6, 5, 4, 7}; /* Numerator. */ - static const int d[] = {15, 8, 5, 4, 3, 5}; /* Denominator. */ - static double j[13]; /* Just semitones */ - int i, m = floor(note); - - if (!j[1]) for (i = 1; i <= 12; ++i) - j[i] = i <= 6? log((double)n[i - 1] / d[i - 1]) / log(2.) : 1 - j[12 - i]; - note -= m; - m -= key = m - ((INT_MAX / 2 - ((INT_MAX / 2) % 12) + m - key) % 12); - return 440 * pow(2., key / 12. + j[m] + (j[m + 1] - j[m]) * note); - } - return 440 * pow(2., note / 12); -} - -int lsx_parse_note(char const * text, char * * end_ptr) -{ - int result = INT_MAX; - - if (*text >= 'A' && *text <= 'G') { - result = (int)(5/3. * (*text++ - 'A') + 9.5) % 12 - 9; - if (*text == 'b') {--result; ++text;} - else if (*text == '#') {++result; ++text;} - if (isdigit((unsigned char)*text)) - result += 12 * (*text++ - '4'); - } - *end_ptr = (char *)text; - return result; -} - -/* Read string 'text' and convert to frequency. - * 'text' can be a positive number which is the frequency in Hz. - * If 'text' starts with a '%' and a following number the corresponding - * note is calculated. - * Return -1 on error. - */ -double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key) -{ - double result; - - if (*text == '%') { - result = strtod(text + 1, end_ptr); - if (*end_ptr == text + 1) - return -1; - return calc_note_freq(result, key); - } - if (*text >= 'A' && *text <= 'G') { - int result2 = lsx_parse_note(text, end_ptr); - return result2 == INT_MAX? - 1 : calc_note_freq((double)result2, key); - } - result = strtod(text, end_ptr); - if (end_ptr) { - if (*end_ptr == text) - return -1; - if (**end_ptr == 'k') { - result *= 1000; - ++*end_ptr; - } - } - return result < 0 ? -1 : result; -} - -FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename) -{ - FILE * file; - - if (!filename || !strcmp(filename, "-")) { - if (effp->global_info->global_info->stdin_in_use_by) { - lsx_fail("stdin already in use by `%s'", effp->global_info->global_info->stdin_in_use_by); - return NULL; - } - effp->global_info->global_info->stdin_in_use_by = effp->handler.name; - file = stdin; - } - else if (!(file = fopen(filename, "r"))) { - lsx_fail("couldn't open file %s: %s", filename, strerror(errno)); - return NULL; - } - return file; -} - -int lsx_effects_init(void) -{ - #ifndef __FREEDV__ - init_fft_cache(); - #endif - return SOX_SUCCESS; -} - -int lsx_effects_quit(void) -{ - #ifndef __FREEDV__ - clear_fft_cache(); - #endif - return SOX_SUCCESS; -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/formats_i.c b/freedv/tags/1.2.2/freedv-dev/src/sox/formats_i.c deleted file mode 100644 index 17c40615..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/formats_i.c +++ /dev/null @@ -1,487 +0,0 @@ -/* Implements a libSoX internal interface for use in implementing file formats. - * All public functions & data are prefixed with lsx_ . - * - * (c) 2005-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include -#include -#include - -void lsx_fail_errno(sox_format_t * ft, int sox_errno, const char *fmt, ...) -{ - va_list args; - - ft->sox_errno = sox_errno; - - va_start(args, fmt); -#ifdef HAVE_VSNPRINTF - vsnprintf(ft->sox_errstr, sizeof(ft->sox_errstr), fmt, args); -#else - vsprintf(ft->sox_errstr, fmt, args); -#endif - va_end(args); - ft->sox_errstr[255] = '\0'; -} - -void lsx_set_signal_defaults(sox_format_t * ft) -{ - if (!ft->signal.rate ) ft->signal.rate = SOX_DEFAULT_RATE; - if (!ft->signal.precision) ft->signal.precision = SOX_DEFAULT_PRECISION; - if (!ft->signal.channels ) ft->signal.channels = SOX_DEFAULT_CHANNELS; - - if (!ft->encoding.bits_per_sample) - ft->encoding.bits_per_sample = ft->signal.precision; - if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN) - ft->encoding.encoding = SOX_ENCODING_SIGN2; -} - -#ifndef __FREEDV__ -int lsx_check_read_params(sox_format_t * ft, unsigned channels, - sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, - uint64_t num_samples, sox_bool check_length) -{ - ft->signal.length = ft->signal.length == SOX_IGNORE_LENGTH? SOX_UNSPEC : num_samples; - - if (ft->seekable) - ft->data_start = lsx_tell(ft); - - if (channels && ft->signal.channels && ft->signal.channels != channels) - lsx_warn("`%s': overriding number of channels", ft->filename); - else ft->signal.channels = channels; - - if (rate && ft->signal.rate && ft->signal.rate != rate) - lsx_warn("`%s': overriding sample rate", ft->filename); - else ft->signal.rate = rate; - - if (encoding && ft->encoding.encoding && ft->encoding.encoding != encoding) - lsx_warn("`%s': overriding encoding type", ft->filename); - else ft->encoding.encoding = encoding; - - if (bits_per_sample && ft->encoding.bits_per_sample && ft->encoding.bits_per_sample != bits_per_sample) - lsx_warn("`%s': overriding encoding size", ft->filename); - ft->encoding.bits_per_sample = bits_per_sample; - - if (check_length && ft->encoding.bits_per_sample && lsx_filelength(ft)) { - uint64_t calculated_length = div_bits(lsx_filelength(ft) - ft->data_start, ft->encoding.bits_per_sample); - if (!ft->signal.length) - ft->signal.length = calculated_length; - else if (num_samples != calculated_length) - lsx_warn("`%s': file header gives the total number of samples as %" PRIu64 " but file length indicates the number is in fact %" PRIu64, ft->filename, num_samples, calculated_length); - } - - if (sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample)) - return SOX_SUCCESS; - lsx_fail_errno(ft, EINVAL, "invalid format for this file type"); - return SOX_EOF; -} -#endif - -/* Read in a buffer of data of length len bytes. - * Returns number of bytes read. - */ -size_t lsx_readbuf(sox_format_t * ft, void *buf, size_t len) -{ - size_t ret = fread(buf, (size_t) 1, len, (FILE*)ft->fp); - if (ret != len && ferror((FILE*)ft->fp)) - lsx_fail_errno(ft, errno, "lsx_readbuf"); - ft->tell_off += ret; - return ret; -} - -/* Skip input without seeking. */ -int lsx_skipbytes(sox_format_t * ft, size_t n) -{ - unsigned char trash; - - while (n--) - if (lsx_readb(ft, &trash) == SOX_EOF) - return (SOX_EOF); - - return (SOX_SUCCESS); -} - -/* Pad output. */ -int lsx_padbytes(sox_format_t * ft, size_t n) -{ - while (n--) - if (lsx_writeb(ft, '\0') == SOX_EOF) - return (SOX_EOF); - - return (SOX_SUCCESS); -} - -/* Write a buffer of data of length bytes. - * Returns number of bytes written. - */ -size_t lsx_writebuf(sox_format_t * ft, void const * buf, size_t len) -{ - size_t ret = fwrite(buf, (size_t) 1, len, (FILE*)ft->fp); - if (ret != len) { - lsx_fail_errno(ft, errno, "error writing output file"); - clearerr((FILE*)ft->fp); /* Allows us to seek back to write header */ - } - ft->tell_off += ret; - return ret; -} - -uint64_t lsx_filelength(sox_format_t * ft) -{ - struct stat st; - int ret = fstat(fileno((FILE*)ft->fp), &st); - - return (!ret && (st.st_mode & S_IFREG))? (uint64_t)st.st_size : 0; -} - -int lsx_flush(sox_format_t * ft) -{ - return fflush((FILE*)ft->fp); -} - -off_t lsx_tell(sox_format_t * ft) -{ - return ft->seekable? (off_t)ftello((FILE*)ft->fp) : (off_t)ft->tell_off; -} - -int lsx_eof(sox_format_t * ft) -{ - return feof((FILE*)ft->fp); -} - -int lsx_error(sox_format_t * ft) -{ - return ferror((FILE*)ft->fp); -} - -void lsx_rewind(sox_format_t * ft) -{ - rewind((FILE*)ft->fp); - ft->tell_off = 0; -} - -void lsx_clearerr(sox_format_t * ft) -{ - clearerr((FILE*)ft->fp); - ft->sox_errno = 0; -} - -int lsx_unreadb(sox_format_t * ft, unsigned b) -{ - return ungetc((int)b, ft->fp); -} - -/* Implements traditional fseek() behavior. Meant to abstract out - * file operations so that they could one day also work on memory - * buffers. - * - * N.B. Can only seek forwards on non-seekable streams! - */ -int lsx_seeki(sox_format_t * ft, off_t offset, int whence) -{ - if (ft->seekable == 0) { - /* If a stream peel off chars else EPERM */ - if (whence == SEEK_CUR) { - while (offset > 0 && !feof((FILE*)ft->fp)) { - getc((FILE*)ft->fp); - offset--; - ++ft->tell_off; - } - if (offset) - lsx_fail_errno(ft,SOX_EOF, "offset past EOF"); - else - ft->sox_errno = SOX_SUCCESS; - } else - lsx_fail_errno(ft,SOX_EPERM, "file not seekable"); - } else { - if (fseeko((FILE*)ft->fp, offset, whence) == -1) - lsx_fail_errno(ft,errno, "%s", strerror(errno)); - else - ft->sox_errno = SOX_SUCCESS; - } - return ft->sox_errno; -} - -int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, off_t to_sample) -{ - double wide_sample = to_sample - (to_sample % ft->signal.channels); - double to_d = wide_sample * ft->encoding.bits_per_sample / 8; - off_t to = to_d; - return (to != to_d)? SOX_EOF : lsx_seeki(ft, (byte_offset + to), SEEK_SET); -} - -/* Read and write known datatypes in "machine format". Swap if indicated. - * They all return SOX_EOF on error and SOX_SUCCESS on success. - */ -/* Read n-char string (and possibly null-terminating). - * Stop reading and null-terminate string if either a 0 or \n is reached. - */ -int lsx_reads(sox_format_t * ft, char *c, size_t len) -{ - char *sc; - char in; - - sc = c; - do - { - if (lsx_readbuf(ft, &in, (size_t)1) != 1) - { - *sc = 0; - return (SOX_EOF); - } - if (in == 0 || in == '\n') - break; - - *sc = in; - sc++; - } while (sc - c < (ptrdiff_t)len); - *sc = 0; - return(SOX_SUCCESS); -} - -/* Write null-terminated string (without \0). */ -int lsx_writes(sox_format_t * ft, char const * c) -{ - if (lsx_writebuf(ft, c, strlen(c)) != strlen(c)) - return(SOX_EOF); - return(SOX_SUCCESS); -} - -/* return swapped 32-bit float */ -static void lsx_swapf(float * f) -{ - union { - uint32_t dw; - float f; - } u; - - u.f= *f; - u.dw= (u.dw>>24) | ((u.dw>>8)&0xff00) | ((u.dw<<8)&0xff0000) | (u.dw<<24); - *f = u.f; -} - -static void swap(void * data, size_t len) -{ - uint8_t * bytes = (uint8_t *)data; - size_t i; - - for (i = 0; i < len / 2; ++i) { - char tmp = bytes[i]; - bytes[i] = bytes[len - 1 - i]; - bytes[len - 1 - i] = tmp; - } -} - -static double lsx_swapdf(double data) -{ - swap(&data, sizeof(data)); - return data; -} - -static uint64_t lsx_swapqw(uint64_t data) -{ - swap(&data, sizeof(data)); - return data; -} - -/* Lookup table to reverse the bit order of a byte. ie MSB become LSB */ -static uint8_t const cswap[256] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, - 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, - 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, - 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, - 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, - 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, - 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, - 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, - 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, - 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, - 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, - 0x3F, 0xBF, 0x7F, 0xFF -}; - -/* Utilities to byte-swap values, use libc optimized macros if possible */ -#define TWIDDLE_BYTE(ub, type) \ - do { \ - if (ft->encoding.reverse_bits) \ - ub = cswap[ub]; \ - if (ft->encoding.reverse_nibbles) \ - ub = ((ub & 15) << 4) | (ub >> 4); \ - } while (0); - -#define TWIDDLE_WORD(uw, type) \ - if (ft->encoding.reverse_bytes) \ - uw = lsx_swap ## type(uw); - -#define TWIDDLE_FLOAT(f, type) \ - if (ft->encoding.reverse_bytes) \ - lsx_swapf(&f); - -/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte - types). */ -#define READ_FUNC(type, size, ctype, twiddle) \ - size_t lsx_read_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nread; \ - nread = lsx_readbuf(ft, buf, len * size) / size; \ - for (n = 0; n < nread; n++) \ - twiddle(buf[n], type); \ - return nread; \ - } - -/* Unpack a 3-byte value from a uint8_t * */ -#define sox_unpack3(p) (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN? \ - ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16)) : \ - ((p)[2] | ((p)[1] << 8) | ((p)[0] << 16))) - -/* This (slower) macro works for unaligned types (e.g. 3-byte types) - that need to be unpacked. */ -#define READ_FUNC_UNPACK(type, size, ctype, twiddle) \ - size_t lsx_read_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nread; \ - uint8_t *data = lsx_malloc(size * len); \ - nread = lsx_readbuf(ft, data, len * size) / size; \ - for (n = 0; n < nread; n++) \ - buf[n] = sox_unpack ## size(data + n * size); \ - free(data); \ - return n; \ - } - -READ_FUNC(b, 1, uint8_t, TWIDDLE_BYTE) -READ_FUNC(w, 2, uint16_t, TWIDDLE_WORD) -READ_FUNC_UNPACK(3, 3, sox_uint24_t, TWIDDLE_WORD) -READ_FUNC(dw, 4, uint32_t, TWIDDLE_WORD) -READ_FUNC(qw, 8, uint64_t, TWIDDLE_WORD) -READ_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT) -READ_FUNC(df, sizeof(double), double, TWIDDLE_WORD) - -#define READ1_FUNC(type, ctype) \ -int lsx_read ## type(sox_format_t * ft, ctype * datum) { \ - if (lsx_read_ ## type ## _buf(ft, datum, (size_t)1) == 1) \ - return SOX_SUCCESS; \ - if (!lsx_error(ft)) \ - lsx_fail_errno(ft, errno, premature_eof); \ - return SOX_EOF; \ -} - -static char const premature_eof[] = "premature EOF"; - -READ1_FUNC(b, uint8_t) -READ1_FUNC(w, uint16_t) -READ1_FUNC(3, sox_uint24_t) -READ1_FUNC(dw, uint32_t) -READ1_FUNC(qw, uint64_t) -READ1_FUNC(f, float) -READ1_FUNC(df, double) - -int lsx_readchars(sox_format_t * ft, char * chars, size_t len) -{ - size_t ret = lsx_readbuf(ft, chars, len); - if (ret == len) - return SOX_SUCCESS; - if (!lsx_error(ft)) - lsx_fail_errno(ft, errno, premature_eof); - return SOX_EOF; -} - -/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte - types). */ -#define WRITE_FUNC(type, size, ctype, twiddle) \ - size_t lsx_write_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nwritten; \ - for (n = 0; n < len; n++) \ - twiddle(buf[n], type); \ - nwritten = lsx_writebuf(ft, buf, len * size); \ - return nwritten / size; \ - } - -/* Pack a 3-byte value to a uint8_t * */ -#define sox_pack3(p, v) do {if (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN)\ -{(p)[0] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[2] = (v >> 16) & 0xff;} else \ -{(p)[2] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[0] = (v >> 16) & 0xff;} \ -} while (0) - -/* This (slower) macro works for unaligned types (e.g. 3-byte types) - that need to be packed. */ -#define WRITE_FUNC_PACK(type, size, ctype, twiddle) \ - size_t lsx_write_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nwritten; \ - uint8_t *data = lsx_malloc(size * len); \ - for (n = 0; n < len; n++) \ - sox_pack ## size(data + n * size, buf[n]); \ - nwritten = lsx_writebuf(ft, data, len * size); \ - free(data); \ - return nwritten / size; \ - } - -WRITE_FUNC(b, 1, uint8_t, TWIDDLE_BYTE) -WRITE_FUNC(w, 2, uint16_t, TWIDDLE_WORD) -WRITE_FUNC_PACK(3, 3, sox_uint24_t, TWIDDLE_WORD) -WRITE_FUNC(dw, 4, uint32_t, TWIDDLE_WORD) -WRITE_FUNC(qw, 8, uint64_t, TWIDDLE_WORD) -WRITE_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT) -WRITE_FUNC(df, sizeof(double), double, TWIDDLE_WORD) - -#define WRITE1U_FUNC(type, ctype) \ - int lsx_write ## type(sox_format_t * ft, unsigned d) \ - { ctype datum = (ctype)d; \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -#define WRITE1S_FUNC(type, ctype) \ - int lsx_writes ## type(sox_format_t * ft, signed d) \ - { ctype datum = (ctype)d; \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -#define WRITE1_FUNC(type, ctype) \ - int lsx_write ## type(sox_format_t * ft, ctype datum) \ - { \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -WRITE1U_FUNC(b, uint8_t) -WRITE1U_FUNC(w, uint16_t) -WRITE1U_FUNC(3, sox_uint24_t) -WRITE1U_FUNC(dw, uint32_t) -WRITE1_FUNC(qw, uint64_t) -WRITE1S_FUNC(b, uint8_t) -WRITE1S_FUNC(w, uint16_t) -WRITE1_FUNC(df, double) - -int lsx_writef(sox_format_t * ft, double datum) -{ - float f = datum; - return lsx_write_f_buf(ft, &f, (size_t) 1) == 1 ? SOX_SUCCESS : SOX_EOF; -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/libsox.c b/freedv/tags/1.2.2/freedv-dev/src/sox/libsox.c deleted file mode 100644 index 43620250..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/libsox.c +++ /dev/null @@ -1,225 +0,0 @@ -/* Implements the public API for libSoX general functions - * All public functions & data are prefixed with sox_ . - * - * (c) 2006-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include - -const char *sox_version(void) -{ - static char versionstr[20]; - - sprintf(versionstr, "%d.%d.%d", - (SOX_LIB_VERSION_CODE & 0xff0000) >> 16, - (SOX_LIB_VERSION_CODE & 0x00ff00) >> 8, - (SOX_LIB_VERSION_CODE & 0x0000ff)); - return(versionstr); -} - -sox_version_info_t const * sox_version_info(void) -{ -#define STRINGIZE1(x) #x -#define STRINGIZE(x) STRINGIZE1(x) - static char arch[30]; - static sox_version_info_t info = { - /* size */ - sizeof(sox_version_info_t), - /* flags */ - (sox_version_flags_t)( -#if HAVE_POPEN - sox_version_have_popen + -#endif -#if HAVE_MAGIC - sox_version_have_magic + -#endif -#if HAVE_OPENMP - sox_version_have_threads + -#endif -#ifdef HAVE_FMEMOPEN - sox_version_have_memopen + -#endif - sox_version_none), - /* version_code */ - SOX_LIB_VERSION_CODE, - /* version */ - NULL, - /* sox_version_extra */ -#ifdef PACKAGE_EXTRA - PACKAGE_EXTRA, -#else - NULL, -#endif - /* sox_time */ - __DATE__ " " __TIME__, - /* sox_distro */ -#ifdef DISTRO - DISTRO, -#else - NULL, -#endif - /* sox_compiler */ -#if defined __GNUC__ - "gcc " __VERSION__, -#elif defined _MSC_VER - "msvc " STRINGIZE(_MSC_FULL_VER), -#elif defined __SUNPRO_C - fprintf(file, "sun c " STRINGIZE(__SUNPRO_C), -#else - NULL, -#endif - /* sox_arch */ - NULL - }; - - if (!info.version) - { - info.version = sox_version(); - } - - if (!info.arch) - { - snprintf(arch, sizeof(arch), - "%" PRIuPTR "%" PRIuPTR "%" PRIuPTR "%" PRIuPTR - " %" PRIuPTR "%" PRIuPTR " %" PRIuPTR "%" PRIuPTR " %c %s", - sizeof(char), sizeof(short), sizeof(long), sizeof(off_t), - sizeof(float), sizeof(double), sizeof(int *), sizeof(int (*)(void)), - MACHINE_IS_BIGENDIAN ? 'B' : 'L', - (info.flags & sox_version_have_threads) ? "OMP" : ""); - arch[sizeof(arch) - 1] = 0; - info.arch = arch; - } - - return &info; -} - -/* Default routine to output messages; can be overridden */ -static void output_message( - unsigned level, const char *filename, const char *fmt, va_list ap) -{ - if (sox_globals.verbosity >= level) { - char base_name[128]; - sox_basename(base_name, sizeof(base_name), filename); - fprintf(stderr, "%s: ", base_name); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - } -} - -static sox_globals_t s_sox_globals = { - 2, /* unsigned verbosity */ - output_message, /* sox_output_message_handler */ - sox_false, /* sox_bool repeatable */ - 8192, /* size_t bufsiz */ - 0, /* size_t input_bufsiz */ - 0, /* int32_t ranqd1 */ - NULL, /* char const * stdin_in_use_by */ - NULL, /* char const * stdout_in_use_by */ - NULL, /* char const * subsystem */ - NULL, /* char * tmp_path */ - sox_false, /* sox_bool use_magic */ - sox_false /* sox_bool use_threads */ -}; - -sox_globals_t * sox_get_globals(void) -{ - return &s_sox_globals; -} - -/* FIXME: Not thread safe using globals */ -static sox_effects_globals_t s_sox_effects_globals = - {sox_plot_off, &s_sox_globals}; - -sox_effects_globals_t * -sox_get_effects_globals(void) -{ - return &s_sox_effects_globals; -} - -char const * sox_strerror(int sox_errno) -{ - static char const * const errors[] = { - "Invalid Audio Header", - "Unsupported data format", - "Can't allocate memory", - "Operation not permitted", - "Operation not supported", - "Invalid argument", - }; - if (sox_errno < SOX_EHDR) - return strerror(sox_errno); - sox_errno -= SOX_EHDR; - if (sox_errno < 0 || (size_t)sox_errno >= array_length(errors)) - return "Unknown error"; - return errors[sox_errno]; -} - -size_t sox_basename(char * base_buffer, size_t base_buffer_len, const char * filename) -{ - if (!base_buffer || !base_buffer_len) - { - return 0; - } - else - { - char const * slash_pos = LAST_SLASH(filename); - char const * base_name = slash_pos ? slash_pos + 1 : filename; - char const * dot_pos = strrchr(base_name, '.'); - size_t i, len; - dot_pos = dot_pos ? dot_pos : base_name + strlen(base_name); - len = dot_pos - base_name; - len = min(len, base_buffer_len - 1); - for (i = 0; i < len; i++) - { - base_buffer[i] = base_name[i]; - } - base_buffer[i] = 0; - return i; - } -} - -#define SOX_MESSAGE_FUNCTION(name,level) \ -void name(char const * fmt, ...) { \ - va_list ap; \ - va_start(ap, fmt); \ - if (sox_globals.output_message_handler) \ - (*sox_globals.output_message_handler)(level,sox_globals.subsystem,fmt,ap); \ - va_end(ap); \ -} - -SOX_MESSAGE_FUNCTION(lsx_fail_impl , 1) -SOX_MESSAGE_FUNCTION(lsx_warn_impl , 2) -SOX_MESSAGE_FUNCTION(lsx_report_impl, 3) -SOX_MESSAGE_FUNCTION(lsx_debug_impl , 4) -SOX_MESSAGE_FUNCTION(lsx_debug_more_impl , 5) -SOX_MESSAGE_FUNCTION(lsx_debug_most_impl , 6) - -#undef SOX_MESSAGE_FUNCTION - -int sox_init(void) -{ - return lsx_effects_init(); -} - -int sox_quit(void) -{ - #ifndef __FREEDV__ - sox_format_quit(); - #endif - return lsx_effects_quit(); -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/sox.h b/freedv/tags/1.2.2/freedv-dev/src/sox/sox.h deleted file mode 100644 index 05372558..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/sox.h +++ /dev/null @@ -1,2608 +0,0 @@ -/* libSoX Library Public Interface - * - * Copyright 1999-2011 Chris Bagwell and SoX Contributors. - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Chris Bagwell And SoX Contributors are not responsible for - * the consequences of using this software. - */ - -/** @file -Contains the interface exposed to clients of the libSoX library. -Symbols starting with "sox_" or "SOX_" are part of the public interface for -libSoX clients (applications that consume libSoX). Symbols starting with -"lsx_" or "LSX_" are internal use by libSoX and plugins. -LSX_ and lsx_ symbols should not be used by libSoX-based applications. -*/ - -#ifndef SOX_H -#define SOX_H /**< Client API: This macro is defined if sox.h has been included. */ - -#include -#include -#include - -#if defined(__cplusplus) -extern "C" { -#endif - -/* Suppress warnings from use of type long long. */ -#if defined __GNUC__ -#pragma GCC system_header -#endif - -/***************************************************************************** -API decoration macros: -Mostly for documentation purposes. For some compilers, decorations also affect -code generation, influence compiler warnings or activate compiler -optimizations. -*****************************************************************************/ - -/** -Plugins API: -Attribute required on all functions exported by libSoX and on all function -pointer types used by the libSoX API. -*/ -#ifdef __GNUC__ -#define LSX_API __attribute__ ((cdecl)) /* libSoX function */ -#elif _MSC_VER -#define LSX_API __cdecl /* libSoX function */ -#else -#define LSX_API /* libSoX function */ -#endif - -/** -Plugins API: -Attribute applied to a parameter or local variable to suppress warnings about -the variable being unused (especially in macro-generated code). -*/ -#ifdef __GNUC__ -#define LSX_UNUSED __attribute__ ((unused)) /* Parameter or local variable is intentionally unused. */ -#else -#define LSX_UNUSED /* Parameter or local variable is intentionally unused. */ -#endif - -/** -Plugins API: -LSX_PRINTF12: Attribute applied to a function to indicate that it requires -a printf-style format string for arg1 and that printf parameters start at -arg2. -*/ -#ifdef __GNUC__ -#define LSX_PRINTF12 __attribute__ ((format (printf, 1, 2))) /* Function has printf-style arguments. */ -#else -#define LSX_PRINTF12 /* Function has printf-style arguments. */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that it has no side effects and -depends only its input parameters and global memory. If called repeatedly, it -returns the same result each time. -*/ -#ifdef __GNUC__ -#define LSX_RETURN_PURE __attribute__ ((pure)) /* Function is pure. */ -#else -#define LSX_RETURN_PURE /* Function is pure. */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the -return value is always a pointer to a valid object (never NULL). -*/ -#ifdef _Ret_ -#define LSX_RETURN_VALID _Ret_ /* Function always returns a valid object (never NULL). */ -#else -#define LSX_RETURN_VALID /* Function always returns a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the return value is always a -pointer to a valid array (never NULL). -*/ -#ifdef _Ret_valid_ -#define LSX_RETURN_ARRAY _Ret_valid_ /* Function always returns a valid array (never NULL). */ -#else -#define LSX_RETURN_ARRAY /* Function always returns a valid array (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the return value is always a -pointer to a valid 0-terminated array (never NULL). -*/ -#ifdef _Ret_z_ -#define LSX_RETURN_VALID_Z _Ret_z_ /* Function always returns a 0-terminated array (never NULL). */ -#else -#define LSX_RETURN_VALID_Z /* Function always returns a 0-terminated array (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the returned pointer may be -null. -*/ -#ifdef _Ret_opt_ -#define LSX_RETURN_OPT _Ret_opt_ /* Function may return NULL. */ -#else -#define LSX_RETURN_OPT /* Function may return NULL. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to one const element of the pointed-to type (never NULL). -*/ -#ifdef _In_ -#define LSX_PARAM_IN _In_ /* Required const pointer to a valid object (never NULL). */ -#else -#define LSX_PARAM_IN /* Required const pointer to a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to a const 0-terminated string (never NULL). -*/ -#ifdef _In_z_ -#define LSX_PARAM_IN_Z _In_z_ /* Required const pointer to 0-terminated string (never NULL). */ -#else -#define LSX_PARAM_IN_Z /* Required const pointer to 0-terminated string (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a const -pointer to a 0-terminated printf format string. -*/ -#ifdef _Printf_format_string_ -#define LSX_PARAM_IN_PRINTF _Printf_format_string_ /* Required const pointer to 0-terminated printf format string (never NULL). */ -#else -#define LSX_PARAM_IN_PRINTF /* Required const pointer to 0-terminated printf format string (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) const initialized elements of the pointed-to type, where -(len) is the name of another parameter. -@param len The parameter that contains the number of elements in the array. -*/ -#ifdef _In_count_ -#define LSX_PARAM_IN_COUNT(len) _In_count_(len) /* Required const pointer to (len) valid objects (never NULL). */ -#else -#define LSX_PARAM_IN_COUNT(len) /* Required const pointer to (len) valid objects (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) const bytes of initialized data, where (len) is the name of -another parameter. -@param len The parameter that contains the number of bytes in the array. -*/ -#ifdef _In_bytecount_ -#define LSX_PARAM_IN_BYTECOUNT(len) _In_bytecount_(len) /* Required const pointer to (len) bytes of data (never NULL). */ -#else -#define LSX_PARAM_IN_BYTECOUNT(len) /* Required const pointer to (len) bytes of data (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to one const element of the pointed-to type. -*/ -#ifdef _In_opt_ -#define LSX_PARAM_IN_OPT _In_opt_ /* Optional const pointer to a valid object (may be NULL). */ -#else -#define LSX_PARAM_IN_OPT /* Optional const pointer to a valid object (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to a const 0-terminated string. -*/ -#ifdef _In_opt_z_ -#define LSX_PARAM_IN_OPT_Z _In_opt_z_ /* Optional const pointer to 0-terminated string (may be NULL). */ -#else -#define LSX_PARAM_IN_OPT_Z /* Optional const pointer to 0-terminated string (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to one initialized element of the pointed-to type (never NULL). The -function may modify the element. -*/ -#ifdef _Inout_ -#define LSX_PARAM_INOUT _Inout_ /* Required pointer to a valid object (never NULL). */ -#else -#define LSX_PARAM_INOUT /* Required pointer to a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) initialized elements of the pointed-to type (never NULL). The -function may modify the elements. -@param len The parameter that contains the number of elements in the array. -*/ -#ifdef _Inout_count_x_ -#define LSX_PARAM_INOUT_COUNT(len) _Inout_count_x_(len) /* Required pointer to (len) valid objects (never NULL). */ -#else -#define LSX_PARAM_INOUT_COUNT(len) /* Required pointer to (len) valid objects (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for one element of the pointed-to type (never -NULL). The function will initialize the element. -*/ -#ifdef _Out_ -#define LSX_PARAM_OUT _Out_ /* Required pointer to an object to be initialized (never NULL). */ -#else -#define LSX_PARAM_OUT /* Required pointer to an object to be initialized (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) bytes of data (never NULL), where (len) -is the name of another parameter. The function may write up to len bytes of -data to this memory. -@param len The parameter that contains the number of bytes in the array. -*/ -#ifdef _Out_bytecap_ -#define LSX_PARAM_OUT_BYTECAP(len) _Out_bytecap_(len) /* Required pointer to writable buffer with room for len bytes. */ -#else -#define LSX_PARAM_OUT_BYTECAP(len) /* Required pointer to writable buffer with room for len bytes. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) elements of the pointed-to type (never -NULL), where (len) is the name of another parameter. On return, (filled) -elements will have been initialized, where (filled) is either the dereference -of another pointer parameter (for example "*written") or the "return" -parameter (indicating that the function returns the number of elements -written). -@param len The parameter that contains the number of elements in the array. -@param filled The dereference of the parameter that receives the number of elements written to the array, or "return" if the value is returned. -*/ -#ifdef _Out_cap_post_count_ -#define LSX_PARAM_OUT_CAP_POST_COUNT(len,filled) _Out_cap_post_count_(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled) elements will have been initialized. */ -#else -#define LSX_PARAM_OUT_CAP_POST_COUNT(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled) elements will have been initialized. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) elements of the pointed-to type (never -NULL), where (len) is the name of another parameter. On return, (filled+1) -elements will have been initialized, with the last element having been -initialized to 0, where (filled) is either the dereference of another pointer -parameter (for example, "*written") or the "return" parameter (indicating that -the function returns the number of elements written). -@param len The parameter that contains the number of elements in the array. -@param filled The dereference of the parameter that receives the number of elements written to the array (not counting the terminating null), or "return" if the value is returned. -*/ -#ifdef _Out_z_cap_post_count_ -#define LSX_PARAM_OUT_Z_CAP_POST_COUNT(len,filled) _Out_z_cap_post_count_(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled+1) elements will have been initialized, and the array will be 0-terminated. */ -#else -#define LSX_PARAM_OUT_Z_CAP_POST_COUNT(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled+1) elements will have been initialized, and the array will be 0-terminated. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to memory sufficient for one element of the pointed-to -type. The function will initialize the element. -*/ -#ifdef _Out_opt_ -#define LSX_PARAM_OUT_OPT _Out_opt_ /* Optional pointer to an object to be initialized (may be NULL). */ -#else -#define LSX_PARAM_OUT_OPT /* Optional pointer to an object to be initialized (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which may be NULL when the function is -invoked. -*/ -#ifdef _Deref_pre_maybenull_ -#define LSX_PARAM_DEREF_PRE_MAYBENULL _Deref_pre_maybenull_ /* Required pointer (never NULL) to another pointer (may be NULL). */ -#else -#define LSX_PARAM_DEREF_PRE_MAYBENULL /* Required pointer (never NULL) to another pointer (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which will be NULL when the function -returns. -*/ -#ifdef _Deref_post_null_ -#define LSX_PARAM_DEREF_POST_NULL _Deref_post_null_ /* Required pointer (never NULL) to another pointer, which will be NULL on exit. */ -#else -#define LSX_PARAM_DEREF_POST_NULL /* Required pointer (never NULL) to another pointer, which will be NULL on exit. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which will be non-NULL when the -function returns. -*/ -#ifdef _Deref_post_notnull_ -#define LSX_PARAM_DEREF_POST_NOTNULL _Deref_post_notnull_ /* Required pointer (never NULL) to another pointer, which will be valid (not NULL) on exit. */ -#else -#define LSX_PARAM_DEREF_POST_NOTNULL /* Required pointer (never NULL) to another pointer, which will be valid (not NULL) on exit. */ -#endif - -/** -Plugins API: -Expression that "uses" a potentially-unused variable to avoid compiler -warnings (especially in macro-generated code). -*/ -#ifdef _PREFAST_ -#define LSX_USE_VAR(x) ((void)(x=0)) /* During static analysis, initialize unused variables to 0. */ -#else -#define LSX_USE_VAR(x) ((void)(x)) /* Parameter or variable is intentionally unused. */ -#endif - -/** -Plugins API: -Compile-time assertion. Causes a compile error if the expression is false. -@param e The expression to test. If expression is false, compilation will fail. -@param f A unique identifier for the test, for example foo_must_not_be_zero. -*/ -#define lsx_static_assert(e,f) enum {lsx_static_assert_##f = 1/((e) ? 1 : 0)} - -/***************************************************************************** -Basic typedefs: -*****************************************************************************/ - -/** -Client API: -Signed twos-complement 8-bit type. Typically defined as signed char. -*/ -#if SCHAR_MAX==127 && SCHAR_MIN==(-128) -typedef signed char sox_int8_t; -#elif CHAR_MAX==127 && CHAR_MIN==(-128) -typedef char sox_int8_t; -#else -#error Unable to determine an appropriate definition for sox_int8_t. -#endif - -/** -Client API: -Unsigned 8-bit type. Typically defined as unsigned char. -*/ -#if UCHAR_MAX==0xff -typedef unsigned char sox_uint8_t; -#elif CHAR_MAX==0xff && CHAR_MIN==0 -typedef char sox_uint8_t; -#else -#error Unable to determine an appropriate definition for sox_uint8_t. -#endif - -/** -Client API: -Signed twos-complement 16-bit type. Typically defined as short. -*/ -#if SHRT_MAX==32767 && SHRT_MIN==(-32768) -typedef short sox_int16_t; -#elif INT_MAX==32767 && INT_MIN==(-32768) -typedef int sox_int16_t; -#else -#error Unable to determine an appropriate definition for sox_int16_t. -#endif - -/** -Client API: -Unsigned 16-bit type. Typically defined as unsigned short. -*/ -#if USHRT_MAX==0xffff -typedef unsigned short sox_uint16_t; -#elif UINT_MAX==0xffff -typedef unsigned int sox_uint16_t; -#else -#error Unable to determine an appropriate definition for sox_uint16_t. -#endif - -/** -Client API: -Signed twos-complement 32-bit type. Typically defined as int. -*/ -#if INT_MAX==2147483647 && INT_MIN==(-2147483647-1) -typedef int sox_int32_t; -#elif LONG_MAX==2147483647 && LONG_MIN==(-2147483647-1) -typedef long sox_int32_t; -#else -#error Unable to determine an appropriate definition for sox_int32_t. -#endif - -/** -Client API: -Unsigned 32-bit type. Typically defined as unsigned int. -*/ -#if UINT_MAX==0xffffffff -typedef unsigned int sox_uint32_t; -#elif ULONG_MAX==0xffffffff -typedef unsigned long sox_uint32_t; -#else -#error Unable to determine an appropriate definition for sox_uint32_t. -#endif - -/** -Client API: -Signed twos-complement 64-bit type. Typically defined as long or long long. -*/ -#if LONG_MAX==9223372036854775807 && LONG_MIN==(-9223372036854775807-1) -typedef long sox_int64_t; -#elif defined(_MSC_VER) -typedef __int64 sox_int64_t; -#else -typedef long long sox_int64_t; -#endif - -/** -Client API: -Unsigned 64-bit type. Typically defined as unsigned long or unsigned long long. -*/ -#if ULONG_MAX==0xffffffffffffffff -typedef unsigned long sox_uint64_t; -#elif defined(_MSC_VER) -typedef unsigned __int64 sox_uint64_t; -#else -typedef unsigned long long sox_uint64_t; -#endif - -#ifndef _DOXYGEN_ -lsx_static_assert(sizeof(sox_int8_t)==1, sox_int8_size); -lsx_static_assert(sizeof(sox_uint8_t)==1, sox_uint8_size); -lsx_static_assert(sizeof(sox_int16_t)==2, sox_int16_size); -lsx_static_assert(sizeof(sox_uint16_t)==2, sox_uint16_size); -lsx_static_assert(sizeof(sox_int32_t)==4, sox_int32_size); -lsx_static_assert(sizeof(sox_uint32_t)==4, sox_uint32_size); -lsx_static_assert(sizeof(sox_int64_t)==8, sox_int64_size); -lsx_static_assert(sizeof(sox_uint64_t)==8, sox_uint64_size); -#endif - -/** -Client API: -Alias for sox_int32_t (beware of the extra byte). -*/ -typedef sox_int32_t sox_int24_t; - -/** -Client API: -Alias for sox_uint32_t (beware of the extra byte). -*/ -typedef sox_uint32_t sox_uint24_t; - -/** -Client API: -Native SoX audio sample type (alias for sox_int32_t). -*/ -typedef sox_int32_t sox_sample_t; - -/** -Client API: -Samples per second is stored as a double. -*/ -typedef double sox_rate_t; - -/** -Client API: -File's metadata, access via sox_*_comments functions. -*/ -typedef char * * sox_comments_t; - -/***************************************************************************** -Enumerations: -*****************************************************************************/ - -/** -Client API: -Boolean type, assignment (but not necessarily binary) compatible with C++ bool. -*/ -typedef enum sox_bool { - sox_false, /**< False = 0. */ - sox_true /**< True = 1. */ -} sox_bool; - -/** -Client API: -no, yes, or default (default usually implies some kind of auto-detect logic). -*/ -typedef enum sox_option_t { - sox_option_no, /**< Option specified as no = 0. */ - sox_option_yes, /**< Option specified as yes = 1. */ - sox_option_default /**< Option unspecified = 2. */ -} sox_option_t; - -/** -Client API: -The libSoX-specific error codes. -libSoX functions may return these codes or others that map from errno codes. -*/ -enum sox_error_t { - SOX_SUCCESS = 0, /**< Function succeeded = 0 */ - SOX_EOF = -1, /**< End Of File or other error = -1 */ - SOX_EHDR = 2000, /**< Invalid Audio Header = 2000 */ - SOX_EFMT, /**< Unsupported data format = 2001 */ - SOX_ENOMEM, /**< Can't alloc memory = 2002 */ - SOX_EPERM, /**< Operation not permitted = 2003 */ - SOX_ENOTSUP, /**< Operation not supported = 2004 */ - SOX_EINVAL /**< Invalid argument = 2005 */ -}; - -/** -Client API: -Flags indicating whether optional features are present in this build of libSoX. -*/ -typedef enum sox_version_flags_t { - sox_version_none = 0, /**< No special features = 0. */ - sox_version_have_popen = 1, /**< popen = 1. */ - sox_version_have_magic = 2, /**< magic = 2. */ - sox_version_have_threads = 4, /**< threads = 4. */ - sox_version_have_memopen = 8 /**< memopen = 8. */ -} sox_version_flags_t; - -/** -Client API: -Format of sample data. -*/ -typedef enum sox_encoding_t { - SOX_ENCODING_UNKNOWN , /**< encoding has not yet been determined */ - - SOX_ENCODING_SIGN2 , /**< signed linear 2's comp: Mac */ - SOX_ENCODING_UNSIGNED , /**< unsigned linear: Sound Blaster */ - SOX_ENCODING_FLOAT , /**< floating point (binary format) */ - SOX_ENCODING_FLOAT_TEXT, /**< floating point (text format) */ - SOX_ENCODING_FLAC , /**< FLAC compression */ - SOX_ENCODING_HCOM , /**< Mac FSSD files with Huffman compression */ - SOX_ENCODING_WAVPACK , /**< WavPack with integer samples */ - SOX_ENCODING_WAVPACKF , /**< WavPack with float samples */ - SOX_ENCODING_ULAW , /**< u-law signed logs: US telephony, SPARC */ - SOX_ENCODING_ALAW , /**< A-law signed logs: non-US telephony, Psion */ - SOX_ENCODING_G721 , /**< G.721 4-bit ADPCM */ - SOX_ENCODING_G723 , /**< G.723 3 or 5 bit ADPCM */ - SOX_ENCODING_CL_ADPCM , /**< Creative Labs 8 --> 2,3,4 bit Compressed PCM */ - SOX_ENCODING_CL_ADPCM16, /**< Creative Labs 16 --> 4 bit Compressed PCM */ - SOX_ENCODING_MS_ADPCM , /**< Microsoft Compressed PCM */ - SOX_ENCODING_IMA_ADPCM , /**< IMA Compressed PCM */ - SOX_ENCODING_OKI_ADPCM , /**< Dialogic/OKI Compressed PCM */ - SOX_ENCODING_DPCM , /**< Differential PCM: Fasttracker 2 (xi) */ - SOX_ENCODING_DWVW , /**< Delta Width Variable Word */ - SOX_ENCODING_DWVWN , /**< Delta Width Variable Word N-bit */ - SOX_ENCODING_GSM , /**< GSM 6.10 33byte frame lossy compression */ - SOX_ENCODING_MP3 , /**< MP3 compression */ - SOX_ENCODING_VORBIS , /**< Vorbis compression */ - SOX_ENCODING_AMR_WB , /**< AMR-WB compression */ - SOX_ENCODING_AMR_NB , /**< AMR-NB compression */ - SOX_ENCODING_CVSD , /**< Continuously Variable Slope Delta modulation */ - SOX_ENCODING_LPC10 , /**< Linear Predictive Coding */ - - SOX_ENCODINGS /**< End of list marker */ -} sox_encoding_t; - -/** -Client API: -Flags for sox_encodings_info_t: lossless/lossy1/lossy2. -*/ -typedef enum sox_encodings_flags_t { - sox_encodings_none = 0, /**< no flags specified (implies lossless encoding) = 0. */ - sox_encodings_lossy1 = 1, /**< encode, decode: lossy once = 1. */ - sox_encodings_lossy2 = 2 /**< encode, decode, encode, decode: lossy twice = 2. */ -} sox_encodings_flags_t; - -/** -Client API: -Type of plot. -*/ -typedef enum sox_plot_t { - sox_plot_off, /**< No plot = 0. */ - sox_plot_octave, /**< Octave plot = 1. */ - sox_plot_gnuplot, /**< Gnuplot plot = 2. */ - sox_plot_data /**< Plot data = 3. */ -} sox_plot_t; - -/** -Client API: -Loop modes: upper 4 bits mask the loop blass, lower 4 bits describe -the loop behaviour, for example single shot, bidirectional etc. -*/ -enum sox_loop_flags_t { - sox_loop_none = 0, /**< single-shot = 0 */ - sox_loop_forward = 1, /**< forward loop = 1 */ - sox_loop_forward_back = 2, /**< forward/back loop = 2 */ - sox_loop_8 = 32, /**< 8 loops (??) = 32 */ - sox_loop_sustain_decay = 64 /**< AIFF style, one sustain & one decay loop = 64 */ -}; - -/** -Plugins API: -Is file a real file, a pipe, or a url? -*/ -typedef enum lsx_io_type -{ - lsx_io_file, /**< File is a real file = 0. */ - lsx_io_pipe, /**< File is a pipe (no seeking) = 1. */ - lsx_io_url /**< File is a URL (no seeking) = 2. */ -} lsx_io_type; - -/***************************************************************************** -Macros: -*****************************************************************************/ - -/** -Client API: -Compute a 32-bit integer API version from three 8-bit parts. -@param a Major version. -@param b Minor version. -@param c Revision or build number. -@returns 32-bit integer API version 0x000a0b0c. -*/ -#define SOX_LIB_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) - -/** -Client API: -The API version of the sox.h file. It is not meant to follow the version -number of SoX but it has historically. Please do not count on -SOX_LIB_VERSION_CODE staying in sync with the libSoX version. -*/ -#define SOX_LIB_VERSION_CODE SOX_LIB_VERSION(14, 4, 1) - -/** -Client API: -Returns the smallest (negative) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer; -for example, SOX_INT_MIN(8) = 0x80, SOX_INT_MIN(16) = 0x8000, etc. -@param bits Size of value for which to calculate minimum. -@returns the smallest (negative) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer. -*/ -#define SOX_INT_MIN(bits) (1 <<((bits)-1)) - -/** -Client API: -Returns the largest (positive) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer; -for example, SOX_INT_MAX(8) = 0x7F, SOX_INT_MAX(16) = 0x7FFF, etc. -@param bits Size of value for which to calculate maximum. -@returns the largest (positive) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer. -*/ -#define SOX_INT_MAX(bits) (((unsigned)-1)>>(33-(bits))) - -/** -Client API: -Returns the largest value storable in an unsigned integer with the specified -number of bits; for example, SOX_UINT_MAX(8) = 0xFF, -SOX_UINT_MAX(16) = 0xFFFF, etc. -@param bits Size of value for which to calculate maximum. -@returns the largest value storable in an unsigned integer with the specified -number of bits. -*/ -#define SOX_UINT_MAX(bits) (SOX_INT_MIN(bits)|SOX_INT_MAX(bits)) - -/** -Client API: -Returns 0x7F. -*/ -#define SOX_INT8_MAX SOX_INT_MAX(8) - -/** -Client API: -Returns 0x7FFF. -*/ -#define SOX_INT16_MAX SOX_INT_MAX(16) - -/** -Client API: -Returns 0x7FFFFF. -*/ -#define SOX_INT24_MAX SOX_INT_MAX(24) - -/** -Client API: -Returns 0x7FFFFFFF. -*/ -#define SOX_INT32_MAX SOX_INT_MAX(32) - -/** -Client API: -Bits in a sox_sample_t = 32. -*/ -#define SOX_SAMPLE_PRECISION 32 - -/** -Client API: -Max value for sox_sample_t = 0x7FFFFFFF. -*/ -#define SOX_SAMPLE_MAX (sox_sample_t)SOX_INT_MAX(32) - -/** -Client API: -Min value for sox_sample_t = 0x80000000. -*/ -#define SOX_SAMPLE_MIN (sox_sample_t)SOX_INT_MIN(32) - - -/* Conversions: Linear PCM <--> sox_sample_t - * - * I/O Input sox_sample_t Clips? Input sox_sample_t Clips? - * Format Minimum Minimum I O Maximum Maximum I O - * ------ --------- ------------ -- -- -------- ------------ -- -- - * Float -inf -1 y n inf 1 - 5e-10 y n - * Int8 -128 -128 n n 127 127.9999999 n y - * Int16 -32768 -32768 n n 32767 32767.99998 n y - * Int24 -8388608 -8388608 n n 8388607 8388607.996 n y - * Int32 -2147483648 -2147483648 n n 2147483647 2147483647 n n - * - * Conversions are as accurate as possible (with rounding). - * - * Rounding: halves toward +inf, all others to nearest integer. - * - * Clips? shows whether on not there is the possibility of a conversion - * clipping to the minimum or maximum value when inputing from or outputing - * to a given type. - * - * Unsigned integers are converted to and from signed integers by flipping - * the upper-most bit then treating them as signed integers. - */ - -/** -Client API: -Declares the temporary local variables that are required when using SOX -conversion macros. -*/ -#define SOX_SAMPLE_LOCALS sox_sample_t sox_macro_temp_sample LSX_UNUSED; \ - double sox_macro_temp_double LSX_UNUSED - -/** -Client API: -Sign bit for sox_sample_t = 0x80000000. -*/ -#define SOX_SAMPLE_NEG SOX_INT_MIN(32) - -/** -Client API: -Converts sox_sample_t to an unsigned integer of width (bits). -@param bits Width of resulting sample (1 through 32). -@param d Input sample to be converted. -@param clips Variable that is incremented if the result is too big. -@returns Unsigned integer of width (bits). -*/ -#define SOX_SAMPLE_TO_UNSIGNED(bits,d,clips) \ - (sox_uint##bits##_t)(SOX_SAMPLE_TO_SIGNED(bits,d,clips)^SOX_INT_MIN(bits)) - -/** -Client API: -Converts sox_sample_t to a signed integer of width (bits). -@param bits Width of resulting sample (1 through 32). -@param d Input sample to be converted. -@param clips Variable that is incremented if the result is too big. -@returns Signed integer of width (bits). -*/ -#define SOX_SAMPLE_TO_SIGNED(bits,d,clips) \ - (sox_int##bits##_t)(LSX_USE_VAR(sox_macro_temp_double),sox_macro_temp_sample=(d),sox_macro_temp_sample>SOX_SAMPLE_MAX-(1<<(31-bits))?++(clips),SOX_INT_MAX(bits):((sox_uint32_t)(sox_macro_temp_sample+(1<<(31-bits))))>>(32-bits)) - -/** -Client API: -Converts signed integer of width (bits) to sox_sample_t. -@param bits Width of input sample (1 through 32). -@param d Input sample to be converted. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_TO_SAMPLE(bits,d)((sox_sample_t)(d)<<(32-bits)) - -/** -Client API: -Converts unsigned integer of width (bits) to sox_sample_t. -@param bits Width of input sample (1 through 32). -@param d Input sample to be converted. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_TO_SAMPLE(bits,d)(SOX_SIGNED_TO_SAMPLE(bits,d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts unsigned 8-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_8BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(8,d) - -/** -Client API: -Converts signed 8-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_8BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(8,d) - -/** -Client API: -Converts unsigned 16-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_16BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(16,d) - -/** -Client API: -Converts signed 16-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_16BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(16,d) - -/** -Client API: -Converts unsigned 24-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_24BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(24,d) - -/** -Client API: -Converts signed 24-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_24BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(24,d) - -/** -Client API: -Converts unsigned 32-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_32BIT_TO_SAMPLE(d,clips) ((sox_sample_t)(d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts signed 32-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_32BIT_TO_SAMPLE(d,clips) (sox_sample_t)(d) - -/** -Client API: -Converts 32-bit float to sox_sample_t. -@param d Input sample to be converted, range [-1, 1). -@param clips Variable to increment if the input sample is too large or too small. -@returns SoX native sample value. -*/ -#define SOX_FLOAT_32BIT_TO_SAMPLE(d,clips) (sox_sample_t)(LSX_USE_VAR(sox_macro_temp_sample),sox_macro_temp_double=(d)*(SOX_SAMPLE_MAX+1.),sox_macro_temp_double=SOX_SAMPLE_MAX+1.?sox_macro_temp_double>SOX_SAMPLE_MAX+1.?++(clips),SOX_SAMPLE_MAX:SOX_SAMPLE_MAX:sox_macro_temp_double) - -/** -Client API: -Converts 64-bit float to sox_sample_t. -@param d Input sample to be converted, range [-1, 1). -@param clips Variable to increment if the input sample is too large or too small. -@returns SoX native sample value. -*/ -#define SOX_FLOAT_64BIT_TO_SAMPLE(d,clips) (sox_sample_t)(LSX_USE_VAR(sox_macro_temp_sample),sox_macro_temp_double=(d)*(SOX_SAMPLE_MAX+1.),sox_macro_temp_double<0?sox_macro_temp_double<=SOX_SAMPLE_MIN-.5?++(clips),SOX_SAMPLE_MIN:sox_macro_temp_double-.5:sox_macro_temp_double>=SOX_SAMPLE_MAX+.5?sox_macro_temp_double>SOX_SAMPLE_MAX+1.?++(clips),SOX_SAMPLE_MAX:SOX_SAMPLE_MAX:sox_macro_temp_double+.5) - -/** -Client API: -Converts SoX native sample to an unsigned 8-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_8BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(8,d,clips) - -/** -Client API: -Converts SoX native sample to an signed 8-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_8BIT(d,clips) SOX_SAMPLE_TO_SIGNED(8,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 16-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_16BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(16,d,clips) - -/** -Client API: -Converts SoX native sample to a signed 16-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_16BIT(d,clips) SOX_SAMPLE_TO_SIGNED(16,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 24-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_24BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(24,d,clips) - -/** -Client API: -Converts SoX native sample to a signed 24-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_24BIT(d,clips) SOX_SAMPLE_TO_SIGNED(24,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 32-bit integer. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_32BIT(d,clips) (sox_uint32_t)((d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts SoX native sample to a signed 32-bit integer. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_SIGNED_32BIT(d,clips) (sox_int32_t)(d) - -/** -Client API: -Converts SoX native sample to a 32-bit float. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_FLOAT_32BIT(d,clips) (LSX_USE_VAR(sox_macro_temp_double),sox_macro_temp_sample=(d),sox_macro_temp_sample>SOX_SAMPLE_MAX-128?++(clips),1:(((sox_macro_temp_sample+128)&~255)*(1./(SOX_SAMPLE_MAX+1.)))) - -/** -Client API: -Converts SoX native sample to a 64-bit float. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_FLOAT_64BIT(d,clips) ((d)*(1./(SOX_SAMPLE_MAX+1.))) - -/** -Client API: -Clips a value of a type that is larger then sox_sample_t (for example, int64) -to sox_sample_t's limits and increment a counter if clipping occurs. -@param samp Value (lvalue) to be clipped, updated as necessary. -@param clips Value (lvalue) that is incremented if clipping is needed. -*/ -#define SOX_SAMPLE_CLIP_COUNT(samp, clips) \ - do { \ - if (samp > SOX_SAMPLE_MAX) \ - { samp = SOX_SAMPLE_MAX; clips++; } \ - else if (samp < SOX_SAMPLE_MIN) \ - { samp = SOX_SAMPLE_MIN; clips++; } \ - } while (0) - -/** -Client API: -Clips a value of a type that is larger then sox_sample_t (for example, int64) -to sox_sample_t's limits and increment a counter if clipping occurs. -@param d Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_ROUND_CLIP_COUNT(d, clips) \ - ((d) < 0? (d) <= SOX_SAMPLE_MIN - 0.5? ++(clips), SOX_SAMPLE_MIN: (d) - 0.5 \ - : (d) >= SOX_SAMPLE_MAX + 0.5? ++(clips), SOX_SAMPLE_MAX: (d) + 0.5) - -/** -Client API: -Clips a value to the limits of a signed integer of the specified width -and increment a counter if clipping occurs. -@param bits Width (in bits) of target integer type. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_INTEGER_CLIP_COUNT(bits,i,clips) ( \ - (i) >(1 << ((bits)-1))- 1? ++(clips),(1 << ((bits)-1))- 1 : \ - (i) <-1 << ((bits)-1) ? ++(clips),-1 << ((bits)-1) : (i)) - -/** -Client API: -Clips a value to the limits of a 16-bit signed integer and increment a counter -if clipping occurs. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_16BIT_CLIP_COUNT(i,clips) SOX_INTEGER_CLIP_COUNT(16,i,clips) - -/** -Client API: -Clips a value to the limits of a 24-bit signed integer and increment a counter -if clipping occurs. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_24BIT_CLIP_COUNT(i,clips) SOX_INTEGER_CLIP_COUNT(24,i,clips) - -#define SOX_SIZE_MAX ((size_t)(-1)) /**< Client API: Maximum value of size_t. */ - -#define SOX_UNSPEC 0 /**< Client API: Members of sox_signalinfo_t are set to SOX_UNSPEC (= 0) if the actual value is not yet known. */ -#define SOX_UNKNOWN_LEN (sox_uint64_t)(-1) /**< Client API: sox_signalinfo_t.length is set to SOX_UNKNOWN_LEN (= -1) within the effects chain if the actual length is not known. Format handlers currently use SOX_UNSPEC instead. */ -#define SOX_IGNORE_LENGTH (sox_uint64_t)(-2) /**< Client API: sox_signalinfo_t.length is set to SOX_IGNORE_LENGTH (= -2) to indicate that a format handler should ignore length information in file headers. */ - -#define SOX_DEFAULT_CHANNELS 2 /**< Client API: Default channel count is 2 (stereo). */ -#define SOX_DEFAULT_RATE 48000 /**< Client API: Default rate is 48000Hz. */ -#define SOX_DEFAULT_PRECISION 16 /**< Client API: Default precision is 16 bits per sample. */ -#define SOX_DEFAULT_ENCODING SOX_ENCODING_SIGN2 /**< Client API: Default encoding is SIGN2 (linear 2's complement PCM). */ - -#define SOX_LOOP_NONE ((unsigned char)sox_loop_none) /**< Client API: single-shot = 0 */ -#define SOX_LOOP_8 ((unsigned char)sox_loop_8) /**< Client API: 8 loops = 32 */ -#define SOX_LOOP_SUSTAIN_DECAY ((unsigned char)sox_loop_sustain_decay) /**< Client API: AIFF style, one sustain & one decay loop = 64 */ - -#define SOX_MAX_NLOOPS 8 /**< Client API: Maximum number of loops supported by sox_oob_t = 8. */ - -#define SOX_FILE_NOSTDIO 0x0001 /**< Client API: Does not use stdio routines */ -#define SOX_FILE_DEVICE 0x0002 /**< Client API: File is an audio device */ -#define SOX_FILE_PHONY 0x0004 /**< Client API: Phony file/device (for example /dev/null) */ -#define SOX_FILE_REWIND 0x0008 /**< Client API: File should be rewound to write header */ -#define SOX_FILE_BIT_REV 0x0010 /**< Client API: Is file bit-reversed? */ -#define SOX_FILE_NIB_REV 0x0020 /**< Client API: Is file nibble-reversed? */ -#define SOX_FILE_ENDIAN 0x0040 /**< Client API: Is file format endian? */ -#define SOX_FILE_ENDBIG 0x0080 /**< Client API: For endian file format, is it big endian? */ -#define SOX_FILE_MONO 0x0100 /**< Client API: Do channel restrictions allow mono? */ -#define SOX_FILE_STEREO 0x0200 /**< Client API: Do channel restrictions allow stereo? */ -#define SOX_FILE_QUAD 0x0400 /**< Client API: Do channel restrictions allow quad? */ - -#define SOX_FILE_CHANS (SOX_FILE_MONO | SOX_FILE_STEREO | SOX_FILE_QUAD) /**< Client API: No channel restrictions */ -#define SOX_FILE_LIT_END (SOX_FILE_ENDIAN | 0) /**< Client API: File is little-endian */ -#define SOX_FILE_BIG_END (SOX_FILE_ENDIAN | SOX_FILE_ENDBIG) /**< Client API: File is big-endian */ - -#define SOX_EFF_CHAN 1 /**< Client API: Effect might alter the number of channels */ -#define SOX_EFF_RATE 2 /**< Client API: Effect might alter sample rate */ -#define SOX_EFF_PREC 4 /**< Client API: Effect does its own calculation of output sample precision (otherwise a default value is taken, depending on the presence of SOX_EFF_MODIFY) */ -#define SOX_EFF_LENGTH 8 /**< Client API: Effect might alter audio length (as measured in time units, not necessarily in samples) */ -#define SOX_EFF_MCHAN 16 /**< Client API: Effect handles multiple channels internally */ -#define SOX_EFF_NULL 32 /**< Client API: Effect does nothing (can be optimized out of chain) */ -#define SOX_EFF_DEPRECATED 64 /**< Client API: Effect will soon be removed from SoX */ -#define SOX_EFF_GAIN 128 /**< Client API: Effect does not support gain -r */ -#define SOX_EFF_MODIFY 256 /**< Client API: Effect does not modify sample values (but might remove or duplicate samples or insert zeros) */ -#define SOX_EFF_ALPHA 512 /**< Client API: Effect is experimental/incomplete */ -#define SOX_EFF_INTERNAL 1024 /**< Client API: Effect present in libSoX but not valid for use by SoX command-line tools */ - -/** -Client API: -When used as the "whence" parameter of sox_seek, indicates that the specified -offset is relative to the beginning of the file. -*/ -#define SOX_SEEK_SET 0 - -/***************************************************************************** -Forward declarations: -*****************************************************************************/ - -typedef struct sox_format_t sox_format_t; -typedef struct sox_effect_t sox_effect_t; -typedef struct sox_effect_handler_t sox_effect_handler_t; -typedef struct sox_format_handler_t sox_format_handler_t; - -/***************************************************************************** -Function pointers: -*****************************************************************************/ - -/** -Client API: -Callback to write a message to an output device (console or log file), -used by sox_globals_t.output_message_handler. -*/ -typedef void (LSX_API * sox_output_message_handler_t)( - unsigned level, /* 1 = FAIL, 2 = WARN, 3 = INFO, 4 = DEBUG, 5 = DEBUG_MORE, 6 = DEBUG_MOST. */ - LSX_PARAM_IN_Z char const * filename, /* Source code __FILENAME__ from which message originates. */ - LSX_PARAM_IN_PRINTF char const * fmt, /* Message format string. */ - LSX_PARAM_IN va_list ap /* Message format parameters. */ - ); - -/** -Client API: -Callback to retrieve information about a format handler, -used by sox_format_tab_t.fn. -@returns format handler information. -*/ -typedef sox_format_handler_t const * (LSX_API * sox_format_fn_t)(void); - -/** -Client API: -Callback to get information about an effect handler, -used by the table returned from sox_get_effect_fns(void). -@returns Pointer to information about an effect handler. -*/ -typedef sox_effect_handler_t const * (LSX_API *sox_effect_fn_t)(void); - -/** -Client API: -Callback to initialize reader (decoder), used by -sox_format_handler.startread. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_startread)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to read (decode) a block of samples, -used by sox_format_handler.read. -@returns number of samples read, or 0 if unsuccessful. -*/ -typedef size_t (LSX_API * sox_format_handler_read)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(len,return) sox_sample_t *buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Callback to close reader (decoder), -used by sox_format_handler.stopread. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_stopread)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to initialize writer (encoder), -used by sox_format_handler.startwrite. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_startwrite)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to write (encode) a block of samples, -used by sox_format_handler.write. -@returns number of samples written, or 0 if unsuccessful. -*/ -typedef size_t (LSX_API * sox_format_handler_write)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_IN_COUNT(len) sox_sample_t const * buf, /**< Buffer to which samples are written. */ - size_t len /**< Capacity of buf, measured in samples. */ - ); - -/** -Client API: -Callback to close writer (decoder), -used by sox_format_handler.stopwrite. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_stopwrite)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to reposition reader, -used by sox_format_handler.seek. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_seek)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - sox_uint64_t offset /**< Sample offset to which reader should be positioned. */ - ); - -/** -Client API: -Callback to parse command-line arguments (called once per effect), -used by sox_effect_handler.getopts. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_getopts)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - int argc, /**< Number of arguments in argv. */ - LSX_PARAM_IN_COUNT(argc) char *argv[] /**< Array of command-line arguments. */ - ); - -/** -Client API: -Callback to initialize effect (called once per flow), -used by sox_effect_handler.start. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_start)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback to process samples, -used by sox_effect_handler.flow. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_flow)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - LSX_PARAM_IN_COUNT(*isamp) sox_sample_t const * ibuf, /**< Buffer from which to read samples. */ - LSX_PARAM_OUT_CAP_POST_COUNT(*osamp,*osamp) sox_sample_t * obuf, /**< Buffer to which samples are written. */ - LSX_PARAM_INOUT size_t *isamp, /**< On entry, contains capacity of ibuf; on exit, contains number of samples consumed. */ - LSX_PARAM_INOUT size_t *osamp /**< On entry, contains capacity of obuf; on exit, contains number of samples written. */ - ); - -/** -Client API: -Callback to finish getting output after input is complete, -used by sox_effect_handler.drain. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_drain)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(*osamp,*osamp) sox_sample_t *obuf, /**< Buffer to which samples are written. */ - LSX_PARAM_INOUT size_t *osamp /**< On entry, contains capacity of obuf; on exit, contains number of samples written. */ - ); - -/** -Client API: -Callback to shut down effect (called once per flow), -used by sox_effect_handler.stop. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_stop)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback to shut down effect (called once per effect), -used by sox_effect_handler.kill. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_kill)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback called while flow is running (called once per buffer), -used by sox_flow_effects.callback. -@returns SOX_SUCCESS to continue, other value to abort flow. -*/ -typedef int (LSX_API * sox_flow_effects_callback)( - sox_bool all_done, - void * client_data - ); - -/** -Client API: -Callback for enumerating the contents of a playlist, -used by the sox_parse_playlist function. -@returns SOX_SUCCESS if successful, any other value to abort playlist enumeration. -*/ -typedef int (LSX_API * sox_playlist_callback_t)( - void * callback_data, - LSX_PARAM_IN_Z char const * filename - ); - -/***************************************************************************** -Structures: -*****************************************************************************/ - -/** -Client API: -Information about a build of libSoX, returned from the sox_version_info -function. -*/ -typedef struct sox_version_info_t { - size_t size; /**< structure size = sizeof(sox_version_info_t) */ - sox_version_flags_t flags; /**< feature flags = popen | magic | threads | memopen */ - sox_uint32_t version_code; /**< version number = 0x140400 */ - char const * version; /**< version string = sox_version(), for example, "14.4.0" */ - char const * version_extra;/**< version extra info or null = "PACKAGE_EXTRA", for example, "beta" */ - char const * time; /**< build time = "__DATE__ __TIME__", for example, "Jan 7 2010 03:31:50" */ - char const * distro; /**< distro or null = "DISTRO", for example, "Debian" */ - char const * compiler; /**< compiler info or null, for example, "msvc 160040219" */ - char const * arch; /**< arch, for example, "1248 48 44 L OMP" */ - /* new info should be added at the end for version backwards-compatibility. */ -} sox_version_info_t; - -/** -Client API: -Global parameters (for effects & formats), returned from the sox_get_globals -function. -*/ -typedef struct sox_globals_t { -/* public: */ - unsigned verbosity; /**< messages are only written if globals.verbosity >= message.level */ - sox_output_message_handler_t output_message_handler; /**< client-specified message output callback */ - sox_bool repeatable; /**< true to use pre-determined timestamps and PRNG seed */ - - /** - Default size (in bytes) used by libSoX for blocks of sample data. - Plugins should use similarly-sized buffers to get best performance. - */ - size_t bufsiz; - - /** - Default size (in bytes) used by libSoX for blocks of input sample data. - Plugins should use similarly-sized buffers to get best performance. - */ - size_t input_bufsiz; - - sox_int32_t ranqd1; /**< Can be used to re-seed libSoX's PRNG */ - - char const * stdin_in_use_by; /**< Private: tracks the name of the handler currently using stdin */ - char const * stdout_in_use_by; /**< Private: tracks the name of the handler currently using stdout */ - char const * subsystem; /**< Private: tracks the name of the handler currently writing an output message */ - char * tmp_path; /**< Private: client-configured path to use for temporary files */ - sox_bool use_magic; /**< Private: true if client has requested use of 'magic' file-type detection */ - sox_bool use_threads; /**< Private: true if client has requested parallel effects processing */ -} sox_globals_t; - -/** -Client API: -Signal parameters; members should be set to SOX_UNSPEC (= 0) if unknown. -*/ -typedef struct sox_signalinfo_t { - sox_rate_t rate; /**< samples per second, 0 if unknown */ - unsigned channels; /**< number of sound channels, 0 if unknown */ - unsigned precision; /**< bits per sample, 0 if unknown */ - sox_uint64_t length; /**< samples * chans in file, 0 if unknown, -1 if unspecified */ - double * mult; /**< Effects headroom multiplier; may be null */ -} sox_signalinfo_t; - -/** -Client API: -Basic information about an encoding. -*/ -typedef struct sox_encodings_info_t { - sox_encodings_flags_t flags; /**< lossy once (lossy1), lossy twice (lossy2), or lossless (none). */ - char const * name; /**< encoding name. */ - char const * desc; /**< encoding description. */ -} sox_encodings_info_t; - -/** -Client API: -Encoding parameters. -*/ -typedef struct sox_encodinginfo_t { - sox_encoding_t encoding; /**< format of sample numbers */ - unsigned bits_per_sample;/**< 0 if unknown or variable; uncompressed value if lossless; compressed value if lossy */ - double compression; /**< compression factor (where applicable) */ - - /** - Should bytes be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_bytes; - - /** - Should nibbles be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_nibbles; - - /** - Should bits be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_bits; - - /** - If set to true, the format should reverse its default endianness. - */ - sox_bool opposite_endian; -} sox_encodinginfo_t; - -/** -Client API: -Looping parameters (out-of-band data). -*/ -typedef struct sox_loopinfo_t { - sox_uint64_t start; /**< first sample */ - sox_uint64_t length; /**< length */ - unsigned count; /**< number of repeats, 0=forever */ - unsigned char type; /**< 0=no, 1=forward, 2=forward/back (see sox_loop_* for valid values). */ -} sox_loopinfo_t; - -/** -Client API: -Instrument information. -*/ -typedef struct sox_instrinfo_t{ - signed char MIDInote; /**< for unity pitch playback */ - signed char MIDIlow; /**< MIDI pitch-bend low range */ - signed char MIDIhi; /**< MIDI pitch-bend high range */ - unsigned char loopmode; /**< 0=no, 1=forward, 2=forward/back (see sox_loop_* values) */ - unsigned nloops; /**< number of active loops (max SOX_MAX_NLOOPS). */ -} sox_instrinfo_t; - -/** -Client API: -File buffer info. Holds info so that data can be read in blocks. -*/ -typedef struct sox_fileinfo_t { - char *buf; /**< Pointer to data buffer */ - size_t size; /**< Size of buffer in bytes */ - size_t count; /**< Count read into buffer */ - size_t pos; /**< Position in buffer */ -} sox_fileinfo_t; - -/** -Client API: -Handler structure defined by each format. -*/ -struct sox_format_handler_t { - unsigned sox_lib_version_code; /**< Checked on load; must be 1st in struct*/ - char const * description; /**< short description of format */ - char const * const * names; /**< null-terminated array of filename extensions that are handled by this format */ - unsigned int flags; /**< File flags (SOX_FILE_* values). */ - sox_format_handler_startread startread; /**< called to initialize reader (decoder) */ - sox_format_handler_read read; /**< called to read (decode) a block of samples */ - sox_format_handler_stopread stopread; /**< called to close reader (decoder); may be null if no closing necessary */ - sox_format_handler_startwrite startwrite; /**< called to initialize writer (encoder) */ - sox_format_handler_write write; /**< called to write (encode) a block of samples */ - sox_format_handler_stopwrite stopwrite; /**< called to close writer (decoder); may be null if no closing necessary */ - sox_format_handler_seek seek; /**< called to reposition reader; may be null if not supported */ - - /** - Array of values indicating the encodings and precisions supported for - writing (encoding). Precisions specified with default precision first. - Encoding, precision, precision, ..., 0, repeat. End with one more 0. - Example: - unsigned const * formats = { - SOX_ENCODING_SIGN2, 16, 24, 0, // Support SIGN2 at 16 and 24 bits, default to 16 bits. - SOX_ENCODING_UNSIGNED, 8, 0, // Support UNSIGNED at 8 bits, default to 8 bits. - 0 // No more supported encodings. - }; - */ - unsigned const * write_formats; - - /** - Array of sample rates (samples per second) supported for writing (encoding). - NULL if all (or almost all) rates are supported. End with 0. - */ - sox_rate_t const * write_rates; - - /** - SoX will automatically allocate a buffer in which the handler can store data. - Specify the size of the buffer needed here. Usually this will be sizeof(your_struct). - The buffer will be allocated and zeroed before the call to startread/startwrite. - The buffer will be freed after the call to stopread/stopwrite. - The buffer will be provided via format.priv in each call to the handler. - */ - size_t priv_size; -}; - -/** -Client API: -Comments, instrument info, loop info (out-of-band data). -*/ -typedef struct sox_oob_t{ - /* Decoded: */ - sox_comments_t comments; /**< Comment strings in id=value format. */ - sox_instrinfo_t instr; /**< Instrument specification */ - sox_loopinfo_t loops[SOX_MAX_NLOOPS]; /**< Looping specification */ - - /* TBD: Non-decoded chunks, etc: */ -} sox_oob_t; - -/** -Client API: -Data passed to/from the format handler -*/ -struct sox_format_t { - char * filename; /**< File name */ - - /** - Signal specifications for reader (decoder) or writer (encoder): - sample rate, number of channels, precision, length, headroom multiplier. - Any info specified by the user is here on entry to startread or - startwrite. Info will be SOX_UNSPEC if the user provided no info. - At exit from startread, should be completely filled in, using - either data from the file's headers (if available) or whatever - the format is guessing/assuming (if header data is not available). - At exit from startwrite, should be completely filled in, using - either the data that was specified, or values chosen by the format - based on the format's defaults or capabilities. - */ - sox_signalinfo_t signal; - - /** - Encoding specifications for reader (decoder) or writer (encoder): - encoding (sample format), bits per sample, compression rate, endianness. - Should be filled in by startread. Values specified should be used - by startwrite when it is configuring the encoding parameters. - */ - sox_encodinginfo_t encoding; - - char * filetype; /**< Type of file, as determined by header inspection or libmagic. */ - sox_oob_t oob; /**< comments, instrument info, loop info (out-of-band data) */ - sox_bool seekable; /**< Can seek on this file */ - char mode; /**< Read or write mode ('r' or 'w') */ - sox_uint64_t olength; /**< Samples * chans written to file */ - sox_uint64_t clips; /**< Incremented if clipping occurs */ - int sox_errno; /**< Failure error code */ - char sox_errstr[256]; /**< Failure error text */ - void * fp; /**< File stream pointer */ - lsx_io_type io_type; /**< Stores whether this is a file, pipe or URL */ - sox_uint64_t tell_off; /**< Current offset within file */ - sox_uint64_t data_start; /**< Offset at which headers end and sound data begins (set by lsx_check_read_params) */ - sox_format_handler_t handler; /**< Format handler for this file */ - void * priv; /**< Format handler's private data area */ -}; - -/** -Client API: -Information about a loaded format handler, including the format name and a -function pointer that can be invoked to get additional information about the -format. -*/ -typedef struct sox_format_tab_t { - char *name; /**< Name of format handler */ - sox_format_fn_t fn; /**< Function to call to get format handler's information */ -} sox_format_tab_t; - -/** -Client API: -Global parameters for effects. -*/ -typedef struct sox_effects_globals_t { - sox_plot_t plot; /**< To help the user choose effect & options */ - sox_globals_t * global_info; /**< Pointer to associated SoX globals */ -} sox_effects_globals_t; - -/** -Client API: -Effect handler information. -*/ -struct sox_effect_handler_t { - char const * name; /**< Effect name */ - char const * usage; /**< Short explanation of parameters accepted by effect */ - unsigned int flags; /**< Combination of SOX_EFF_* flags */ - sox_effect_handler_getopts getopts; /**< Called to parse command-line arguments (called once per effect). */ - sox_effect_handler_start start; /**< Called to initialize effect (called once per flow). */ - sox_effect_handler_flow flow; /**< Called to process samples. */ - sox_effect_handler_drain drain; /**< Called to finish getting output after input is complete. */ - sox_effect_handler_stop stop; /**< Called to shut down effect (called once per flow). */ - sox_effect_handler_kill kill; /**< Called to shut down effect (called once per effect). */ - size_t priv_size; /**< Size of private data SoX should pre-allocate for effect */ -}; - -/** -Client API: -Effect information. -*/ -struct sox_effect_t { - sox_effects_globals_t * global_info; /**< global effect parameters */ - sox_signalinfo_t in_signal; /**< Information about the incoming data stream */ - sox_signalinfo_t out_signal; /**< Information about the outgoing data stream */ - sox_encodinginfo_t const * in_encoding; /**< Information about the incoming data encoding */ - sox_encodinginfo_t const * out_encoding; /**< Information about the outgoing data encoding */ - sox_effect_handler_t handler; /**< The handler for this effect */ - sox_sample_t * obuf; /**< output buffer */ - size_t obeg; /**< output buffer: start of valid data section */ - size_t oend; /**< output buffer: one past valid data section (oend-obeg is length of current content) */ - size_t imin; /**< minimum input buffer content required for calling this effect's flow function; set via lsx_effect_set_imin() */ - sox_uint64_t clips; /**< increment if clipping occurs */ - size_t flows; /**< 1 if MCHAN, number of chans otherwise */ - size_t flow; /**< flow number */ - void * priv; /**< Effect's private data area (each flow has a separate copy) */ -}; - -/** -Client API: -Chain of effects to be applied to a stream. -*/ -typedef struct sox_effects_chain_t { - sox_effect_t **effects; /**< Table of effects to be applied to a stream */ - unsigned table_size; /**< Number of entries in effects table */ - unsigned length; /**< Number of effects to be applied */ - sox_sample_t **ibufc; /**< Channel interleave buffer */ - sox_sample_t **obufc; /**< Channel interleave buffer */ - sox_effects_globals_t global_info; /**< Copy of global effects settings */ - sox_encodinginfo_t const * in_enc; /**< Input encoding */ - sox_encodinginfo_t const * out_enc; /**< Output encoding */ -} sox_effects_chain_t; - -/***************************************************************************** -Functions: -*****************************************************************************/ - -/** -Client API: -Returns version number string of libSoX, for example, "14.4.0". -@returns The version number string of libSoX, for example, "14.4.0". -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -sox_version(void); - -/** -Client API: -Returns information about this build of libsox. -@returns Pointer to a version information structure. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_version_info_t const * -LSX_API -sox_version_info(void); - -/** -Client API: -Returns a pointer to the structure with libSoX's global settings. -@returns a pointer to the structure with libSoX's global settings. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_globals_t * -LSX_API -sox_get_globals(void); - -/** -Client API: -Deprecated macro that returns the structure with libSoX's global settings -as an lvalue. -*/ -#define sox_globals (*sox_get_globals()) - -/** -Client API: -Returns a pointer to the list of available encodings. -End of list indicated by name == NULL. -@returns pointer to the list of available encodings. -*/ -LSX_RETURN_ARRAY LSX_RETURN_PURE -sox_encodings_info_t const * -LSX_API -sox_get_encodings_info(void); - -/** -Client API: -Deprecated macro that returns the list of available encodings. -End of list indicated by name == NULL. -*/ -#define sox_encodings_info (sox_get_encodings_info()) - -/** -Client API: -Fills in an encodinginfo with default values. -*/ -void -LSX_API -sox_init_encodinginfo( - LSX_PARAM_OUT sox_encodinginfo_t * e /**< Pointer to uninitialized encoding info structure to be initialized. */ - ); - -/** -Client API: -Given an encoding (for example, SIGN2) and the encoded bits_per_sample (for -example, 16), returns the number of useful bits per sample in the decoded data -(for example, 16), or returns 0 to indicate that the value returned by the -format handler should be used instead of a pre-determined precision. -@returns the number of useful bits per sample in the decoded data (for example -16), or returns 0 to indicate that the value returned by the format handler -should be used instead of a pre-determined precision. -*/ -LSX_RETURN_PURE -unsigned -LSX_API -sox_precision( - sox_encoding_t encoding, /**< Encoding for which to lookup precision information. */ - unsigned bits_per_sample /**< The number of encoded bits per sample. */ - ); - -/** -Client API: -Returns the number of items in the metadata block. -@returns the number of items in the metadata block. -*/ -size_t -LSX_API -sox_num_comments( - LSX_PARAM_IN_OPT sox_comments_t comments /**< Metadata block. */ - ); - -/** -Client API: -Adds an "id=value" item to the metadata block. -*/ -void -LSX_API -sox_append_comment( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NOTNULL sox_comments_t * comments, /**< Metadata block. */ - LSX_PARAM_IN_Z char const * item /**< Item to be added in "id=value" format. */ - ); - -/** -Client API: -Adds a newline-delimited list of "id=value" items to the metadata block. -*/ -void -LSX_API -sox_append_comments( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NOTNULL sox_comments_t * comments, /**< Metadata block. */ - LSX_PARAM_IN_Z char const * items /**< Newline-separated list of items to be added, for example "id1=value1\\nid2=value2". */ - ); - -/** -Client API: -Duplicates the metadata block. -@returns the copied metadata block. -*/ -LSX_RETURN_OPT -sox_comments_t -LSX_API -sox_copy_comments( - LSX_PARAM_IN_OPT sox_comments_t comments /**< Metadata block to copy. */ - ); - -/** -Client API: -Frees the metadata block. -*/ -void -LSX_API -sox_delete_comments( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NULL sox_comments_t * comments /**< Metadata block. */ - ); - -/** -Client API: -If "id=value" is found, return value, else return null. -@returns value, or null if value not found. -*/ -LSX_RETURN_OPT -char const * -LSX_API -sox_find_comment( - LSX_PARAM_IN_OPT sox_comments_t comments, /**< Metadata block in which to search. */ - LSX_PARAM_IN_Z char const * id /**< Id for which to search */ - ); - -/** -Client API: -Find and load format handler plugins. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_format_init(void); - -/** -Client API: -Unload format handler plugins. -*/ -void -LSX_API -sox_format_quit(void); - -/** -Client API: -Initialize effects library. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_init(void); - -/** -Client API: -Close effects library and unload format handler plugins. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_quit(void); - -/** -Client API: -Returns the table of format handler names and functions. -@returns the table of format handler names and functions. -*/ -LSX_RETURN_ARRAY LSX_RETURN_PURE -sox_format_tab_t const * -LSX_API -sox_get_format_fns(void); - -/** -Client API: -Deprecated macro that returns the table of format handler names and functions. -*/ -#define sox_format_fns (sox_get_format_fns()) - -/** -Client API: -Opens a decoding session for a file. Returned handle must be closed with sox_close(). -@returns The handle for the new session, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_read( - LSX_PARAM_IN_Z char const * path, /**< Path to file to be opened (required). */ - LSX_PARAM_IN_OPT sox_signalinfo_t const * signal, /**< Information already known about audio stream, or NULL if none. */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information already known about sample encoding, or NULL if none. */ - LSX_PARAM_IN_OPT_Z char const * filetype /**< Previously-determined file type, or NULL to auto-detect. */ - ); - -/** -Client API: -Opens a decoding session for a memory buffer. Returned handle must be closed with sox_close(). -@returns The handle for the new session, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_mem_read( - LSX_PARAM_IN_BYTECOUNT(buffer_size) void * buffer, /**< Pointer to audio data buffer (required). */ - size_t buffer_size,/**< Number of bytes to read from audio data buffer. */ - LSX_PARAM_IN_OPT sox_signalinfo_t const * signal, /**< Information already known about audio stream, or NULL if none. */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information already known about sample encoding, or NULL if none. */ - LSX_PARAM_IN_OPT_Z char const * filetype /**< Previously-determined file type, or NULL to auto-detect. */ - ); - -/** -Client API: -Returns true if the format handler for the specified file type supports the specified encoding. -@returns true if the format handler for the specified file type supports the specified encoding. -*/ -sox_bool -LSX_API -sox_format_supports_encoding( - LSX_PARAM_IN_OPT_Z char const * path, /**< Path to file to be examined (required if filetype is NULL). */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to use extension from path. */ - LSX_PARAM_IN sox_encodinginfo_t const * encoding /**< Encoding for which format handler should be queried. */ - ); - -/** -Client API: -Gets the format handler for a specified file type. -@returns The found format handler, or null if not found. -*/ -LSX_RETURN_OPT -sox_format_handler_t const * -LSX_API -sox_write_handler( - LSX_PARAM_IN_OPT_Z char const * path, /**< Path to file (required if filetype is NULL). */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Filetype for which handler is needed, or NULL to use extension from path. */ - LSX_PARAM_OUT_OPT char const * * filetype1 /**< Receives the filetype that was detected. Pass NULL if not needed. */ - ); - -/** -Client API: -Opens an encoding session for a file. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_write( - LSX_PARAM_IN_Z char const * path, /**< Path to file to be written (required). */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob, /**< Out-of-band data to add to file, or NULL if none. */ - LSX_PARAM_IN_OPT sox_bool (LSX_API * overwrite_permitted)(LSX_PARAM_IN_Z char const * filename) /**< Called if file exists to determine whether overwrite is ok. */ - ); - -/** -Client API: -Opens an encoding session for a memory buffer. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_mem_write( - LSX_PARAM_OUT_BYTECAP(buffer_size) void * buffer, /**< Pointer to audio data buffer that receives data (required). */ - LSX_PARAM_IN size_t buffer_size, /**< Maximum number of bytes to write to audio data buffer. */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob /**< Out-of-band data to add to file, or NULL if none. */ - ); - -/** -Client API: -Opens an encoding session for a memstream buffer. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_memstream_write( - LSX_PARAM_OUT char * * buffer_ptr, /**< Receives pointer to audio data buffer that receives data (required). */ - LSX_PARAM_OUT size_t * buffer_size_ptr, /**< Receives size of data written to audio data buffer (required). */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob /**< Out-of-band data to add to file, or NULL if none. */ - ); - -/** -Client API: -Reads samples from a decoding session into a sample buffer. -@returns Number of samples decoded, or 0 for EOF. -*/ -size_t -LSX_API -sox_read( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(len,return) sox_sample_t *buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Writes samples to an encoding session from a sample buffer. -@returns Number of samples encoded. -*/ -size_t -LSX_API -sox_write( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_IN_COUNT(len) sox_sample_t const * buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Closes an encoding or decoding session. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_close( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Sets the location at which next samples will be decoded. Returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_seek( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - sox_uint64_t offset, /**< Sample offset at which to position reader. */ - int whence /**< Set to SOX_SEEK_SET. */ - ); - -/** -Client API: -Finds a format handler by name. -@returns Format handler data, or null if not found. -*/ -LSX_RETURN_OPT -sox_format_handler_t const * -LSX_API -sox_find_format( - LSX_PARAM_IN_Z char const * name, /**< Name of format handler to find. */ - sox_bool ignore_devices /**< Set to true to ignore device names. */ - ); - -/** -Client API: -Returns global parameters for effects -@returns global parameters for effects. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_effects_globals_t * -LSX_API -sox_get_effects_globals(void); - -/** -Client API: -Deprecated macro that returns global parameters for effects. -*/ -#define sox_effects_globals (*sox_get_effects_globals()) - -/** -Client API: -Finds the effect handler with the given name. -@returns Effect pointer, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -sox_effect_handler_t const * -LSX_API -sox_find_effect( - LSX_PARAM_IN_Z char const * name /**< Name of effect to find. */ - ); - -/** -Client API: -Creates an effect using the given handler. -@returns The new effect, or null if not found. -*/ -LSX_RETURN_OPT -sox_effect_t * -LSX_API -sox_create_effect( - LSX_PARAM_IN sox_effect_handler_t const * eh /**< Handler to use for effect. */ - ); - -/** -Client API: -Applies the command-line options to the effect. -@returns the number of arguments consumed. -*/ -int -LSX_API -sox_effect_options( - LSX_PARAM_IN sox_effect_t *effp, /**< Effect pointer on which to set options. */ - int argc, /**< Number of arguments in argv. */ - LSX_PARAM_IN_COUNT(argc) char * const argv[] /**< Array of command-line options. */ - ); - -/** -Client API: -Returns an array containing the known effect handlers. -@returns An array containing the known effect handlers. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -sox_effect_fn_t const * -LSX_API -sox_get_effect_fns(void); - -/** -Client API: -Deprecated macro that returns an array containing the known effect handlers. -*/ -#define sox_effect_fns (sox_get_effect_fns()) - -/** -Client API: -Initializes an effects chain. Returned handle must be closed with sox_delete_effects_chain(). -@returns Handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_effects_chain_t * -LSX_API -sox_create_effects_chain( - LSX_PARAM_IN sox_encodinginfo_t const * in_enc, /**< Input encoding. */ - LSX_PARAM_IN sox_encodinginfo_t const * out_enc /**< Output encoding. */ - ); - -/** -Client API: -Closes an effects chain. -*/ -void -LSX_API -sox_delete_effects_chain( - LSX_PARAM_INOUT sox_effects_chain_t *ecp /**< Effects chain pointer. */ - ); - -/** -Client API: -Adds an effect to the effects chain, returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_add_effect( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to which effect should be added . */ - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect to be added. */ - LSX_PARAM_INOUT sox_signalinfo_t * in, /**< Input format. */ - LSX_PARAM_IN sox_signalinfo_t const * out /**< Output format. */ - ); - -/** -Client API: -Runs the effects chain, returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_flow_effects( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to run. */ - LSX_PARAM_IN_OPT sox_flow_effects_callback callback, /**< Callback for monitoring flow progress. */ - LSX_PARAM_IN_OPT void * client_data /**< Data to pass into callback. */ - ); - -/** -Client API: -Gets the number of clips that occurred while running an effects chain. -@returns the number of clips that occurred while running an effects chain. -*/ -sox_uint64_t -LSX_API -sox_effects_clips( - LSX_PARAM_IN sox_effects_chain_t * chain /**< Effects chain from which to read clip information. */ - ); - -/** -Client API: -Shuts down an effect (calls stop on each of its flows). -@returns the number of clips from all flows. -*/ -sox_uint64_t -LSX_API -sox_stop_effect( - LSX_PARAM_INOUT_COUNT(effp->flows) sox_effect_t * effp /**< Effect to stop. */ - ); - -/** -Client API: -Adds an already-initialized effect to the end of the chain. -*/ -void -LSX_API -sox_push_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to which effect should be added. */ - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect to be added. */ - ); - -/** -Client API: -Removes and returns an effect from the end of the chain. -@returns the removed effect, or null if no effects. -*/ -LSX_RETURN_OPT -sox_effect_t * -LSX_API -sox_pop_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to remove an effect. */ - ); - -/** -Client API: -Shut down and delete an effect. -*/ -void -LSX_API -sox_delete_effect( - LSX_PARAM_INOUT_COUNT(effp->flows) sox_effect_t *effp /**< Effect to be deleted. */ - ); - -/** -Client API: -Shut down and delete the last effect in the chain. -*/ -void -LSX_API -sox_delete_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to remove the last effect. */ - ); - -/** -Client API: -Shut down and delete all effects in the chain. -*/ -void -LSX_API -sox_delete_effects( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to delete effects. */ - ); - -/** -Client API: -Gets the sample offset of the start of the trim, useful for efficiently -skipping the part that will be trimmed anyway (get trim start, seek, then -clear trim start). -@returns the sample offset of the start of the trim. -*/ -sox_uint64_t -LSX_API -sox_trim_get_start( - LSX_PARAM_IN sox_effect_t * effp /**< Trim effect. */ - ); - -/** -Client API: -Clears the start of the trim to 0. -*/ -void -LSX_API -sox_trim_clear_start( - LSX_PARAM_INOUT sox_effect_t * effp /**< Trim effect. */ - ); - -/** -Client API: -Returns true if the specified file is a known playlist file type. -@returns true if the specified file is a known playlist file type. -*/ -sox_bool -LSX_API -sox_is_playlist( - LSX_PARAM_IN_Z char const * filename /**< Name of file to examine. */ - ); - -/** -Client API: -Parses the specified playlist file. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_parse_playlist( - LSX_PARAM_IN sox_playlist_callback_t callback, /**< Callback to call for each item in the playlist. */ - void * p, /**< Data to pass to callback. */ - LSX_PARAM_IN char const * const listname /**< Filename of playlist file. */ - ); - -/** -Client API: -Converts a SoX error code into an error string. -@returns error string corresponding to the specified error code, -or a generic message if the error code is not recognized. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -sox_strerror( - int sox_errno /**< Error code to look up. */ - ); - -/** -Client API: -Gets the basename of the specified file; for example, the basename of -"/a/b/c.d" would be "c". -@returns the number of characters written to base_buffer, excluding the null, -or 0 on failure. -*/ -size_t -LSX_API -sox_basename( - LSX_PARAM_OUT_Z_CAP_POST_COUNT(base_buffer_len,return) char * base_buffer, /**< Buffer into which basename should be written. */ - size_t base_buffer_len, /**< Size of base_buffer, in bytes. */ - LSX_PARAM_IN_Z char const * filename /**< Filename from which to extract basename. */ - ); - -/***************************************************************************** -Internal API: -WARNING - The items in this section are subject to instability. They only -exist in the public header because sox (the application) currently uses them. -These may be changed or removed in future versions of libSoX. -*****************************************************************************/ - -/** -Plugins API: -Print a fatal error in libSoX. -*/ -void -LSX_API -lsx_fail_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print a warning in libSoX. -*/ -void -LSX_API -lsx_warn_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print an informational message in libSoX. -*/ -void -LSX_API -lsx_report_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print a debug message in libSoX. -*/ -void -LSX_API -lsx_debug_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Report a fatal error in libSoX; printf-style arguments must follow. -*/ -#define lsx_fail sox_get_globals()->subsystem=__FILE__,lsx_fail_impl - -/** -Plugins API: -Report a warning in libSoX; printf-style arguments must follow. -*/ -#define lsx_warn sox_get_globals()->subsystem=__FILE__,lsx_warn_impl - -/** -Plugins API: -Report an informational message in libSoX; printf-style arguments must follow. -*/ -#define lsx_report sox_get_globals()->subsystem=__FILE__,lsx_report_impl - -/** -Plugins API: -Report a debug message in libSoX; printf-style arguments must follow. -*/ -#define lsx_debug sox_get_globals()->subsystem=__FILE__,lsx_debug_impl - -/** -Plugins API: -String name and integer values for enumerated types (type metadata), for use -with LSX_ENUM_ITEM, lsx_find_enum_text, and lsx_find_enum_value. -*/ -typedef struct lsx_enum_item { - char const *text; /**< String name of enumeration. */ - unsigned value; /**< Integer value of enumeration. */ -} lsx_enum_item; - -/** -Plugins API: -Declares a static instance of an lsx_enum_item structure in format -{ "item", prefixitem }, for use in declaring lsx_enum_item[] arrays. -@param prefix The prefix to prepend to the item in the enumeration symbolic name. -@param item The user-visible text name of the item (must also be a valid C symbol name). -*/ -#define LSX_ENUM_ITEM(prefix, item) {#item, prefix##item}, - -/** -Plugins API: -Flags for use with lsx_find_enum_item. -*/ -enum -{ - lsx_find_enum_item_none = 0, /**< Default parameters (case-insensitive). */ - lsx_find_enum_item_case_sensitive = 1 /**< Enable case-sensitive search. */ -}; - -/** -Plugins API: -Looks up an enumeration by name in an array of lsx_enum_items. -@returns the corresponding item, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -lsx_enum_item const * -LSX_API -lsx_find_enum_text( - LSX_PARAM_IN_Z char const * text, /**< Name of enumeration to find. */ - LSX_PARAM_IN lsx_enum_item const * lsx_enum_items, /**< Array of items to search, with text == NULL for last item. */ - int flags /**< Search flags: 0 (case-insensitive) or lsx_find_enum_item_case_sensitive (case-sensitive). */ - ); - -/** -Plugins API: -Looks up an enumeration by value in an array of lsx_enum_items. -@returns the corresponding item, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -lsx_enum_item const * -LSX_API -lsx_find_enum_value( - unsigned value, /**< Enumeration value to find. */ - LSX_PARAM_IN lsx_enum_item const * lsx_enum_items /**< Array of items to search, with text == NULL for last item. */ - ); - -/** -Plugins API: -Looks up a command-line argument in a set of enumeration names, showing an -error message if the argument is not found in the set of names. -@returns The enumeration value corresponding to the matching enumeration, or -INT_MAX if the argument does not match any enumeration name. -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_enum_option( - int c, /**< Option character to which arg is associated, for example with -a, c would be 'a'. */ - LSX_PARAM_IN_Z char const * arg, /**< Argument to find in enumeration list. */ - LSX_PARAM_IN lsx_enum_item const * items /**< Array of items to search, with text == NULL for last item. */ - ); - -/** -Plugins API: -Determines whether the specified string ends with the specified suffix (case-sensitive). -@returns true if the specified string ends with the specified suffix. -*/ -LSX_RETURN_PURE -sox_bool -LSX_API -lsx_strends( - LSX_PARAM_IN_Z char const * str, /**< String to search. */ - LSX_PARAM_IN_Z char const * end /**< Suffix to search for. */ - ); - -/** -Plugins API: -Finds the file extension for a filename. -@returns the file extension, not including the '.', or null if filename does -not have an extension. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -lsx_find_file_extension( - LSX_PARAM_IN_Z char const * pathname /**< Filename to search for extension. */ - ); - -/** -Plugins API: -Formats the specified number with up to three significant figures and adds a -metric suffix in place of the exponent, such as 1.23G. -@returns A static buffer with the formatted number, valid until the next time -this function is called (note: not thread safe). -*/ -LSX_RETURN_VALID_Z -char const * -LSX_API -lsx_sigfigs3( - double number /**< Number to be formatted. */ - ); - -/** -Plugins API: -Formats the specified number as a percentage, showing up to three significant -figures. -@returns A static buffer with the formatted number, valid until the next time -this function is called (note: not thread safe). -*/ -LSX_RETURN_VALID_Z -char const * -LSX_API -lsx_sigfigs3p( - double percentage /**< Number to be formatted. */ - ); - -/** -Plugins API: -Allocates, deallocates, or resizes; like C's realloc, except that this version -terminates the running application if unable to allocate the requested memory. -@returns New buffer, or null if buffer was freed. -*/ -LSX_RETURN_OPT -void * -LSX_API -lsx_realloc( - LSX_PARAM_IN_OPT void *ptr, /**< Pointer to be freed or resized, or null if allocating a new buffer. */ - size_t newsize /**< New size for buffer, or 0 to free the buffer. */ - ); - -/** -Plugins API: -Like strcmp, except that the characters are compared without regard to case. -@returns 0 (s1 == s2), negative (s1 < s2), or positive (s1 > s2). -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_strcasecmp( - LSX_PARAM_IN_Z char const * s1, /**< First string. */ - LSX_PARAM_IN_Z char const * s2 /**< Second string. */ - ); - - -/** -Plugins API: -Like strncmp, except that the characters are compared without regard to case. -@returns 0 (s1 == s2), negative (s1 < s2), or positive (s1 > s2). -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_strncasecmp( - LSX_PARAM_IN_Z char const * s1, /**< First string. */ - LSX_PARAM_IN_Z char const * s2, /**< Second string. */ - size_t n /**< Maximum number of characters to examine. */ - ); - -/** -Plugins API: -Is option argument unsupported, required, or optional. -*/ -typedef enum lsx_option_arg_t { - lsx_option_arg_none, /**< Option does not have an argument. */ - lsx_option_arg_required, /**< Option requires an argument. */ - lsx_option_arg_optional /**< Option can optionally be followed by an argument. */ -} lsx_option_arg_t; - -/** -Plugins API: -lsx_getopt_init options. -*/ -typedef enum lsx_getopt_flags_t { - lsx_getopt_flag_none = 0, /**< no flags (no output, not long-only) */ - lsx_getopt_flag_opterr = 1, /**< if set, invalid options trigger lsx_warn output */ - lsx_getopt_flag_longonly = 2 /**< if set, recognize -option as a long option */ -} lsx_getopt_flags_t; - -/** -Plugins API: -lsx_getopt long option descriptor. -*/ -typedef struct lsx_option_t { - char const * name; /**< Name of the long option. */ - lsx_option_arg_t has_arg; /**< Whether the long option supports an argument and, if so, whether the argument is required or optional. */ - int * flag; /**< Flag to set if argument is present. */ - int val; /**< Value to put in flag if argument is present. */ -} lsx_option_t; - -/** -Plugins API: -lsx_getopt session information (initialization data and state). -*/ -typedef struct lsx_getopt_t { - int argc; /**< IN argc: Number of arguments in argv */ - char * const * argv; /**< IN argv: Array of arguments */ - char const * shortopts;/**< IN shortopts: Short option characters */ - lsx_option_t const * longopts; /**< IN longopts: Array of long option descriptors */ - lsx_getopt_flags_t flags; /**< IN flags: Flags for longonly and opterr */ - char const * curpos; /**< INOUT curpos: Maintains state between calls to lsx_getopt */ - int ind; /**< INOUT optind: Maintains the index of next element to be processed */ - int opt; /**< OUT optopt: Receives the option character that caused error */ - char const * arg; /**< OUT optarg: Receives the value of the option's argument */ - int lngind; /**< OUT lngind: Receives the index of the matched long option or -1 if not a long option */ -} lsx_getopt_t; - -/** -Plugins API: -Initializes an lsx_getopt_t structure for use with lsx_getopt. -*/ -void -LSX_API -lsx_getopt_init( - LSX_PARAM_IN int argc, /**< Number of arguments in argv */ - LSX_PARAM_IN_COUNT(argc) char * const * argv, /**< Array of arguments */ - LSX_PARAM_IN_Z char const * shortopts, /**< Short options, for example ":abc:def::ghi" (+/- not supported) */ - LSX_PARAM_IN_OPT lsx_option_t const * longopts, /**< Array of long option descriptors */ - LSX_PARAM_IN lsx_getopt_flags_t flags, /**< Flags for longonly and opterr */ - LSX_PARAM_IN int first, /**< First argv to check (usually 1) */ - LSX_PARAM_OUT lsx_getopt_t * state /**< State object to be initialized */ - ); - -/** -Plugins API: -Gets the next option. Options are parameters that start with "-" or "--". -If no more options, returns -1. If unrecognized short option, returns '?'. -If a recognized short option is missing a required argument, -return (shortopts[0]==':' ? ':' : '?'). If successfully recognized short -option, return the recognized character. If successfully recognized long -option, returns (option.flag ? 0 : option.val). -Note: lsx_getopt does not permute the non-option arguments. -@returns option character (short), val or 0 (long), or -1 (no more). -*/ -int -LSX_API -lsx_getopt( - LSX_PARAM_INOUT lsx_getopt_t * state /**< The getopt state pointer. */ - ); - -/* WARNING END */ - -#if defined(__cplusplus) -} -#endif - -#endif /* SOX_H */ diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/sox_i.h b/freedv/tags/1.2.2/freedv-dev/src/sox/sox_i.h deleted file mode 100644 index 9d533263..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/sox_i.h +++ /dev/null @@ -1,417 +0,0 @@ -/* libSoX Internal header - * - * This file is meant for libSoX internal use only - * - * Copyright 2001-2008 Chris Bagwell and SoX Contributors - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Chris Bagwell And SoX Contributors are not responsible for - * the consequences of using this software. - */ - -#ifndef SOX_I_H -#define SOX_I_H - -#include "soxomp.h" /* Note: soxomp.h includes soxconfig.h */ -#include "sox.h" - -#define __FREEDV__ - -#if defined HAVE_FMEMOPEN -#define _GNU_SOURCE -#endif - -#include -#include -#include - -#include "util.h" - -#if defined(LSX_EFF_ALIAS) -#undef lsx_debug -#undef lsx_fail -#undef lsx_report -#undef lsx_warn -#define lsx_debug sox_globals.subsystem=effp->handler.name,lsx_debug_impl -#define lsx_fail sox_globals.subsystem=effp->handler.name,lsx_fail_impl -#define lsx_report sox_globals.subsystem=effp->handler.name,lsx_report_impl -#define lsx_warn sox_globals.subsystem=effp->handler.name,lsx_warn_impl -#endif - -#define RANQD1 ranqd1(sox_globals.ranqd1) -#define DRANQD1 dranqd1(sox_globals.ranqd1) - -typedef enum {SOX_SHORT, SOX_INT, SOX_FLOAT, SOX_DOUBLE} sox_data_t; -typedef enum {SOX_WAVE_SINE, SOX_WAVE_TRIANGLE} lsx_wave_t; -lsx_enum_item const * lsx_get_wave_enum(void); - -/* Define fseeko and ftello for platforms lacking them */ -#ifndef HAVE_FSEEKO -#define fseeko fseek -#define ftello ftell -#endif - -#ifdef _FILE_OFFSET_BITS -assert_static(sizeof(off_t) == _FILE_OFFSET_BITS >> 3, OFF_T_BUILD_PROBLEM); -#endif - -FILE * lsx_tmpfile(void); - -void lsx_debug_more_impl(char const * fmt, ...) LSX_PRINTF12; -void lsx_debug_most_impl(char const * fmt, ...) LSX_PRINTF12; - -#define lsx_debug_more sox_get_globals()->subsystem=__FILE__,lsx_debug_more_impl -#define lsx_debug_most sox_get_globals()->subsystem=__FILE__,lsx_debug_most_impl - -/* Digitise one cycle of a wave and store it as - * a table of samples of a specified data-type. - */ -void lsx_generate_wave_table( - lsx_wave_t wave_type, - sox_data_t data_type, - void * table, /* Really of type indicated by data_type. */ - size_t table_size, /* Number of points on the x-axis. */ - double min, /* Minimum value on the y-axis. (e.g. -1) */ - double max, /* Maximum value on the y-axis. (e.g. +1) */ - double phase); /* Phase at 1st point; 0..2pi. (e.g. pi/2 for cosine) */ -char const * lsx_parsesamples(sox_rate_t rate, const char *str, uint64_t *samples, int def); -int lsx_parse_note(char const * text, char * * end_ptr); -double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key); -#define lsx_parse_frequency(a, b) lsx_parse_frequency_k(a, b, INT_MAX) -FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename); - -void lsx_prepare_spline3(double const * x, double const * y, int n, - double start_1d, double end_1d, double * y_2d); -double lsx_spline3(double const * x, double const * y, double const * y_2d, - int n, double x1); - -double lsx_bessel_I_0(double x); -int lsx_set_dft_length(int num_taps); -void init_fft_cache(void); -void clear_fft_cache(void); -void lsx_safe_rdft(int len, int type, double * d); -void lsx_safe_cdft(int len, int type, double * d); -void lsx_power_spectrum(int n, double const * in, double * out); -void lsx_power_spectrum_f(int n, float const * in, float * out); -void lsx_apply_hann_f(float h[], const int num_points); -void lsx_apply_hann(double h[], const int num_points); -void lsx_apply_hamming(double h[], const int num_points); -void lsx_apply_bartlett(double h[], const int num_points); -void lsx_apply_blackman(double h[], const int num_points, double alpha); -void lsx_apply_blackman_nutall(double h[], const int num_points); -double lsx_kaiser_beta(double att); -void lsx_apply_kaiser(double h[], const int num_points, double beta); -double * lsx_make_lpf(int num_taps, double Fc, double beta, double scale, sox_bool dc_norm); -int lsx_lpf_num_taps(double att, double tr_bw, int k); -double * lsx_design_lpf( - double Fp, /* End of pass-band; ~= 0.01dB point */ - double Fc, /* Start of stop-band */ - double Fn, /* Nyquist freq; e.g. 0.5, 1, PI */ - sox_bool allow_aliasing, - double att, /* Stop-band attenuation in dB */ - int * num_taps, /* (Single phase.) 0: value will be estimated */ - int k); /* Number of phases; 0 for single-phase */ -void lsx_fir_to_phase(double * * h, int * len, - int * post_len, double phase0); -#define LSX_TO_6dB .5869 -#define LSX_TO_3dB ((2/3.) * (.5 + LSX_TO_6dB)) -#define LSX_MAX_TBW0 36. -#define LSX_MAX_TBW0A (LSX_MAX_TBW0 / (1 + LSX_TO_3dB)) -#define LSX_MAX_TBW3 floor(LSX_MAX_TBW0 * LSX_TO_3dB) -#define LSX_MAX_TBW3A floor(LSX_MAX_TBW0A * LSX_TO_3dB) -void lsx_plot_fir(double * h, int num_points, sox_rate_t rate, sox_plot_t type, char const * title, double y1, double y2); - -#ifdef HAVE_BYTESWAP_H -#include -#define lsx_swapw(x) bswap_16(x) -#define lsx_swapdw(x) bswap_32(x) -#elif defined(_MSC_VER) -#define lsx_swapw(x) _byteswap_ushort(x) -#define lsx_swapdw(x) _byteswap_ulong(x) -#else -#define lsx_swapw(uw) (((uw >> 8) | (uw << 8)) & 0xffff) -#define lsx_swapdw(udw) ((udw >> 24) | ((udw >> 8) & 0xff00) | ((udw << 8) & 0xff0000) | (udw << 24)) -#endif - - - -/*------------------------ Implemented in libsoxio.c -------------------------*/ - -/* Read and write basic data types from "ft" stream. */ -size_t lsx_readbuf(sox_format_t * ft, void *buf, size_t len); -int lsx_skipbytes(sox_format_t * ft, size_t n); -int lsx_padbytes(sox_format_t * ft, size_t n); -size_t lsx_writebuf(sox_format_t * ft, void const *buf, size_t len); -int lsx_reads(sox_format_t * ft, char *c, size_t len); -int lsx_writes(sox_format_t * ft, char const * c); -void lsx_set_signal_defaults(sox_format_t * ft); -#define lsx_writechars(ft, chars, len) (lsx_writebuf(ft, chars, len) == len? SOX_SUCCESS : SOX_EOF) - -size_t lsx_read_3_buf(sox_format_t * ft, sox_uint24_t *buf, size_t len); -size_t lsx_read_b_buf(sox_format_t * ft, uint8_t *buf, size_t len); -size_t lsx_read_df_buf(sox_format_t * ft, double *buf, size_t len); -size_t lsx_read_dw_buf(sox_format_t * ft, uint32_t *buf, size_t len); -size_t lsx_read_qw_buf(sox_format_t * ft, uint64_t *buf, size_t len); -size_t lsx_read_f_buf(sox_format_t * ft, float *buf, size_t len); -size_t lsx_read_w_buf(sox_format_t * ft, uint16_t *buf, size_t len); - -size_t lsx_write_3_buf(sox_format_t * ft, sox_uint24_t *buf, size_t len); -size_t lsx_write_b_buf(sox_format_t * ft, uint8_t *buf, size_t len); -size_t lsx_write_df_buf(sox_format_t * ft, double *buf, size_t len); -size_t lsx_write_dw_buf(sox_format_t * ft, uint32_t *buf, size_t len); -size_t lsx_write_qw_buf(sox_format_t * ft, uint64_t *buf, size_t len); -size_t lsx_write_f_buf(sox_format_t * ft, float *buf, size_t len); -size_t lsx_write_w_buf(sox_format_t * ft, uint16_t *buf, size_t len); - -int lsx_read3(sox_format_t * ft, sox_uint24_t * u3); -int lsx_readb(sox_format_t * ft, uint8_t * ub); -int lsx_readchars(sox_format_t * ft, char * chars, size_t len); -int lsx_readdf(sox_format_t * ft, double * d); -int lsx_readdw(sox_format_t * ft, uint32_t * udw); -int lsx_readqw(sox_format_t * ft, uint64_t * udw); -int lsx_readf(sox_format_t * ft, float * f); -int lsx_readw(sox_format_t * ft, uint16_t * uw); - -#if 1 /* FIXME: use defines */ -UNUSED static int lsx_readsb(sox_format_t * ft, int8_t * sb) -{return lsx_readb(ft, (uint8_t *)sb);} -UNUSED static int lsx_readsw(sox_format_t * ft, int16_t * sw) -{return lsx_readw(ft, (uint16_t *)sw);} -#else -#define lsx_readsb(ft, sb) lsx_readb(ft, (uint8_t *)sb) -#define lsx_readsw(ft, sw) lsx_readb(ft, (uint16_t *)sw) -#endif - -int lsx_write3(sox_format_t * ft, unsigned u3); -int lsx_writeb(sox_format_t * ft, unsigned ub); -int lsx_writedf(sox_format_t * ft, double d); -int lsx_writedw(sox_format_t * ft, unsigned udw); -int lsx_writeqw(sox_format_t * ft, uint64_t uqw); -int lsx_writef(sox_format_t * ft, double f); -int lsx_writew(sox_format_t * ft, unsigned uw); - -int lsx_writesb(sox_format_t * ft, signed); -int lsx_writesw(sox_format_t * ft, signed); - -int lsx_eof(sox_format_t * ft); -int lsx_error(sox_format_t * ft); -int lsx_flush(sox_format_t * ft); -int lsx_seeki(sox_format_t * ft, off_t offset, int whence); -int lsx_unreadb(sox_format_t * ft, unsigned ub); -uint64_t lsx_filelength(sox_format_t * ft); -off_t lsx_tell(sox_format_t * ft); -void lsx_clearerr(sox_format_t * ft); -void lsx_rewind(sox_format_t * ft); - -int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, off_t to_sample); - -void lsx_fail_errno(sox_format_t *, int, const char *, ...) -#ifdef __GNUC__ -__attribute__ ((format (printf, 3, 4))); -#else -; -#endif - -typedef struct sox_formats_globals { /* Global parameters (for formats) */ - sox_globals_t * global_info; -} sox_formats_globals; - - - -/*------------------------------ File Handlers -------------------------------*/ - -int lsx_check_read_params(sox_format_t * ft, unsigned channels, - sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, - uint64_t num_samples, sox_bool check_length); -#define LSX_FORMAT_HANDLER(name) \ -sox_format_handler_t const * lsx_##name##_format_fn(void); \ -sox_format_handler_t const * lsx_##name##_format_fn(void) -#define div_bits(size, bits) ((uint64_t)(size) * 8 / bits) - -/* Raw I/O */ -int lsx_rawstartread(sox_format_t * ft); -size_t lsx_rawread(sox_format_t * ft, sox_sample_t *buf, size_t nsamp); -int lsx_rawstopread(sox_format_t * ft); -int lsx_rawstartwrite(sox_format_t * ft); -size_t lsx_rawwrite(sox_format_t * ft, const sox_sample_t *buf, size_t nsamp); -int lsx_rawseek(sox_format_t * ft, uint64_t offset); -int lsx_rawstart(sox_format_t * ft, sox_bool default_rate, sox_bool default_channels, sox_bool default_length, sox_encoding_t encoding, unsigned bits_per_sample); -#define lsx_rawstartread(ft) lsx_rawstart(ft, sox_false, sox_false, sox_false, SOX_ENCODING_UNKNOWN, 0) -#define lsx_rawstartwrite lsx_rawstartread -#define lsx_rawstopread NULL -#define lsx_rawstopwrite NULL - -extern sox_format_handler_t const * lsx_sndfile_format_fn(void); - -char * lsx_cat_comments(sox_comments_t comments); - -/*--------------------------------- Effects ----------------------------------*/ - -int lsx_flow_copy(sox_effect_t * effp, const sox_sample_t * ibuf, - sox_sample_t * obuf, size_t * isamp, size_t * osamp); -int lsx_usage(sox_effect_t * effp); -char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n); -#define EFFECT(f) extern sox_effect_handler_t const * lsx_##f##_effect_fn(void); -#include "effects.h" -#undef EFFECT - -#define NUMERIC_PARAMETER(name, min, max) { \ - char * end_ptr; \ - double d; \ - if (argc == 0) break; \ - d = strtod(*argv, &end_ptr); \ - if (end_ptr != *argv) { \ - if (d < min || d > max || *end_ptr != '\0') {\ - lsx_fail("parameter `%s' must be between %g and %g", #name, (double)min, (double)max); \ - return lsx_usage(effp); \ - } \ - p->name = d; \ - --argc, ++argv; \ - } \ -} - -#define TEXTUAL_PARAMETER(name, enum_table) { \ - lsx_enum_item const * e; \ - if (argc == 0) break; \ - e = lsx_find_enum_text(*argv, enum_table, 0); \ - if (e != NULL) { \ - p->name = e->value; \ - --argc, ++argv; \ - } \ -} - -#define GETOPT_NUMERIC(state, ch, name, min, max) case ch:{ \ - char * end_ptr; \ - double d = strtod(state.arg, &end_ptr); \ - if (end_ptr == state.arg || d < min || d > max || *end_ptr != '\0') {\ - lsx_fail("parameter `%s' must be between %g and %g", #name, (double)min, (double)max); \ - return lsx_usage(effp); \ - } \ - p->name = d; \ - break; \ -} - -int lsx_effect_set_imin(sox_effect_t * effp, size_t imin); - -int lsx_effects_init(void); -int lsx_effects_quit(void); - -/*--------------------------------- Dynamic Library ----------------------------------*/ - -#if defined(HAVE_WIN32_LTDL_H) - #include "win32-ltdl.h" - #define HAVE_LIBLTDL 1 - typedef lt_dlhandle lsx_dlhandle; -#elif defined(HAVE_LIBLTDL) - #include - typedef lt_dlhandle lsx_dlhandle; -#else - struct lsx_dlhandle_tag; - typedef struct lsx_dlhandle_tag *lsx_dlhandle; -#endif - -typedef void (*lsx_dlptr)(void); - -typedef struct lsx_dlfunction_info -{ - const char* name; - lsx_dlptr static_func; - lsx_dlptr stub_func; -} lsx_dlfunction_info; - -int lsx_open_dllibrary( - int show_error_on_failure, - const char* library_description, - const char * const library_names[], - const lsx_dlfunction_info func_infos[], - lsx_dlptr selected_funcs[], - lsx_dlhandle* pdl); - -void lsx_close_dllibrary( - lsx_dlhandle dl); - -#define LSX_DLENTRIES_APPLY__(entries, f, x) entries(f, x) - -#define LSX_DLENTRY_TO_PTR__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - func_return (*func_ptr) func_args; - -#define LSX_DLENTRIES_TO_FUNCTIONS__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - func_return func_name func_args; - -/* LSX_DLENTRIES_TO_PTRS: Given an ENTRIES macro and the name of the dlhandle - variable, declares the corresponding function pointer variables and the - dlhandle variable. */ -#define LSX_DLENTRIES_TO_PTRS(entries, dlhandle) \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRY_TO_PTR__, 0) \ - lsx_dlhandle dlhandle - -/* LSX_DLENTRIES_TO_FUNCTIONS: Given an ENTRIES macro, declares the corresponding - functions. */ -#define LSX_DLENTRIES_TO_FUNCTIONS(entries) \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRIES_TO_FUNCTIONS__, 0) - -#define LSX_DLLIBRARY_OPEN1__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - { #func_name, (lsx_dlptr)(static_func), (lsx_dlptr)(stub_func) }, - -#define LSX_DLLIBRARY_OPEN2__(ptr_container, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - (ptr_container)->func_ptr = (func_return (*)func_args)lsx_dlfunction_open_library_funcs[lsx_dlfunction_open_library_index++]; - -/* LSX_DLLIBRARY_OPEN: Input an ENTRIES macro, the library's description, - a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }), - the name of the dlhandle variable, the name of the structure that contains - the function pointer and dlhandle variables, and the name of the variable in - which the result of the lsx_open_dllibrary call should be stored. This will - call lsx_open_dllibrary and copy the resulting function pointers into the - structure members. If the library cannot be opened, show a failure message. */ -#define LSX_DLLIBRARY_OPEN(ptr_container, dlhandle, entries, library_description, library_names, return_var) \ - LSX_DLLIBRARY_TRYOPEN(1, ptr_container, dlhandle, entries, library_description, library_names, return_var) - -/* LSX_DLLIBRARY_TRYOPEN: Input an ENTRIES macro, the library's description, - a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }), - the name of the dlhandle variable, the name of the structure that contains - the function pointer and dlhandle variables, and the name of the variable in - which the result of the lsx_open_dllibrary call should be stored. This will - call lsx_open_dllibrary and copy the resulting function pointers into the - structure members. If the library cannot be opened, show a report or a failure - message, depending on whether error_on_failure is non-zero. */ -#define LSX_DLLIBRARY_TRYOPEN(error_on_failure, ptr_container, dlhandle, entries, library_description, library_names, return_var) \ - do { \ - lsx_dlfunction_info lsx_dlfunction_open_library_infos[] = { \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN1__, 0) \ - {NULL,NULL,NULL} }; \ - int lsx_dlfunction_open_library_index = 0; \ - lsx_dlptr lsx_dlfunction_open_library_funcs[sizeof(lsx_dlfunction_open_library_infos)/sizeof(lsx_dlfunction_open_library_infos[0])]; \ - (return_var) = lsx_open_dllibrary((error_on_failure), (library_description), (library_names), lsx_dlfunction_open_library_infos, lsx_dlfunction_open_library_funcs, &(ptr_container)->dlhandle); \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN2__, ptr_container) \ - } while(0) - -#define LSX_DLLIBRARY_CLOSE(ptr_container, dlhandle) \ - lsx_close_dllibrary((ptr_container)->dlhandle) - - /* LSX_DLENTRY_STATIC: For use in creating an ENTRIES macro. func is - expected to be available at link time. If not present, link will fail. */ -#define LSX_DLENTRY_STATIC(f,x, ret, func, args) f(x, ret, func, args, func, NULL, func) - - /* LSX_DLENTRY_DYNAMIC: For use in creating an ENTRIES macro. func need - not be available at link time (and if present, the link time version will - not be used). func will be loaded via dlsym. If this function is not - found in the shared library, the shared library will not be used. */ -#define LSX_DLENTRY_DYNAMIC(f,x, ret, func, args) f(x, ret, func, args, NULL, NULL, func) - - /* LSX_DLENTRY_STUB: For use in creating an ENTRIES macro. func need not - be available at link time (and if present, the link time version will not - be used). If using DL_LAME, the func may be loaded via dlopen/dlsym, but - if not found, the shared library will still be used if all of the - non-stub functions are found. If the function is not found via dlsym (or - if we are not loading any shared libraries), the stub will be used. This - assumes that the name of the stub function is the name of the function + - "_stub". */ -#define LSX_DLENTRY_STUB(f,x, ret, func, args) f(x, ret, func, args, NULL, func##_stub, func) - - /* LSX_DLFUNC_IS_STUB: returns true if the named function is a do-nothing - stub. Assumes that the name of the stub function is the name of the - function + "_stub". */ -#define LSX_DLFUNC_IS_STUB(ptr_container, func) ((ptr_container)->func == func##_stub) - -#endif diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/soxomp.h b/freedv/tags/1.2.2/freedv-dev/src/sox/soxomp.h deleted file mode 100644 index 6fce07d9..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/soxomp.h +++ /dev/null @@ -1,38 +0,0 @@ -#include "soxconfig.h" - -#ifdef HAVE_OPENMP - #include -#else - -typedef int omp_lock_t; -typedef int omp_nest_lock_t; - -#define omp_set_num_threads(int) (void)0 -#define omp_get_num_threads() 1 -#define omp_get_max_threads() 1 -#define omp_get_thread_num() 0 -#define omp_get_num_procs() 1 -#define omp_in_parallel() 1 - -#define omp_set_dynamic(int) (void)0 -#define omp_get_dynamic() 0 - -#define omp_set_nested(int) (void)0 -#define omp_get_nested() 0 - -#define omp_init_lock(omp_lock_t) (void)0 -#define omp_destroy_lock(omp_lock_t) (void)0 -#define omp_set_lock(omp_lock_t) (void)0 -#define omp_unset_lock(omp_lock_t) (void)0 -#define omp_test_lock(omp_lock_t) 0 - -#define omp_init_nest_lock(omp_nest_lock_t) (void)0 -#define omp_destroy_nest_lock(omp_nest_lock_t) (void)0 -#define omp_set_nest_lock(omp_nest_lock_t) (void)0 -#define omp_unset_nest_lock(omp_nest_lock_t) (void)0 -#define omp_test_nest_lock(omp_nest_lock_t) 0 - -#define omp_get_wtime() 0 -#define omp_get_wtick() 0 - -#endif diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/util.h b/freedv/tags/1.2.2/freedv-dev/src/sox/util.h deleted file mode 100644 index 89bbe752..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/util.h +++ /dev/null @@ -1,231 +0,0 @@ -/* General purpose, i.e. non SoX specific, utility functions and macros. - * - * (c) 2006-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "soxconfig.h" - -#ifdef HAVE_SYS_TYPES_H -#include /* For off_t not found in stdio.h */ -#endif - -#ifdef HAVE_SYS_STAT_H -#include /* Needs to be included before we redefine off_t. */ -#endif - -#include "xmalloc.h" - -/*---------------------------- Portability stuff -----------------------------*/ - -#if defined(HAVE_INTTYPES_H) - #include -#elif defined(HAVE_STDINT_H) - #include -#else - typedef sox_int8_t int8_t; - typedef sox_uint8_t uint8_t; - typedef sox_int16_t int16_t; - typedef sox_uint16_t uint16_t; - typedef sox_int32_t int32_t; - typedef sox_uint32_t uint32_t; - typedef sox_int64_t int64_t; - typedef sox_uint64_t uint64_t; -#endif - -/* Define the format specifier to use for int64_t values. - * Example: printf("You may have already won $ %" PRId64 " !!!", n64); */ -#ifndef PRId64 /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %lld. */ -#define PRId64 "I64d" -#elif LONG_MAX==9223372036854775807 -#define PRId64 "ld" -#else -#define PRId64 "lld" -#endif -#endif /* PRId64 */ - -/* Define the format specifier to use for uint64_t values. */ -#ifndef PRIu64 /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %llu. */ -#define PRIu64 "I64u" -#elif ULONG_MAX==0xffffffffffffffff -#define PRIu64 "lu" -#else -#define PRIu64 "llu" -#endif -#endif /* PRIu64 */ - -/* Define the format specifier to use for size_t values. - * Example: printf("Sizeof(x) = %" PRIuPTR " bytes", sizeof(x)); */ -#ifndef PRIuPTR /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %zu. */ -#define PRIuPTR "Iu" -#else -#define PRIuPTR "zu" -#endif -#endif /* PRIuPTR */ - -#ifdef __GNUC__ -#define NORET __attribute__((noreturn)) -#define UNUSED __attribute__ ((unused)) -#else -#define NORET -#define UNUSED -#endif - -#ifdef _MSC_VER - -#define __STDC__ 1 -#define O_BINARY _O_BINARY -#define O_CREAT _O_CREAT -#define O_RDWR _O_RDWR -#define O_TRUNC _O_TRUNC -#define S_IFMT _S_IFMT -#define S_IFREG _S_IFREG -#define S_IREAD _S_IREAD -#define S_IWRITE _S_IWRITE -#define close _close -#define dup _dup -#define fdopen _fdopen -#define fileno _fileno - -#ifdef _fstati64 -#define fstat _fstati64 -#else -#define fstat _fstat -#endif - -#define ftime _ftime -#define inline __inline -#define isatty _isatty -#define kbhit _kbhit -#define mktemp _mktemp -#define off_t _off_t -#define open _open -#define pclose _pclose -#define popen _popen -#define setmode _setmode -#define snprintf _snprintf - -#ifdef _stati64 -#define stat _stati64 -#else -#define stat _stat -#endif - -#define strdup _strdup -#define timeb _timeb -#define unlink _unlink - -#if defined(HAVE__FSEEKI64) && !defined(HAVE_FSEEKO) -#undef off_t -#define fseeko _fseeki64 -#define ftello _ftelli64 -#define off_t __int64 -#define HAVE_FSEEKO 1 -#endif - -#elif defined(__MINGW32__) - -#if !defined(HAVE_FSEEKO) -#undef off_t -#define fseeko fseeko64 -#define fstat _fstati64 -#define ftello ftello64 -#define off_t off64_t -#define stat _stati64 -#define HAVE_FSEEKO 1 -#endif - -#endif - -#if defined(DOS) || defined(WIN32) || defined(__NT__) || defined(__DJGPP__) || defined(__OS2__) - #define LAST_SLASH(path) max(strrchr(path, '/'), strrchr(path, '\\')) - #define IS_ABSOLUTE(path) ((path)[0] == '/' || (path)[0] == '\\' || (path)[1] == ':') - #define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) - #define POPEN_MODE "rb" -#else - #define LAST_SLASH(path) strrchr(path, '/') - #define IS_ABSOLUTE(path) ((path)[0] == '/') - #define SET_BINARY_MODE(file) -#endif - -#ifdef WORDS_BIGENDIAN - #define MACHINE_IS_BIGENDIAN 1 - #define MACHINE_IS_LITTLEENDIAN 0 -#else - #define MACHINE_IS_BIGENDIAN 0 - #define MACHINE_IS_LITTLEENDIAN 1 -#endif - -/*--------------------------- Language extensions ----------------------------*/ - -/* Compile-time ("static") assertion */ -/* e.g. assert_static(sizeof(int) >= 4, int_type_too_small) */ -#define assert_static(e,f) enum {assert_static__##f = 1/(e)} -#define array_length(a) (sizeof(a)/sizeof(a[0])) -#define field_offset(type, field) ((size_t)&(((type *)0)->field)) -#define unless(x) if (!(x)) - -/*------------------------------- Maths stuff --------------------------------*/ - -#include - -#ifdef min -#undef min -#endif -#define min(a, b) ((a) <= (b) ? (a) : (b)) - -#ifdef max -#undef max -#endif -#define max(a, b) ((a) >= (b) ? (a) : (b)) - -#define range_limit(x, lower, upper) (min(max(x, lower), upper)) - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#endif -#ifndef M_LN10 -#define M_LN10 2.30258509299404568402 /* natural log of 10 */ -#endif -#ifndef M_SQRT2 -#define M_SQRT2 sqrt(2.) -#endif - -#define sqr(a) ((a) * (a)) -#define sign(x) ((x) < 0? -1 : 1) - -/* Numerical Recipes in C, p. 284 */ -#define ranqd1(x) ((x) = 1664525L * (x) + 1013904223L) /* int32_t x */ -#define dranqd1(x) (ranqd1(x) * (1. / (65536. * 32768.))) /* [-1,1) */ - -#define dB_to_linear(x) exp((x) * M_LN10 * 0.05) -#define linear_to_dB(x) (log10(x) * 20) - -extern int lsx_strcasecmp(const char *s1, const char *st); -extern int lsx_strncasecmp(char const *s1, char const *s2, size_t n); - -#ifndef HAVE_STRCASECMP -#define strcasecmp(s1, s2) lsx_strcasecmp((s1), (s2)) -#define strncasecmp(s1, s2, n) lsx_strncasecmp((s1), (s2), (n)) -#endif diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/xmalloc.c b/freedv/tags/1.2.2/freedv-dev/src/sox/xmalloc.c deleted file mode 100644 index 9bf15969..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/xmalloc.c +++ /dev/null @@ -1,43 +0,0 @@ -/* SoX Memory allocation functions - * - * Copyright (c) 2005-2006 Reuben Thomas. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include - -/* Resize an allocated memory area; abort if not possible. - * - * For malloc, `If the size of the space requested is zero, the behavior is - * implementation defined: either a null pointer is returned, or the - * behavior is as if the size were some nonzero value, except that the - * returned pointer shall not be used to access an object' - */ -void *lsx_realloc(void *ptr, size_t newsize) -{ - if (ptr && newsize == 0) { - free(ptr); - return NULL; - } - - if ((ptr = realloc(ptr, newsize)) == NULL) { - lsx_fail("out of memory"); - exit(2); - } - - return ptr; -} diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox/xmalloc.h b/freedv/tags/1.2.2/freedv-dev/src/sox/xmalloc.h deleted file mode 100644 index 9ee77f63..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox/xmalloc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* libSoX Memory allocation functions - * - * Copyright (c) 2005-2006 Reuben Thomas. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LSX_MALLOC_H -#define LSX_MALLOC_H - -#include -#include - -#define lsx_malloc(size) lsx_realloc(NULL, (size)) -#define lsx_calloc(n,s) (((n)*(s))? memset(lsx_malloc((n)*(s)),0,(n)*(s)) : NULL) -#define lsx_Calloc(v,n) v = lsx_calloc(n,sizeof(*(v))) -#define lsx_strdup(p) ((p)? strcpy((char *)lsx_malloc(strlen(p) + 1), p) : NULL) -#define lsx_memdup(p,s) ((p)? memcpy(lsx_malloc(s), p, s) : NULL) -#define lsx_valloc(v,n) v = lsx_malloc((n)*sizeof(*(v))) -#define lsx_revalloc(v,n) v = lsx_realloc(v, (n)*sizeof(*(v))) - -#endif diff --git a/freedv/tags/1.2.2/freedv-dev/src/sox_biquad.c b/freedv/tags/1.2.2/freedv-dev/src/sox_biquad.c deleted file mode 100644 index 548f4249..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/sox_biquad.c +++ /dev/null @@ -1,134 +0,0 @@ -//========================================================================== -// Name: sox_biquad.h -// Purpose: Interface into Sox Biquad filters -// Created: Dec 1, 2012 -// Authors: David Rowe -// -// To test: -/* - $ gcc sox_biquad.c sox/effects_i.c sox/effects.c sox/formats_i.c \ - sox/biquad.c sox/biquads.c sox/xmalloc.c sox/libsox.c \ - -o sox_biquad -DSOX_BIQUAD_UNITTEST -D__FREEDV__ \ - -Wall -lm -lsndfile -g - $ ./sox_biquad -*/ -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include -#include -#include -#include "sox/sox.h" - -#include "sox_biquad.h" - - -#define N_MAX 1024 - -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, - sox_sample_t *obuf, size_t *isamp, size_t *osamp); - -void sox_biquad_start(void) -{ - int r = sox_init(); - assert(r == SOX_SUCCESS); -} - -void sox_biquad_finish(void) -{ - sox_quit(); -} - -/* - Effect must be implemented by biquads.c in sox, arguments are just - like sox command line, for example: - - char *argv[10]; - argv[0] = "highpass"; argv[1]="1000"; argc=1; -*/ - -void *sox_biquad_create(int argc, const char *argv[]) -{ - int ret; - sox_effect_t *e; - int (*start)(sox_effect_t *); /* function pointer to effect start func */ - - e = sox_create_effect(sox_find_effect(argv[0])); assert(e != NULL); - ret = sox_effect_options(e, argc, (char * const*)&argv[1]); - assert(ret == SOX_SUCCESS); - - start = e->handler.start; - e->in_signal.rate = 8000; /* locked at FS=8000 Hz */ - ret = start(e); assert(ret == SOX_SUCCESS); - - return (void *)e; -} - -void sox_biquad_destroy(void *sbq) { - sox_effect_t *e = (sox_effect_t *)sbq; - free(e); -} - -void sox_biquad_filter(void *sbq, short out[], short in[], int n) -{ - sox_effect_t *e = (sox_effect_t *)sbq; - sox_sample_t ibuf[N_MAX]; - sox_sample_t obuf[N_MAX]; - size_t isamp, osamp; - unsigned int clips; - SOX_SAMPLE_LOCALS; - int i; - - assert(n <= N_MAX); - - clips = 0; - for(i=0; i. -// -//========================================================================== - -#ifndef __SOX_BIQUAD__ -#define __SOX_BIQUAD__ - -#ifdef __cplusplus -extern "C" { - -#endif - -void sox_biquad_start(void); -void sox_biquad_finish(void); -void *sox_biquad_create(int argc, const char *argv[]); -void sox_biquad_destroy(void *sbq); -void sox_biquad_filter(void *sbq, short out[], short in[], int n); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/freedv/tags/1.2.2/freedv-dev/src/topFrame.cpp b/freedv/tags/1.2.2/freedv-dev/src/topFrame.cpp deleted file mode 100644 index a5b574a1..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/topFrame.cpp +++ /dev/null @@ -1,595 +0,0 @@ -//========================================================================== -// Name: topFrame.cpp -// -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "topFrame.h" - -extern int g_playFileToMicInEventId; -extern int g_recFileFromRadioEventId; -extern int g_playFileFromRadioEventId; - -//========================================================================= -// Code that lays out the main application window -//========================================================================= -TopFrame::TopFrame(wxString plugInName, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) -{ - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); - //===================================================== - // Menubar Setup - m_menubarMain = new wxMenuBar(wxMB_DOCKABLE); - file = new wxMenu(); - - wxMenuItem* m_menuItemOnTop; - m_menuItemOnTop = new wxMenuItem(file, wxID_ANY, wxString(_("On Top")) , _("Always Top Window"), wxITEM_NORMAL); - file->Append(m_menuItemOnTop); - - wxMenuItem* m_menuItemExit; - m_menuItemExit = new wxMenuItem(file, ID_EXIT, wxString(_("E&xit")) , _("Exit Program"), wxITEM_NORMAL); - file->Append(m_menuItemExit); - - m_menubarMain->Append(file, _("&File")); - - tools = new wxMenu(); - wxMenuItem* m_menuItemAudio; - m_menuItemAudio = new wxMenuItem(tools, wxID_ANY, wxString(_("&Audio Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemAudio); - - wxMenuItem* m_menuItemRigCtrlCfg; - m_menuItemRigCtrlCfg = new wxMenuItem(tools, wxID_ANY, wxString(_("&PTT Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemRigCtrlCfg); - - wxMenuItem* m_menuItemOptions; - m_menuItemOptions = new wxMenuItem(tools, wxID_ANY, wxString(_("Options")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemOptions); - - wxMenuItem* m_menuItemFilter; - m_menuItemFilter = new wxMenuItem(tools, wxID_ANY, wxString(_("&Filter")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemFilter); - - wxMenuItem* m_menuItemPlugIn; - if (!wxIsEmpty(plugInName)) { - m_menuItemPlugIn = new wxMenuItem(tools, wxID_ANY, plugInName + wxString(_(" Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemPlugIn); - } - - wxMenuItem* m_menuItemPlayFileToMicIn; - m_menuItemPlayFileToMicIn = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Play File - Mic In")) , wxEmptyString, wxITEM_NORMAL); - g_playFileToMicInEventId = m_menuItemPlayFileToMicIn->GetId(); - tools->Append(m_menuItemPlayFileToMicIn); - - wxMenuItem* m_menuItemRecFileFromRadio; - m_menuItemRecFileFromRadio = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Record File - From Radio")) , wxEmptyString, wxITEM_NORMAL); - g_recFileFromRadioEventId = m_menuItemRecFileFromRadio->GetId(); - tools->Append(m_menuItemRecFileFromRadio); - - wxMenuItem* m_menuItemPlayFileFromRadio; - m_menuItemPlayFileFromRadio = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Play File - From Radio")) , wxEmptyString, wxITEM_NORMAL); - g_playFileFromRadioEventId = m_menuItemPlayFileFromRadio->GetId(); - tools->Append(m_menuItemPlayFileFromRadio); - m_menubarMain->Append(tools, _("&Tools")); - - help = new wxMenu(); - wxMenuItem* m_menuItemHelpUpdates; - m_menuItemHelpUpdates = new wxMenuItem(help, wxID_ANY, wxString(_("Check for Updates")) , wxEmptyString, wxITEM_NORMAL); - help->Append(m_menuItemHelpUpdates); - m_menuItemHelpUpdates->Enable(false); - - wxMenuItem* m_menuItemAbout; - m_menuItemAbout = new wxMenuItem(help, ID_ABOUT, wxString(_("&About")) , _("About this program"), wxITEM_NORMAL); - help->Append(m_menuItemAbout); - - m_menubarMain->Append(help, _("&Help")); - - this->SetMenuBar(m_menubarMain); - - wxBoxSizer* bSizer1; - bSizer1 = new wxBoxSizer(wxHORIZONTAL); - - //===================================================== - // Left side - //===================================================== - wxBoxSizer* leftSizer; - leftSizer = new wxBoxSizer(wxVERTICAL); - - wxStaticBoxSizer* snrSizer; - snrSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("SNR")), wxVERTICAL); - - //------------------------------ - // S/N ratio Guage (vert. bargraph) - //------------------------------ - m_gaugeSNR = new wxGauge(this, wxID_ANY, 25, wxDefaultPosition, wxSize(15,135), wxGA_SMOOTH|wxGA_VERTICAL); - m_gaugeSNR->SetToolTip(_("Displays signal to noise ratio in dB.")); - snrSizer->Add(m_gaugeSNR, 1, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); - - //------------------------------ - // Box for S/N ratio (Numeric) - //------------------------------ - m_textSNR = new wxStaticText(this, wxID_ANY, wxT(" 0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - snrSizer->Add(m_textSNR, 0, wxALIGN_CENTER_HORIZONTAL, 1); - - //------------------------------ - // S/N ratio slow Checkbox - //------------------------------ - m_ckboxSNR = new wxCheckBox(this, wxID_ANY, _("Slow"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - m_ckboxSNR->SetToolTip(_("Smooth but slow SNR estimation")); - snrSizer->Add(m_ckboxSNR, 0, wxALIGN_CENTER_HORIZONTAL, 5); - - leftSizer->Add(snrSizer, 2, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 1); - - //------------------------------ - // Sync Indicator box - //------------------------------ - wxStaticBoxSizer* sbSizer3_33; - sbSizer3_33 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Sync")), wxVERTICAL); - - m_rbSync = new wxRadioButton( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - m_rbSync->SetForegroundColour( wxColour( 255, 0, 0 ) ); - sbSizer3_33->Add(m_rbSync, 0, wxALIGN_CENTER|wxALL, 1); - leftSizer->Add(sbSizer3_33,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // BER Frames box - //------------------------------ - - wxStaticBoxSizer* sbSizer_ber; - sbSizer_ber = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Bit Error Rate")), wxVERTICAL); - - m_BtnBerReset = new wxButton(this, wxID_ANY, _("Reset"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_ber->Add(m_BtnBerReset, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - m_textBits = new wxStaticText(this, wxID_ANY, wxT("Bits: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textBits, 0, wxALIGN_LEFT, 1); - m_textErrors = new wxStaticText(this, wxID_ANY, wxT("Errs: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textErrors, 0, wxALIGN_LEFT, 1); - m_textBER = new wxStaticText(this, wxID_ANY, wxT("BER: 0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textBER, 0, wxALIGN_LEFT, 1); - - m_textResyncs = new wxStaticText(this, wxID_ANY, wxT("Resyncs: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textResyncs, 0, wxALIGN_LEFT, 1); - - leftSizer->Add(sbSizer_ber,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Signal Level(vert. bargraph) - //------------------------------ - wxStaticBoxSizer* levelSizer; - levelSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Level")), wxVERTICAL); - - m_textLevel = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(60,-1), wxALIGN_CENTRE); - m_textLevel->SetForegroundColour(wxColour(255,0,0)); - levelSizer->Add(m_textLevel, 0, wxALIGN_LEFT, 1); - - m_gaugeLevel = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(15,135), wxGA_SMOOTH|wxGA_VERTICAL); - m_gaugeLevel->SetToolTip(_("Peak of From Radio in Rx, or peak of From Mic in Tx mode. If Red you should reduce your levels")); - levelSizer->Add(m_gaugeLevel, 1, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); - - leftSizer->Add(levelSizer, 2, wxALIGN_CENTER|wxALL|wxEXPAND, 1); - - bSizer1->Add(leftSizer, 0, wxALL|wxEXPAND, 5); - - //===================================================== - // Center Section - //===================================================== - wxBoxSizer* centerSizer; - centerSizer = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* upperSizer; - upperSizer = new wxBoxSizer(wxVERTICAL); - - //===================================================== - // Tabbed Notebook control containing display graphs - //===================================================== - //m_auiNbookCtrl = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_NB_BOTTOM|wxAUI_NB_DEFAULT_STYLE); - //long style = wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_CLOSE_ON_ACTIVE_TAB | wxAUI_NB_MIDDLE_CLICK_CLOSE; - long nb_style = wxAUI_NB_BOTTOM | wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS; - m_auiNbookCtrl = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, nb_style); - // This line sets the fontsize for the tabs on the notebook control - m_auiNbookCtrl->SetFont(wxFont(8, 70, 90, 90, false, wxEmptyString)); - - upperSizer->Add(m_auiNbookCtrl, 1, wxALIGN_TOP|wxEXPAND, 1); - centerSizer->Add(upperSizer, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALIGN_TOP|wxEXPAND, 0); - - // lower middle used for user ID - - wxBoxSizer* lowerSizer; - lowerSizer = new wxBoxSizer(wxHORIZONTAL); - - m_BtnCallSignReset = new wxButton(this, wxID_ANY, _("Clear"), wxDefaultPosition, wxDefaultSize, 0); - lowerSizer->Add(m_BtnCallSignReset, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - wxBoxSizer* bSizer15; - bSizer15 = new wxBoxSizer(wxVERTICAL); - m_txtCtrlCallSign = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_txtCtrlCallSign->SetToolTip(_("Call Sign of transmitting station will appear here")); - bSizer15->Add(m_txtCtrlCallSign, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5); - lowerSizer->Add(bSizer15, 1, wxEXPAND, 5); - -#ifdef __EXPERIMENTAL_UDP__ - wxStaticBoxSizer* sbSizer_Checksum = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Checksums")), wxHORIZONTAL); - - wxStaticText *goodLabel = new wxStaticText(this, wxID_ANY, wxT("Good: "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - sbSizer_Checksum->Add(goodLabel, 0, 0, 2); - m_txtChecksumGood = new wxStaticText(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(30,-1), wxALIGN_CENTRE); - sbSizer_Checksum->Add(m_txtChecksumGood, 0, 0, 2); - - wxStaticText *badLabel = new wxStaticText(this, wxID_ANY, wxT("Bad: "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - sbSizer_Checksum->Add(badLabel, 0, 0, 1); - m_txtChecksumBad = new wxStaticText(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(30,-1), wxALIGN_CENTRE); - sbSizer_Checksum->Add(m_txtChecksumBad, 0, 0, 1); - - lowerSizer->Add(sbSizer_Checksum, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - //===================================================== - // These are the buttons that autosend the userid (?) - //===================================================== - - // DR 4 Dec - taken off for screen for Beta release to avoid questions on their use until - // we implement this feature - #ifdef UNIMPLEMENTED - wxBoxSizer* bSizer141; - bSizer141 = new wxBoxSizer(wxHORIZONTAL); - - // TxID - //--------- - m_togTxID = new wxToggleButton(this, wxID_ANY, _("TxID"), wxDefaultPosition, wxDefaultSize, 0); - m_togTxID->SetToolTip(_("Send Tx ID information")); - bSizer141->Add(m_togTxID, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); - - // RxID - //--------- - m_togRxID = new wxToggleButton(this, wxID_ANY, _("RxID"), wxDefaultPosition, wxDefaultSize, 0); - m_togRxID->SetToolTip(_("Enable reception of ID information")); - bSizer141->Add(m_togRxID, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxALL|wxFIXED_MINSIZE, 5); - - lowerSizer->Add(bSizer141, 0, wxALIGN_RIGHT, 5); -#endif - - centerSizer->Add(lowerSizer, 0, wxALIGN_BOTTOM|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 2); - bSizer1->Add(centerSizer, 4, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 1); - - //===================================================== - // Right side - //===================================================== - wxBoxSizer* rightSizer; - rightSizer = new wxBoxSizer(wxVERTICAL); - - //===================================================== - // Squelch Slider Control - //===================================================== - wxStaticBoxSizer* sbSizer3; - sbSizer3 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Squelch")), wxVERTICAL); - - m_sliderSQ = new wxSlider(this, wxID_ANY, 0, 0, 40, wxDefaultPosition, wxSize(-1,80), wxSL_AUTOTICKS|wxSL_INVERSE|wxSL_VERTICAL); - m_sliderSQ->SetToolTip(_("Set Squelch level in dB.")); - - sbSizer3->Add(m_sliderSQ, 1, wxALIGN_CENTER_HORIZONTAL, 0); - - //------------------------------ - // Squelch Level static text box - //------------------------------ - m_textSQ = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - - sbSizer3->Add(m_textSQ, 0, wxALIGN_CENTER_HORIZONTAL, 0); - - //------------------------------ - // Squelch Toggle Checkbox - //------------------------------ - m_ckboxSQ = new wxCheckBox(this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - - sbSizer3->Add(m_ckboxSQ, 0, wxALIGN_CENTER_HORIZONTAL, 0); - rightSizer->Add(sbSizer3, 2, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 0); - - //rightSizer->Add(sbSizer3_33,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - /* new --- */ - - //------------------------------ - // Mode box - //------------------------------ - wxStaticBoxSizer* sbSizer_mode; - sbSizer_mode = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Mode")), wxVERTICAL); - -#ifdef DISABLED_FEATURE - m_rb1400old = new wxRadioButton( this, wxID_ANY, wxT("1400 V0.91"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb1400old, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1400 = new wxRadioButton( this, wxID_ANY, wxT("1400"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1400, 0, wxALIGN_LEFT|wxALL, 1); - m_rb700 = new wxRadioButton( this, wxID_ANY, wxT("700"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700, 0, wxALIGN_LEFT|wxALL, 1); - m_rb700b = new wxRadioButton( this, wxID_ANY, wxT("700B"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700b, 0, wxALIGN_LEFT|wxALL, 1); -#endif - m_rb700c = new wxRadioButton( this, wxID_ANY, wxT("700C"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700c, 0, wxALIGN_LEFT|wxALL, 1); - m_rb800xa = new wxRadioButton( this, wxID_ANY, wxT("800XA"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb800xa, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1600 = new wxRadioButton( this, wxID_ANY, wxT("1600"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1600, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1600->SetValue(true); - - m_rbPlugIn = NULL; - if (!wxIsEmpty(plugInName)) { - // Optional plug in - - m_rbPlugIn = new wxRadioButton( this, wxID_ANY, plugInName, wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rbPlugIn, 0, wxALIGN_LEFT|wxALL, 1); - } - -#ifdef DISABLED_FEATURE - m_rb1600Wide = new wxRadioButton( this, wxID_ANY, wxT("1600 Wide"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1600Wide, 0, wxALIGN_LEFT|wxALL, 1); - m_rb2000 = new wxRadioButton( this, wxID_ANY, wxT("2000"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb2000, 0, wxALIGN_LEFT|wxALL, 1); -#endif - - rightSizer->Add(sbSizer_mode,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - #ifdef MOVED_TO_OPTIONS_DIALOG - /* new --- */ - - //------------------------------ - // Test Frames box - //------------------------------ - - wxStaticBoxSizer* sbSizer_testFrames; - sbSizer_testFrames = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Test Frames")), wxVERTICAL); - - m_ckboxTestFrame = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxTestFrame, 0, wxALIGN_LEFT, 0); - - rightSizer->Add(sbSizer_testFrames,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - #endif - - //===================================================== - // Control Toggles box - //===================================================== - wxStaticBoxSizer* sbSizer5; - sbSizer5 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Control")), wxVERTICAL); - wxBoxSizer* bSizer1511; - bSizer1511 = new wxBoxSizer(wxVERTICAL); - - //------------------------------- - // Stop/Stop signal processing (rx and tx) - //------------------------------- - m_togBtnOnOff = new wxToggleButton(this, wxID_ANY, _("Start"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnOnOff->SetToolTip(_("Begin/End receiving data.")); - bSizer1511->Add(m_togBtnOnOff, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer1511, 0, wxEXPAND, 1); - -#ifdef UNIMPLEMENTED - //------------------------------ - // Toggle Loopback button for RX - //------------------------------ - wxBoxSizer* bSizer15113; - bSizer15113 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* bSizer15111; - bSizer15111 = new wxBoxSizer(wxVERTICAL); - wxSize wxSz = wxSize(44, 30); - m_togBtnLoopRx = new wxToggleButton(this, wxID_ANY, _("Loop\nRX"), wxDefaultPosition, wxSz, 0); - m_togBtnLoopRx->SetFont(wxFont(6, 70, 90, 90, false, wxEmptyString)); - m_togBtnLoopRx->SetToolTip(_("Loopback Receive audio data.")); - - bSizer15111->Add(m_togBtnLoopRx, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - //sbSizer5->Add(bSizer15111, 0, wxEXPAND, 1); - bSizer15113->Add(bSizer15111, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - //------------------------------ - // Toggle Loopback button for Tx - //------------------------------ - wxBoxSizer* bSizer15112; - bSizer15112 = new wxBoxSizer(wxVERTICAL); - m_togBtnLoopTx = new wxToggleButton(this, wxID_ANY, _("Loop\nTX"), wxDefaultPosition, wxSz, 0); - m_togBtnLoopTx->SetFont(wxFont(6, 70, 90, 90, false, wxEmptyString)); - m_togBtnLoopTx->SetToolTip(_("Loopback Transmit audio data.")); - - bSizer15112->Add(m_togBtnLoopTx, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - bSizer15113->Add(bSizer15112, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - sbSizer5->Add(bSizer15113, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - //------------------------------ - // Split Frequency Mode Toggle - //------------------------------ - wxBoxSizer* bSizer151; - bSizer151 = new wxBoxSizer(wxVERTICAL); - - m_togBtnSplit = new wxToggleButton(this, wxID_ANY, _("Split"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnSplit->SetToolTip(_("Toggle split frequency mode.")); - - bSizer151->Add(m_togBtnSplit, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 1); - wxBoxSizer* bSizer13; - bSizer13 = new wxBoxSizer(wxVERTICAL); - - //------------------------------ - // Analog Passthrough Toggle - //------------------------------ - m_togBtnAnalog = new wxToggleButton(this, wxID_ANY, _("Analog"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnAnalog->SetToolTip(_("Toggle analog/digital operation.")); - bSizer13->Add(m_togBtnAnalog, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer13, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - //------------------------------ - // Voice Keyer Toggle - //------------------------------ - m_togBtnVoiceKeyer = new wxToggleButton(this, wxID_ANY, _("Voice Keyer"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnVoiceKeyer->SetToolTip(_("Toggle Voice Keyer")); - wxBoxSizer* bSizer13a = new wxBoxSizer(wxVERTICAL); - bSizer13a->Add(m_togBtnVoiceKeyer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer13a, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - // not implemented on fdmdv2 -#ifdef ALC - //------------------------------ - // Toggle for ALC - //------------------------------ - wxBoxSizer* bSizer14; - bSizer14 = new wxBoxSizer(wxVERTICAL); - m_togBtnALC = new wxToggleButton(this, wxID_ANY, _("ALC"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnALC->SetToolTip(_("Toggle automatic level control mode.")); - - bSizer14->Add(m_togBtnALC, 0, wxALL, 1); - sbSizer5->Add(bSizer14, 0, wxALIGN_CENTER|wxALIGN_CENTER_HORIZONTAL|wxALL, 1); -#endif - - //------------------------------ - // PTT button: Toggle Transmit/Receive mode - //------------------------------ - wxBoxSizer* bSizer11; - bSizer11 = new wxBoxSizer(wxVERTICAL); - m_btnTogPTT = new wxToggleButton(this, wxID_ANY, _("PTT"), wxDefaultPosition, wxDefaultSize, 0); - m_btnTogPTT->SetToolTip(_("Push to Talk - Switch between Receive and Transmit - you can also use the space bar ")); - bSizer11->Add(m_btnTogPTT, 1, wxALIGN_CENTER|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer11, 2, wxEXPAND, 1); - rightSizer->Add(sbSizer5, 2, wxALIGN_CENTER|wxALL|wxEXPAND, 3); - bSizer1->Add(rightSizer, 0, wxALL|wxEXPAND, 3); - this->SetSizer(bSizer1); - this->Layout(); - m_statusBar1 = this->CreateStatusBar(3, wxST_SIZEGRIP, wxID_ANY); - - //===================================================== - // End of layout - //===================================================== - - //------------------- - // Connect Events - //------------------- - this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TopFrame::topFrame_OnClose)); - this->Connect(wxEVT_PAINT, wxPaintEventHandler(TopFrame::topFrame_OnPaint)); - this->Connect(wxEVT_SIZE, wxSizeEventHandler(TopFrame::topFrame_OnSize)); - this->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::topFrame_OnUpdateUI)); - - this->Connect(m_menuItemExit->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnExit)); - this->Connect(m_menuItemOnTop->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnTop)); - - this->Connect(m_menuItemAudio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsAudio)); - this->Connect(m_menuItemAudio->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsAudioUI)); - this->Connect(m_menuItemFilter->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsFilter)); - this->Connect(m_menuItemFilter->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsFilterUI)); - this->Connect(m_menuItemRigCtrlCfg->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsComCfg)); - this->Connect(m_menuItemRigCtrlCfg->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsComCfgUI)); - this->Connect(m_menuItemOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsOptions)); - this->Connect(m_menuItemOptions->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsOptionsUI)); - - if (!wxIsEmpty(plugInName)) { - this->Connect(m_menuItemPlugIn->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsPlugInCfg)); - this->Connect(m_menuItemPlugIn->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsPlugInCfgUI)); - } - - this->Connect(m_menuItemPlayFileToMicIn->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); - this->Connect(m_menuItemRecFileFromRadio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); - this->Connect(m_menuItemPlayFileFromRadio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileFromRadio)); - - this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); - this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - this->Connect(m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpAbout)); - //m_togRxID->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnRxID), NULL, this); - //m_togTxID->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnTxID), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnSliderScrollBottom), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScrollChanged), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnSliderScrollTop), NULL, this); - m_ckboxSQ->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSQClick), NULL, this); - - m_ckboxSNR->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSNRClick), NULL, this); - - m_togBtnOnOff->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnOnOff), NULL, this); - m_togBtnSplit->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnSplitClick), NULL, this); - m_togBtnAnalog->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnAnalogClick), NULL, this); - m_togBtnVoiceKeyer->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnVoiceKeyerClick), NULL, this); -#ifdef ALC - m_togBtnALC->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnALCClick), NULL, this); -#endif - m_btnTogPTT->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnPTT), NULL, this); - - m_BtnCallSignReset->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnCallSignReset), NULL, this); - m_BtnBerReset->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnBerReset), NULL, this); -} - -TopFrame::~TopFrame() -{ - //------------------- - // Disconnect Events - //------------------- - this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TopFrame::topFrame_OnClose)); - this->Disconnect(wxEVT_PAINT, wxPaintEventHandler(TopFrame::topFrame_OnPaint)); - this->Disconnect(wxEVT_SIZE, wxSizeEventHandler(TopFrame::topFrame_OnSize)); - this->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::topFrame_OnUpdateUI)); - this->Disconnect(ID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnExit)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsAudio)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsAudioUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsFilter)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsFilterUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsComCfg)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsComCfgUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsOptions)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsOptionsUI)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsPlugInCfg)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileFromRadio)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - this->Disconnect(ID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpAbout)); - //m_togRxID->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnRxID), NULL, this); - //m_togTxID->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnTxID), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnSliderScrollBottom), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScrollChanged), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnSliderScrollTop), NULL, this); - m_ckboxSQ->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSQClick), NULL, this); - - m_togBtnOnOff->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnOnOff), NULL, this); - m_togBtnSplit->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnSplitClick), NULL, this); - m_togBtnAnalog->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnAnalogClick), NULL, this); - m_togBtnVoiceKeyer->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnVoiceKeyerClick), NULL, this); -#ifdef ALC - m_togBtnALC->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnALCClick), NULL, this); -#endif - m_btnTogPTT->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnPTT), NULL, this); - -} - diff --git a/freedv/tags/1.2.2/freedv-dev/src/topFrame.h b/freedv/tags/1.2.2/freedv-dev/src/topFrame.h deleted file mode 100644 index 37950a99..00000000 --- a/freedv/tags/1.2.2/freedv-dev/src/topFrame.h +++ /dev/null @@ -1,194 +0,0 @@ -//========================================================================== -// Name: topFrame.h -// -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// 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 . -// -//========================================================================== -#ifndef __TOPFRAME_H__ -#define __TOPFRAME_H__ - -#include "version.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/////////////////////////////////////////////////////////////////////////// - -#define ID_OPEN 1000 -#define ID_SAVE 1001 -#define ID_CLOSE 1002 -#define ID_EXIT 1003 -#define ID_COPY 1004 -#define ID_CUT 1005 -#define ID_PASTE 1006 -#define ID_OPTIONS 1007 -#define ID_ABOUT 1008 - -/////////////////////////////////////////////////////////////////////////////// -/// Class TopFrame -/////////////////////////////////////////////////////////////////////////////// -class TopFrame : public wxFrame -{ - private: - - protected: - wxMenuBar* m_menubarMain; - wxMenu* file; - wxMenu* edit; - wxMenu* tools; - wxMenu* help; - wxGauge* m_gaugeSNR; - wxStaticText* m_textSNR; - wxCheckBox* m_ckboxSNR; - wxGauge* m_gaugeLevel; - wxStaticText* m_textLevel; - - wxButton* m_BtnCallSignReset; - wxTextCtrl* m_txtCtrlCallSign; - wxStaticText* m_txtChecksumGood; - wxStaticText* m_txtChecksumBad; - - wxSlider* m_sliderSQ; - wxCheckBox* m_ckboxSQ; - wxStaticText* m_textSQ; - wxStatusBar* m_statusBar1; - - wxButton* m_BtnBerReset; - wxStaticText *m_textBits; - wxStaticText *m_textErrors; - wxStaticText *m_textBER; - wxStaticText *m_textResyncs; - - wxRadioButton *m_rbSync; - wxRadioButton *m_rb1400old; - wxRadioButton *m_rb1400; - wxRadioButton *m_rb700; - wxRadioButton *m_rb700b; - wxRadioButton *m_rb700c; - wxRadioButton *m_rb800xa; - wxRadioButton *m_rb1600; - wxRadioButton *m_rb2000; - wxRadioButton *m_rb1600Wide; - wxRadioButton *m_rbPlugIn; - - // Virtual event handlers, overide them in your derived class - virtual void topFrame_OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void topFrame_OnPaint( wxPaintEvent& event ) { event.Skip(); } - virtual void topFrame_OnSize( wxSizeEvent& event ) { event.Skip(); } - virtual void topFrame_OnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnExit( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTop( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsAudio( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsAudioUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsFilter( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsFilterUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsOptions( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnToolsPlugInCfg( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsPlugInCfgUI( wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnToolsUDP( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsOptionsUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsComCfg( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsComCfgUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnPlayFileToMicIn( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRecFileFromRadio( wxCommandEvent& event ) { event.Skip(); } - virtual void OnPlayFileFromRadio( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnHelpCheckUpdates( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnHelpAbout( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnRxID( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnTxID( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCmdSliderScroll( wxScrollEvent& event ) { event.Skip(); } - virtual void OnSliderScrollBottom( wxScrollEvent& event ) { event.Skip(); } - virtual void OnCmdSliderScrollChanged( wxScrollEvent& event ) { event.Skip(); } - virtual void OnSliderScrollTop( wxScrollEvent& event ) { event.Skip(); } - virtual void OnCheckSQClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCheckSNRClick( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnTogBtnLoopRx( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnLoopTx( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnOnOff( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnSplitClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnAnalogClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnVoiceKeyerClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnALCClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnPTT( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnTogBtnSplitClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnAnalogClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnALCClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnRxIDUI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnTxIDUI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnPTT_UI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnOnOffUI(wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnCallSignReset( wxCommandEvent& event ) { event.Skip(); } - virtual void OnBerReset( wxCommandEvent& event ) { event.Skip(); } - - public: - wxToggleButton* m_togRxID; - wxToggleButton* m_togTxID; - wxToggleButton* m_togBtnOnOff; - wxToggleButton* m_togBtnSplit; - wxToggleButton* m_togBtnAnalog; - wxToggleButton* m_togBtnVoiceKeyer; - wxToggleButton* m_togBtnALC; - wxToggleButton* m_btnTogPTT; - wxToggleButton* m_togBtnLoopRx; - wxToggleButton* m_togBtnLoopTx; - wxAuiNotebook* m_auiNbookCtrl; - - TopFrame( wxString plugInName, wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeDV ") + _(FREEDV_VERSION), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(561,300 ), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL ); - - ~TopFrame(); -}; - -#endif //__TOPFRAME_H__ diff --git a/freedv/tags/1.2.2/script/spot.sh b/freedv/tags/1.2.2/script/spot.sh deleted file mode 100644 index cb1309a2..00000000 --- a/freedv/tags/1.2.2/script/spot.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# -# spot.sh -# David Rowe Sep 2015 -# - -# Demo script for "spotting" based on FreeDV txt string. Posts a -# date-stamped text file to a web server. Called from FreeDV GUI -# program when a callsign is received in the txt msg. - - -# Q: how to remove repeated spots, or those close in time? -# -# Set up automated lftp login: -# -# $ lftp ftp://username@server -# Password: -# lftp username@server:~> set bmk:save-passwords true -# lftp username@server:~> bookmark add yourserver -# lftp username@server:~> bookmark list -# lftp username@server:~> quit - -SPOTFILE=/home/david/tmp/freedvspot.html -FTPSERVER=ftp.rowetel.com - -echo `date -u` " " $1 "
" >> $SPOTFILE -tail -n 25 $SPOTFILE > /tmp/spot.tmp1 -mv /tmp/spot.tmp1 $SPOTFILE -lftp -e "cd www;put $SPOTFILE;quit" $FTPSERVER diff --git a/freedv/tags/1.2.2/src/CMakeLists.txt b/freedv/tags/1.2.2/src/CMakeLists.txt deleted file mode 100644 index ef0798b3..00000000 --- a/freedv/tags/1.2.2/src/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -set(FREEDV_SOURCES - dlg_audiooptions.cpp - dlg_filter.cpp - dlg_options.cpp - dlg_ptt.cpp - dlg_plugin.cpp - fdmdv2_main.cpp - fdmdv2_pa_wrapper.cpp - fdmdv2_plot.cpp - fdmdv2_plot_scalar.cpp - fdmdv2_plot_scatter.cpp - fdmdv2_plot_spectrum.cpp - fdmdv2_plot_waterfall.cpp - hamlib.cpp - serialport.cpp - topFrame.cpp - sox_biquad.c - comp.h - dlg_audiooptions.h - dlg_filter.h - dlg_options.h - dlg_ptt.h - fdmdv2_defines.h - fdmdv2_main.h - fdmdv2_pa_wrapper.h - fdmdv2_plot.h - fdmdv2_plot_scalar.h - fdmdv2_plot_scatter.h - fdmdv2_plot_spectrum.h - fdmdv2_plot_waterfall.h - hamlib.h - sox_biquad.h - sox/band.h - sox/biquad.c - sox/biquads.c - sox/biquad.h - sox/effects.c - sox/effects.h - sox/effects_i.c - sox/formats_i.c - sox/libsox.c - sox/sox.h - sox/sox_i.h - sox/soxomp.h - sox/util.h - sox/xmalloc.h - sox/xmalloc.c - topFrame.h - version.h -) - -# WIN32 is needed for Windows GUI apps and is ignored for UNIX like systems. -add_executable(freedv WIN32 ${FREEDV_SOURCES} ${RES_FILES}) -target_link_libraries(freedv ${FREEDV_LINK_LIBS}) -include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) -if(FREEDV_STATIC_DEPS) - add_dependencies(freedv ${FREEDV_STATIC_DEPS}) -endif(FREEDV_STATIC_DEPS) -install(TARGETS freedv - RUNTIME DESTINATION bin) - -# Custom commands to build OSX images. -if(APPLE) - add_custom_command( - TARGET freedv - POST_BUILD - COMMAND mkdir ARGS -p FreeDV.app/Contents/MacOS - COMMAND mkdir ARGS -p FreeDV.app/Contents/Resources/English.lproj - COMMAND cp ARGS ${CMAKE_CURRENT_SOURCE_DIR}/info.plist FreeDV.app/Contents - COMMAND cp ARGS ${CMAKE_CURRENT_SOURCE_DIR}/freedv.icns FreeDV.app/Contents/Resources - COMMAND echo ARGS -n "APPL????" > FreeDV.app/Contents/PkgInfo - COMMAND cp ARGS freedv FreeDV.app/Contents/MacOS/FreeDV - COMMAND dylibbundler ARGS -od -b -x FreeDV.app/Contents/MacOS/FreeDV -d FreeDV.app/Contents/libs -p @executable_path/../libs/ - COMMAND mkdir dist_tmp - COMMAND cp -r FreeDV.app dist_tmp - COMMAND hdiutil create -srcfolder dist_tmp/ -volname FreeDV -format UDZO ./FreeDV.dmg - COMMAND rm -rf dist_tmp - ) -endif(APPLE) diff --git a/freedv/tags/1.2.2/src/Makefile.win32 b/freedv/tags/1.2.2/src/Makefile.win32 deleted file mode 100644 index 932e8518..00000000 --- a/freedv/tags/1.2.2/src/Makefile.win32 +++ /dev/null @@ -1,52 +0,0 @@ -# src/Makefile.win32 -# David Rowe 26 Oct 2012 -# -# Makefile for Win32 on msys/Mingw to help David R get up to speed -# -# $ make -f Makefile.Win32 - -CODEC2_PATH=$(HOME)/codec2-dev -INCLUDE_PATH=/usr/local/include - -WX_CONFIG=wx-config -WX_CPPFLAGS = $(shell $(WX_CONFIG) --cxxflags) -D__WXDEBUG__ -WX_LIBS = $(shell $(WX_CONFIG) --libs core, base, aui, adv, net) -SVN_REVISION=$(shell svnversion) -CODEC2_INC=$(CODEC2_PATH)/src -CODEC2_LIB=$(CODEC2_PATH)/build_win32/src/ - -CPP_FLAGS = -D_NO_AUTOTOOLS_ -I$(INCLUDE_PATH) $(WX_CPPFLAGS) -I$(CODEC2_INC) -I../extern/include -I. -g -Wall -DSVN_REVISION=\"$(SVN_REVISION)\" -LIBS = $(WX_LIBS) -L$(CODEC2_LIB) -lcodec2 -lm -lportaudiocpp -lportaudio -lpthread -lsndfile -lsamplerate -lhamlib -lsox -lspeexdsp - -OBJS = topFrame.o \ -fdmdv2_main.o \ -fdmdv2_plot.o \ -fdmdv2_plot_scalar.o \ -fdmdv2_plot_scatter.o \ -fdmdv2_plot_spectrum.o \ -fdmdv2_plot_waterfall.o \ -fdmdv2_pa_wrapper.o \ -dlg_audiooptions.o \ -dlg_ptt.o \ -dlg_options.o \ -dlg_filter.o \ -sox_biquad.o \ -hamlib.o \ -../../codec2-dev/src/golay23.o - -HDRS = version.h dlg_audiooptions.h dlg_ptt.h dlg_filter.h fdmdv2_main.h fdmdv2_defines.h fdmdv2_plot.h fdmdv2_plot_scalar.h fdmdv2_plot_waterfall.h fdmdv2_plot_scatter.h fdmdv2_plot_spectrum.h fdmdv2_pa_wrapper.h topFrame.h dlg_audiooptions.h topFrame.h varicode.h ../../codec2-dev/src/golay23.h hamlib.h - -all: freedv - -freedv: $(OBJS) - g++ -o freedv $(OBJS) $(CPP_FLAGS) $(LIBS) - -%.o: %.cpp $(HDRS) Makefile.win32 - g++ $(CPP_FLAGS) -c $< -o $@ - -%.o: %.c $(HDRS) Makefile.win32 - gcc $(CPP_FLAGS) -c $< -o $@ - -clean: - rm -f *.o fdmdv2 - diff --git a/freedv/tags/1.2.2/src/afreedvplugin.c b/freedv/tags/1.2.2/src/afreedvplugin.c deleted file mode 100644 index 5fdc424e..00000000 --- a/freedv/tags/1.2.2/src/afreedvplugin.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - afreedvplugin.c - David Rowe Feb 2016 - - Sample FreeDV plugin - - TODO: - [ ] plugin to call back to functions - [ ] ability to list .so's/DLLs and scan - [ ] where do we put plugins? - [ ] Windows build and test environment - - linux .so: - $ gcc -Wall -fPIC -c afreedvplugin.c - $ gcc -shared -Wl,-soname,afreedvplugin.so -o afreedvplugin.so afreedvplugin.o - win32 .dll: - $ i686-w64-mingw32-gcc -c afreedvplugin.c - $ i686-w64-mingw32-gcc -shared -o afreedvplugin.dll afreedvplugin.o -Wl,--out-implib,afreedvplugin_dll.a - -*/ - -#include -#include -#include - -#ifdef _WIN32_ -#define DLL __declspec(dllexport) -#else -#define DLL -#endif - - -#ifdef LATER -/* functions plugin can call - not sure how to link to these */ - -int plugin_alert(char string[]); -int plugin_get_persistant(char name[], char value[]); -int plugin_set_persistant(char name[], char value[]); -#endif -static int (*plugin_get_persistant)(char name[], char value[]); - -struct APLUGIN_STATES { - int symbol_rate; - int num_tones; - int counter; -}; - -/* plugin functions called by host, we need to write these */ - -void DLL plugin_name(char name[]) { - - sprintf(name, "aFreeDVplugIn"); -} - -/* - Text fields will be created for nparams, using the names - in *param_names[]. These fields we be saved to persistent - storage as name/param_names[0], name/param_names[1] .... -*/ - -void DLL *plugin_open(char *param_names[], - int *nparams, - int (*aplugin_get_persistant)(char *, char *)) -{ - struct APLUGIN_STATES *states; - - /* set up function ptrs */ - - plugin_get_persistant = aplugin_get_persistant; - - /* tell host how many persistent parameters we have and their names */ - - strcpy(param_names[0], "SymbolRate"); - strcpy(param_names[1], "NumTones"); - *nparams = 2; - - /* init local states */ - - states = (struct APLUGIN_STATES *)malloc(sizeof(struct APLUGIN_STATES)); - if (states == NULL) { - // TODO: plugin_alert("Problem starting plugin!"); - return NULL; - } - states->counter = 0; - - return (void*)states; -} - -void DLL plugin_close(void *states) { - free(states); -} - -void DLL plugin_start(void *s) { - struct APLUGIN_STATES *states = (struct APLUGIN_STATES*)s; - char txt[80]; - - fprintf(stderr, "\nplugin_start\n"); - - (plugin_get_persistant)("SymbolRate",txt); - states->symbol_rate = atoi(txt); - - (plugin_get_persistant)("NumTones",txt); - states->num_tones = atoi(txt); - - fprintf(stderr, "symbol_rate: %d num_tones: %d\n", states->symbol_rate, states->num_tones); -} - -void DLL plugin_stop(void *states) { - fprintf(stderr, "\nplugin_stop\n"); -} - -void DLL plugin_rx_samples(void *s, short samples[], int n) { - struct APLUGIN_STATES *states = (struct APLUGIN_STATES*)s; - //fprintf(stderr, "Got n=%d samples!\n", n); - //fprintf(stderr, "samples[0] = %d samples[%d-1] = %d counter = %d\n", samples[0], n, samples[n-1], states->counter++); -} - diff --git a/freedv/tags/1.2.2/src/comp.h b/freedv/tags/1.2.2/src/comp.h deleted file mode 100644 index a3a1bd9b..00000000 --- a/freedv/tags/1.2.2/src/comp.h +++ /dev/null @@ -1,39 +0,0 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: comp.h - AUTHOR......: David Rowe - DATE CREATED: 24/08/09 - - Complex number definition. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2009 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 . -*/ - -#ifndef __COMP__ -#define __COMP__ - -/* Complex number */ - -typedef struct -{ - float real; - float imag; -} COMP; - -#endif diff --git a/freedv/tags/1.2.2/src/dlg_audiooptions.cpp b/freedv/tags/1.2.2/src/dlg_audiooptions.cpp deleted file mode 100644 index 4e8cd981..00000000 --- a/freedv/tags/1.2.2/src/dlg_audiooptions.cpp +++ /dev/null @@ -1,1264 +0,0 @@ -//========================================================================= -// Name: AudioOptsDialog.cpp -// Purpose: Implements an Audio options selection dialog. -// -// Authors: David Rowe, David Witten -// License: -// -// All rights reserved. -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================= -#include "fdmdv2_main.h" -#include "dlg_audiooptions.h" - -// constants for test waveform plots - -#define TEST_WAVEFORM_X 180 -#define TEST_WAVEFORM_Y 180 -#define TEST_WAVEFORM_PLOT_TIME 2.0 -#define TEST_WAVEFORM_PLOT_FS 400 -#define TEST_BUF_SIZE 1024 -#define TEST_FS 48000.0 -#define TEST_DT 0.1 // time between plot updates in seconds -#define TEST_WAVEFORM_PLOT_BUF ((int)(DT*400)) - -void AudioOptsDialog::Pa_Init(void) -{ - m_isPaInitialized = false; - - if((pa_err = Pa_Initialize()) == paNoError) - { - m_isPaInitialized = true; - } - else - { - wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK); - return; - } -} - - -void AudioOptsDialog::buildTestControls(PlotScalar **plotScalar, wxButton **btnTest, - wxPanel *parentPanel, wxBoxSizer *bSizer, wxString buttonLabel) -{ - wxBoxSizer* bSizer1 = new wxBoxSizer(wxVERTICAL); - - wxPanel *panel = new wxPanel(parentPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); - *plotScalar = new PlotScalar((wxFrame*) panel, 1, TEST_WAVEFORM_PLOT_TIME, 1.0/TEST_WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "", 1); - (*plotScalar)->SetClientSize(wxSize(TEST_WAVEFORM_X,TEST_WAVEFORM_Y)); - bSizer1->Add(panel, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 8); - - *btnTest = new wxButton(parentPanel, wxID_ANY, buttonLabel, wxDefaultPosition, wxDefaultSize); - bSizer1->Add(*btnTest, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 0); - - bSizer->Add(bSizer1, 0, wxALIGN_CENTER_HORIZONTAL |wxALIGN_CENTER_VERTICAL ); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// AudioOptsDialog() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -AudioOptsDialog::AudioOptsDialog(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - //this->SetSizeHints(wxSize(850, 600), wxDefaultSize); - fprintf(stderr, "pos %d %d\n", pos.x, pos.y); - Pa_Init(); - - wxBoxSizer* mainSizer; - mainSizer = new wxBoxSizer(wxVERTICAL); - m_panel1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer4; - bSizer4 = new wxBoxSizer(wxVERTICAL); - m_notebook1 = new wxNotebook(m_panel1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM); - m_panelRx = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer20; - bSizer20 = new wxBoxSizer(wxVERTICAL); - wxGridSizer* gSizer4; - gSizer4 = new wxGridSizer(2, 1, 0, 0); - - // Rx In ----------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer2; - sbSizer2 = new wxStaticBoxSizer(new wxStaticBox(m_panelRx, wxID_ANY, _("From Radio")), wxHORIZONTAL); - - wxBoxSizer* bSizer811a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlRxInDevices = new wxListCtrl(m_panelRx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer811a->Add(m_listCtrlRxInDevices, 1, wxALL|wxEXPAND, 1); - - wxBoxSizer* bSizer811; - bSizer811 = new wxBoxSizer(wxHORIZONTAL); - m_staticText51 = new wxStaticText(m_panelRx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText51->Wrap(-1); - bSizer811->Add(m_staticText51, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_textCtrlRxIn = new wxTextCtrl(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer811->Add(m_textCtrlRxIn, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText6 = new wxStaticText(m_panelRx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText6->Wrap(-1); - bSizer811->Add(m_staticText6, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateRxIn = new wxComboBox(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer811->Add(m_cbSampleRateRxIn, 0, wxALIGN_CENTER_VERTICAL|wxALL, 1); - - bSizer811a->Add(bSizer811, 0, wxEXPAND, 5); - - sbSizer2->Add(bSizer811a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarRxIn, &m_btnRxInTest, m_panelRx, sbSizer2, _("Rec 2s")); - - gSizer4->Add(sbSizer2, 1, wxEXPAND, 5); - - // Rx Out ----------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer3; - sbSizer3 = new wxStaticBoxSizer(new wxStaticBox(m_panelRx, wxID_ANY, _("To Speaker/Headphones")), wxHORIZONTAL); - - wxBoxSizer* bSizer81a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlRxOutDevices = new wxListCtrl(m_panelRx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer81a->Add(m_listCtrlRxOutDevices, 1, wxALL|wxEXPAND, 1); - - wxBoxSizer* bSizer81; - bSizer81 = new wxBoxSizer(wxHORIZONTAL); - m_staticText9 = new wxStaticText(m_panelRx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText9->Wrap(-1); - bSizer81->Add(m_staticText9, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_textCtrlRxOut = new wxTextCtrl(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer81->Add(m_textCtrlRxOut, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText10 = new wxStaticText(m_panelRx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText10->Wrap(-1); - bSizer81->Add(m_staticText10, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateRxOut = new wxComboBox(m_panelRx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer81->Add(m_cbSampleRateRxOut, 0, wxALIGN_CENTER_VERTICAL|wxALL, 1); - - bSizer81a->Add(bSizer81, 0, wxEXPAND, 5); - - sbSizer3->Add(bSizer81a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarRxOut, &m_btnRxOutTest, m_panelRx, sbSizer3, _("Play 2s")); - - gSizer4->Add(sbSizer3, 1, wxEXPAND, 2); - bSizer20->Add(gSizer4, 1, wxEXPAND, 1); - m_panelRx->SetSizer(bSizer20); - m_panelRx->Layout(); - bSizer20->Fit(m_panelRx); - m_notebook1->AddPage(m_panelRx, _("Receive"), true); - - // Tx Tab ------------------------------------------------------------------------------- - - m_panelTx = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer18; - bSizer18 = new wxBoxSizer(wxVERTICAL); - wxGridSizer* gSizer2; - gSizer2 = new wxGridSizer(2, 1, 0, 0); - - // Tx In ---------------------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer22; - sbSizer22 = new wxStaticBoxSizer(new wxStaticBox(m_panelTx, wxID_ANY, _("From Microphone")), wxHORIZONTAL); - - wxBoxSizer* bSizer83a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlTxInDevices = new wxListCtrl(m_panelTx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer83a->Add(m_listCtrlTxInDevices, 1, wxALL|wxEXPAND, 1); - wxBoxSizer* bSizer83; - bSizer83 = new wxBoxSizer(wxHORIZONTAL); - m_staticText12 = new wxStaticText(m_panelTx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText12->Wrap(-1); - bSizer83->Add(m_staticText12, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_textCtrlTxIn = new wxTextCtrl(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer83->Add(m_textCtrlTxIn, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText11 = new wxStaticText(m_panelTx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText11->Wrap(-1); - bSizer83->Add(m_staticText11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateTxIn = new wxComboBox(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer83->Add(m_cbSampleRateTxIn, 0, wxALL, 1); - - bSizer83a->Add(bSizer83, 0, wxEXPAND, 5); - - sbSizer22->Add(bSizer83a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarTxIn, &m_btnTxInTest, m_panelTx, sbSizer22, _("Rec 2s")); - - gSizer2->Add(sbSizer22, 1, wxEXPAND, 5); - - // Tx Out ---------------------------------------------------------------------------------- - - wxStaticBoxSizer* sbSizer21; - sbSizer21 = new wxStaticBoxSizer(new wxStaticBox(m_panelTx, wxID_ANY, _("To Radio")), wxHORIZONTAL); - - wxBoxSizer* bSizer82a = new wxBoxSizer(wxVERTICAL); - - m_listCtrlTxOutDevices = new wxListCtrl(m_panelTx, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_VRULES); - bSizer82a->Add(m_listCtrlTxOutDevices, 1, wxALL|wxEXPAND, 2); - wxBoxSizer* bSizer82; - bSizer82 = new wxBoxSizer(wxHORIZONTAL); - m_staticText81 = new wxStaticText(m_panelTx, wxID_ANY, _("Device:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText81->Wrap(-1); - bSizer82->Add(m_staticText81, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_textCtrlTxOut = new wxTextCtrl(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - bSizer82->Add(m_textCtrlTxOut, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); - m_staticText71 = new wxStaticText(m_panelTx, wxID_ANY, _("Sample Rate:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText71->Wrap(-1); - bSizer82->Add(m_staticText71, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); - m_cbSampleRateTxOut = new wxComboBox(m_panelTx, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(90,-1), 0, NULL, wxCB_DROPDOWN); - bSizer82->Add(m_cbSampleRateTxOut, 0, wxALL, 1); - - bSizer82a->Add(bSizer82, 0, wxEXPAND, 5); - - sbSizer21->Add(bSizer82a, 1, wxEXPAND, 2); - buildTestControls(&m_plotScalarTxOut, &m_btnTxOutTest, m_panelTx, sbSizer21, _("Play 2s")); - - gSizer2->Add(sbSizer21, 1, wxEXPAND, 5); - bSizer18->Add(gSizer2, 1, wxEXPAND, 1); - m_panelTx->SetSizer(bSizer18); - m_panelTx->Layout(); - bSizer18->Fit(m_panelTx); - m_notebook1->AddPage(m_panelTx, _("Transmit"), false); - - // API Tab ------------------------------------------------------------------- - - m_panelAPI = new wxPanel(m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* bSizer12; - bSizer12 = new wxBoxSizer(wxHORIZONTAL); - wxGridSizer* gSizer31; - gSizer31 = new wxGridSizer(2, 1, 0, 0); - wxStaticBoxSizer* sbSizer1; - sbSizer1 = new wxStaticBoxSizer(new wxStaticBox(m_panelAPI, wxID_ANY, _("PortAudio")), wxVERTICAL); - - wxGridSizer* gSizer3; - gSizer3 = new wxGridSizer(4, 2, 0, 0); - - m_staticText7 = new wxStaticText(m_panelAPI, wxID_ANY, _("PortAudio Version String:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText7->Wrap(-1); - gSizer3->Add(m_staticText7, 1, wxALIGN_RIGHT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - m_textStringVer = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - gSizer3->Add(m_textStringVer, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText8 = new wxStaticText(m_panelAPI, wxID_ANY, _("PortAudio Int Version:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText8->Wrap(-1); - gSizer3->Add(m_staticText8, 1, wxALIGN_RIGHT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - m_textIntVer = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - gSizer3->Add(m_textIntVer, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText5 = new wxStaticText(m_panelAPI, wxID_ANY, _("Device Count:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText5->Wrap(-1); - gSizer3->Add(m_staticText5, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 10); - m_textCDevCount = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - gSizer3->Add(m_textCDevCount, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - m_staticText4 = new wxStaticText(m_panelAPI, wxID_ANY, _("API Count:"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText4->Wrap(-1); - gSizer3->Add(m_staticText4, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 10); - m_textAPICount = new wxStaticText(m_panelAPI, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(45,-1), 0); - m_textAPICount->SetMaxSize(wxSize(45,-1)); - gSizer3->Add(m_textAPICount, 1, wxALIGN_LEFT|wxALL|wxALIGN_CENTER_VERTICAL, 10); - - sbSizer1->Add(gSizer3, 1, wxEXPAND, 2); - gSizer31->Add(sbSizer1, 1, wxEXPAND, 2); - wxStaticBoxSizer* sbSizer6; - sbSizer6 = new wxStaticBoxSizer(new wxStaticBox(m_panelAPI, wxID_ANY, _("Other")), wxVERTICAL); - gSizer31->Add(sbSizer6, 1, wxEXPAND, 5); - bSizer12->Add(gSizer31, 1, wxEXPAND, 5); - m_panelAPI->SetSizer(bSizer12); - m_panelAPI->Layout(); - bSizer12->Fit(m_panelAPI); - m_notebook1->AddPage(m_panelAPI, _("API Info"), false); - bSizer4->Add(m_notebook1, 1, wxEXPAND | wxALL, 0); - m_panel1->SetSizer(bSizer4); - m_panel1->Layout(); - bSizer4->Fit(m_panel1); - mainSizer->Add(m_panel1, 1, wxEXPAND | wxALL, 1); - - wxBoxSizer* bSizer6; - bSizer6 = new wxBoxSizer(wxHORIZONTAL); - m_btnRefresh = new wxButton(this, wxID_ANY, _("Refresh"), wxDefaultPosition, wxDefaultSize, 0); - bSizer6->Add(m_btnRefresh, 0, wxALIGN_CENTER|wxALL, 2); - - m_sdbSizer1 = new wxStdDialogButtonSizer(); - - m_sdbSizer1OK = new wxButton(this, wxID_OK); - m_sdbSizer1->AddButton(m_sdbSizer1OK); - - m_sdbSizer1Cancel = new wxButton(this, wxID_CANCEL); - m_sdbSizer1->AddButton(m_sdbSizer1Cancel); - - m_sdbSizer1Apply = new wxButton(this, wxID_APPLY); - m_sdbSizer1->AddButton(m_sdbSizer1Apply); - - m_sdbSizer1->Realize(); - - bSizer6->Add(m_sdbSizer1, 1, wxALIGN_CENTER_VERTICAL, 2); - mainSizer->Add(bSizer6, 0, wxEXPAND, 2); - this->SetSizer(mainSizer); - this->Layout(); - this->Centre(wxBOTH); -// this->Centre(wxBOTH); - - m_notebook1->SetSelection(0); - - showAPIInfo(); - m_RxInDevices.m_listDevices = m_listCtrlRxInDevices; - m_RxInDevices.direction = AUDIO_IN; - m_RxInDevices.m_textDevice = m_textCtrlRxIn; - m_RxInDevices.m_cbSampleRate = m_cbSampleRateRxIn; - - m_RxOutDevices.m_listDevices = m_listCtrlRxOutDevices; - m_RxOutDevices.direction = AUDIO_OUT; - m_RxOutDevices.m_textDevice = m_textCtrlRxOut; - m_RxOutDevices.m_cbSampleRate = m_cbSampleRateRxOut; - - m_TxInDevices.m_listDevices = m_listCtrlTxInDevices; - m_TxInDevices.direction = AUDIO_IN; - m_TxInDevices.m_textDevice = m_textCtrlTxIn; - m_TxInDevices.m_cbSampleRate = m_cbSampleRateTxIn; - - m_TxOutDevices.m_listDevices = m_listCtrlTxOutDevices; - m_TxOutDevices.direction = AUDIO_OUT; - m_TxOutDevices.m_textDevice = m_textCtrlTxOut; - m_TxOutDevices.m_cbSampleRate = m_cbSampleRateTxOut; - - populateParams(m_RxInDevices); - populateParams(m_RxOutDevices); - populateParams(m_TxInDevices); - populateParams(m_TxOutDevices); - - m_listCtrlRxInDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnRxInDeviceSelect ), NULL, this ); - m_listCtrlRxOutDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnRxOutDeviceSelect ), NULL, this ); - m_listCtrlTxInDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnTxInDeviceSelect ), NULL, this ); - m_listCtrlTxOutDevices->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( AudioOptsDialog::OnTxOutDeviceSelect ), NULL, this ); - - // wire up test buttons - m_btnRxInTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxInTest ), NULL, this ); - m_btnRxOutTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxOutTest ), NULL, this ); - m_btnTxInTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxInTest ), NULL, this ); - m_btnTxOutTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxOutTest ), NULL, this ); - - m_btnRefresh->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRefreshClick ), NULL, this ); - m_sdbSizer1Apply->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnApplyAudioParameters ), NULL, this ); - m_sdbSizer1Cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnCancelAudioParameters ), NULL, this ); - m_sdbSizer1OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnOkAudioParameters ), NULL, this ); -/* - void OnClose( wxCloseEvent& event ) { event.Skip(); } - void OnHibernate( wxActivateEvent& event ) { event.Skip(); } - void OnIconize( wxIconizeEvent& event ) { event.Skip(); } - void OnInitDialog( wxInitDialogEvent& event ) { event.Skip(); } -*/ -// this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(AudioOptsDialog::OnClose)); - this->Connect(wxEVT_HIBERNATE, wxActivateEventHandler(AudioOptsDialog::OnHibernate)); - this->Connect(wxEVT_ICONIZE, wxIconizeEventHandler(AudioOptsDialog::OnIconize)); - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(AudioOptsDialog::OnInitDialog)); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// ~AudioOptsDialog() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -AudioOptsDialog::~AudioOptsDialog() -{ - Pa_Terminate(); - - // Disconnect Events - this->Disconnect(wxEVT_HIBERNATE, wxActivateEventHandler(AudioOptsDialog::OnHibernate)); - this->Disconnect(wxEVT_ICONIZE, wxIconizeEventHandler(AudioOptsDialog::OnIconize)); - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(AudioOptsDialog::OnInitDialog)); - - m_listCtrlRxInDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnRxInDeviceSelect), NULL, this); - m_listCtrlRxOutDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnRxOutDeviceSelect), NULL, this); - m_listCtrlTxInDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnTxInDeviceSelect), NULL, this); - m_listCtrlTxOutDevices->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(AudioOptsDialog::OnTxOutDeviceSelect), NULL, this); - - m_btnRxInTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxInTest ), NULL, this ); - m_btnRxOutTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnRxOutTest ), NULL, this ); - m_btnTxInTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxInTest ), NULL, this ); - m_btnTxOutTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AudioOptsDialog::OnTxOutTest ), NULL, this ); - - m_btnRefresh->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnRefreshClick), NULL, this); - m_sdbSizer1Apply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnApplyAudioParameters), NULL, this); - m_sdbSizer1Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnCancelAudioParameters), NULL, this); - m_sdbSizer1OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AudioOptsDialog::OnOkAudioParameters), NULL, this); - -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnInitDialog( wxInitDialogEvent& event ) -{ - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -// -// helper function to look up name of devNum, and if it exists write -// name to textCtrl. Used to trap dissapearing devices. -//------------------------------------------------------------------------- -int AudioOptsDialog::setTextCtrlIfDevNumValid(wxTextCtrl *textCtrl, wxListCtrl *listCtrl, int devNum) -{ - int i, aDevNum, found_devNum; - - // ignore last list entry as it is the "none" entry - - found_devNum = 0; - for(i=0; iGetItemCount()-1; i++) { - aDevNum = wxAtoi(listCtrl->GetItemText(i, 1)); - //printf("aDevNum: %d devNum: %d\n", aDevNum, devNum); - if (aDevNum == devNum) { - found_devNum = 1; - textCtrl->SetValue(listCtrl->GetItemText(i, 0) + " (" + wxString::Format(wxT("%i"),devNum) + ")"); - printf("setting focus of %d\n", i); - listCtrl->SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED); - } - } - - if (found_devNum) - return devNum; - else { - textCtrl->SetValue("none"); - return -1; - } -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -int AudioOptsDialog::ExchangeData(int inout) -{ - if(inout == EXCHANGE_DATA_IN) - { - // Map sound card device numbers to tx/rx device numbers depending - // on number of sound cards in use - - printf("EXCHANGE_DATA_IN:\n"); - printf(" g_nSoundCards: %d\n", g_nSoundCards); - printf(" g_soundCard1InDeviceNum: %d\n", g_soundCard1InDeviceNum); - printf(" g_soundCard1OutDeviceNum: %d\n", g_soundCard1OutDeviceNum); - printf(" g_soundCard1SampleRate: %d\n", g_soundCard1SampleRate); - printf(" g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - printf(" g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - printf(" g_soundCard2SampleRate: %d\n", g_soundCard2SampleRate); - - if (g_nSoundCards == 0) { - m_textCtrlRxIn ->SetValue("none"); rxInAudioDeviceNum = -1; - m_textCtrlRxOut->SetValue("none"); rxOutAudioDeviceNum = -1; - m_textCtrlTxIn ->SetValue("none"); txInAudioDeviceNum = -1; - m_textCtrlTxOut->SetValue("none"); txOutAudioDeviceNum = -1; - } - - if (g_nSoundCards == 1) { - rxInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxIn, - m_listCtrlRxInDevices, - g_soundCard1InDeviceNum); - - rxOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxOut, - m_listCtrlRxOutDevices, - g_soundCard1OutDeviceNum); - - if ((rxInAudioDeviceNum != -1) && (rxInAudioDeviceNum != -1)) { - m_cbSampleRateRxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - m_cbSampleRateRxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - } - - m_textCtrlTxIn ->SetValue("none"); txInAudioDeviceNum = -1; - m_textCtrlTxOut->SetValue("none"); txOutAudioDeviceNum = -1; - } - - if (g_nSoundCards == 2) { - - rxInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxIn, - m_listCtrlRxInDevices, - g_soundCard1InDeviceNum); - - rxOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlRxOut, - m_listCtrlRxOutDevices, - g_soundCard2OutDeviceNum); - - txInAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlTxIn, - m_listCtrlTxInDevices, - g_soundCard2InDeviceNum); - - txOutAudioDeviceNum = setTextCtrlIfDevNumValid(m_textCtrlTxOut, - m_listCtrlTxOutDevices, - g_soundCard1OutDeviceNum); - - if ((rxInAudioDeviceNum != -1) && (txOutAudioDeviceNum != -1)) { - m_cbSampleRateRxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - m_cbSampleRateTxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard1SampleRate)); - } - - if ((txInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1)) { - m_cbSampleRateTxIn->SetValue(wxString::Format(wxT("%i"),g_soundCard2SampleRate)); - m_cbSampleRateRxOut->SetValue(wxString::Format(wxT("%i"),g_soundCard2SampleRate)); - } - } - printf(" rxInAudioDeviceNum: %d\n rxOutAudioDeviceNum: %d\n txInAudioDeviceNum: %d\n txOutAudioDeviceNum: %d\n", - rxInAudioDeviceNum, rxOutAudioDeviceNum, txInAudioDeviceNum, txOutAudioDeviceNum); - } - - if(inout == EXCHANGE_DATA_OUT) - { - int valid_one_card_config = 0; - int valid_two_card_config = 0; - wxString sampleRate1, sampleRate2; - - printf("EXCHANGE_DATA_OUT:\n"); - printf(" rxInAudioDeviceNum: %d\n rxOutAudioDeviceNum: %d\n txInAudioDeviceNum: %d\n txOutAudioDeviceNum: %d\n", - rxInAudioDeviceNum, rxOutAudioDeviceNum, txInAudioDeviceNum, txOutAudioDeviceNum); - - // --------------------------------------------------------------- - // check we have a valid 1 or 2 sound card configuration - // --------------------------------------------------------------- - - // one sound card config, tx device numbers should be set to -1 - - if ((rxInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1) && - (txInAudioDeviceNum == -1) && (txOutAudioDeviceNum == -1)) { - - valid_one_card_config = 1; - - // in and out sample rate must be the same, as there is one callback - - sampleRate1 = m_cbSampleRateRxIn->GetValue(); - if (!sampleRate1.IsSameAs(m_cbSampleRateRxOut->GetValue())) { - wxMessageBox(wxT("With a single sound card the Sample Rate of " - "From Radio and To Speaker/Headphones must be the same."), wxT(""), wxOK); - return -1; - } - } - - // two card configuration - - if ((rxInAudioDeviceNum != -1) && (rxOutAudioDeviceNum != -1) && - (txInAudioDeviceNum != -1) && (txOutAudioDeviceNum != -1)) { - - valid_two_card_config = 1; - - // Check we haven't doubled up on sound devices - - if (rxInAudioDeviceNum == txInAudioDeviceNum) { - wxMessageBox(wxT("You must use different devices for From Radio and From Microphone"), wxT(""), wxOK); - return -1; - } - - if (rxOutAudioDeviceNum == txOutAudioDeviceNum) { - wxMessageBox(wxT("You must use different devices for To Radio and To Speaker/Headphones"), wxT(""), wxOK); - return -1; - } - - // Check sample rates for callback 1 devices are the same, - // as input and output are handled synchronously by one - // portaudio callback - - sampleRate1 = m_cbSampleRateRxIn->GetValue(); - if (!sampleRate1.IsSameAs(m_cbSampleRateTxOut->GetValue())) { - wxMessageBox(wxT("With two sound cards the Sample Rate " - "of From Radio and To Radio must be the same."), wxT(""), wxOK); - return -1; - } - - // check sample rate for callback 2 devices is the same - - sampleRate2 = m_cbSampleRateTxIn->GetValue(); - if (!sampleRate2.IsSameAs(m_cbSampleRateRxOut->GetValue())) { - wxMessageBox(wxT("With two sound cards the Sample Rate of " - "From Microphone and To Speaker/Headphones must be the same."), wxT(""), wxOK); - return -1; - } - - } - - printf(" valid_one_card_config: %d valid_two_card_config: %d\n", valid_one_card_config, valid_two_card_config); - - if (!valid_one_card_config && !valid_two_card_config) { - wxMessageBox(wxT("Invalid one or two sound card configuration"), wxT(""), wxOK); - return -1; - } - - // --------------------------------------------------------------- - // Map Rx/TX device numbers to sound card device numbers used - // in callbacks. Portaudio uses one callback per sound card so - // we have to be soundcard oriented at run time rather than - // Tx/Rx oriented as in this dialog. - // --------------------------------------------------------------- - g_nSoundCards = 0; - g_soundCard1InDeviceNum = g_soundCard1OutDeviceNum = g_soundCard2InDeviceNum = g_soundCard2OutDeviceNum = -1; - - if (valid_one_card_config) { - - // Only callback 1 used - - g_nSoundCards = 1; - g_soundCard1InDeviceNum = rxInAudioDeviceNum; - g_soundCard1OutDeviceNum = rxOutAudioDeviceNum; - g_soundCard1SampleRate = wxAtoi(sampleRate1); - } - - if (valid_two_card_config) { - g_nSoundCards = 2; - g_soundCard1InDeviceNum = rxInAudioDeviceNum; - g_soundCard1OutDeviceNum = txOutAudioDeviceNum; - g_soundCard1SampleRate = wxAtoi(sampleRate1); - g_soundCard2InDeviceNum = txInAudioDeviceNum; - g_soundCard2OutDeviceNum = rxOutAudioDeviceNum; - g_soundCard2SampleRate = wxAtoi(sampleRate2); - } - - printf(" g_nSoundCards: %d\n", g_nSoundCards); - printf(" g_soundCard1InDeviceNum: %d\n", g_soundCard1InDeviceNum); - printf(" g_soundCard1OutDeviceNum: %d\n", g_soundCard1OutDeviceNum); - printf(" g_soundCard1SampleRate: %d\n", g_soundCard1SampleRate); - printf(" g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - printf(" g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - printf(" g_soundCard2SampleRate: %d\n", g_soundCard2SampleRate); - - wxConfigBase *pConfig = wxConfigBase::Get(); - pConfig->Write(wxT("/Audio/soundCard1InDeviceNum"), g_soundCard1InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1OutDeviceNum"), g_soundCard1OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1SampleRate"), g_soundCard1SampleRate ); - - pConfig->Write(wxT("/Audio/soundCard2InDeviceNum"), g_soundCard2InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2OutDeviceNum"), g_soundCard2OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2SampleRate"), g_soundCard2SampleRate ); - - pConfig->Flush(); - delete wxConfigBase::Set((wxConfigBase *) NULL); - } - - return 0; -} - -//------------------------------------------------------------------------- -// buildListOfSupportedSampleRates() -//------------------------------------------------------------------------- -int AudioOptsDialog:: buildListOfSupportedSampleRates(wxComboBox *cbSampleRate, int devNum, int in_out) -{ - // every sound device has a different list of supported sample rates, so - // we work out which ones are supported and populate the list ctrl - - static double standardSampleRates[] = - { - 8000.0, 9600.0, - 11025.0, 12000.0, - 16000.0, 22050.0, - 24000.0, 32000.0, - 44100.0, 48000.0, - 88200.0, 96000.0, - 192000.0, -1 // negative terminated list - }; - - const PaDeviceInfo *deviceInfo; - PaStreamParameters inputParameters, outputParameters; - PaError err; - wxString str; - int i, numSampleRates; - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - printf("Pa_GetDeviceInfo(%d) failed!\n", devNum); - cbSampleRate->Clear(); - return 0; - } - - inputParameters.device = devNum; - inputParameters.channelCount = deviceInfo->maxInputChannels; - inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = 0; - inputParameters.hostApiSpecificStreamInfo = NULL; - - outputParameters.device = devNum; - outputParameters.channelCount = deviceInfo->maxOutputChannels; - outputParameters.sampleFormat = paInt16; - outputParameters.suggestedLatency = 0; - outputParameters.hostApiSpecificStreamInfo = NULL; - - cbSampleRate->Clear(); - //printf("devNum %d supports: ", devNum); - numSampleRates = 0; - for(i = 0; standardSampleRates[i] > 0; i++) - { - if (in_out == AUDIO_IN) - err = Pa_IsFormatSupported(&inputParameters, NULL, standardSampleRates[i]); - else - err = Pa_IsFormatSupported(NULL, &outputParameters, standardSampleRates[i]); - - if( err == paFormatIsSupported ) { - str.Printf("%i", (int)standardSampleRates[i]); - cbSampleRate->AppendString(str); - printf("%i ", (int)standardSampleRates[i]); - numSampleRates++; - } - } - printf("\n"); - - return numSampleRates; -} - -//------------------------------------------------------------------------- -// showAPIInfo() -//------------------------------------------------------------------------- -void AudioOptsDialog::showAPIInfo() -{ - wxString strval; - int apiVersion; - int apiCount = 0; - int numDevices = 0; - - strval = Pa_GetVersionText(); - m_textStringVer->SetLabel(strval); - - apiVersion = Pa_GetVersion(); - strval.Printf(wxT("%d"), apiVersion); - m_textIntVer->SetLabel(strval); - - apiCount = Pa_GetHostApiCount(); - strval.Printf(wxT("%d"), apiCount); - m_textAPICount->SetLabel(strval); - - numDevices = Pa_GetDeviceCount(); - strval.Printf(wxT("%d"), numDevices); - m_textCDevCount->SetLabel(strval); -} - -//------------------------------------------------------------------------- -// populateParams() -//------------------------------------------------------------------------- -void AudioOptsDialog::populateParams(AudioInfoDisplay ai) -{ - const PaDeviceInfo *deviceInfo = NULL; - wxListCtrl* ctrl = ai.m_listDevices; - int in_out = ai.direction; - long idx; - int numDevices; - wxListItem listItem; - wxString buf; - int devn; - int col = 0; - - numDevices = Pa_GetDeviceCount(); - - if(ctrl->GetColumnCount() > 0) - { - ctrl->ClearAll(); - } - - listItem.SetAlign(wxLIST_FORMAT_LEFT); - listItem.SetText(wxT("Device")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 300); - - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("ID")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 45); - - listItem.SetAlign(wxLIST_FORMAT_LEFT); - listItem.SetText(wxT("API")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - - if(in_out == AUDIO_IN) - { - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Default Sample Rate")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 160); - } - else if(in_out == AUDIO_OUT) - { - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Default Sample Rate")); - idx = ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 160); - } - - #ifdef LATENCY - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Min Latency")); - ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - - listItem.SetAlign(wxLIST_FORMAT_CENTRE); - listItem.SetText(wxT("Max Latency")); - ctrl->InsertColumn(col, listItem); - ctrl->SetColumnWidth(col++, 100); - #endif - - for(devn = 0; devn < numDevices; devn++) - { - buf.Printf(wxT("")); - deviceInfo = Pa_GetDeviceInfo(devn); - if( ((in_out == AUDIO_IN) && (deviceInfo->maxInputChannels > 0)) || - ((in_out == AUDIO_OUT) && (deviceInfo->maxOutputChannels > 0))) - { - col = 0; - buf.Printf(wxT("%s"), deviceInfo->name); - idx = ctrl->InsertItem(ctrl->GetItemCount(), buf); - col++; - - buf.Printf(wxT("%d"), devn); - ctrl->SetItem(idx, col++, buf); - - buf.Printf(wxT("%s"), Pa_GetHostApiInfo(deviceInfo->hostApi)->name); - ctrl->SetItem(idx, col++, buf); - - buf.Printf(wxT("%i"), (int)deviceInfo->defaultSampleRate); - ctrl->SetItem(idx, col++, buf); - - #ifdef LATENCY - if (in_out == AUDIO_IN) - buf.Printf(wxT("%8.4f"), deviceInfo->defaultLowInputLatency); - else - buf.Printf(wxT("%8.4f"), deviceInfo->defaultLowOutputLatency); - ctrl->SetItem(idx, col++, buf); - - if (in_out == AUDIO_IN) - buf.Printf(wxT("%8.4f"), deviceInfo->defaultHighInputLatency); - else - buf.Printf(wxT("%8.4f"), deviceInfo->defaultHighOutputLatency); - ctrl->SetItem(idx, col++, buf); - #endif - } - } - - // add "none" option at end - - buf.Printf(wxT("%s"), "none"); - idx = ctrl->InsertItem(ctrl->GetItemCount(), buf); -} - -//------------------------------------------------------------------------- -// OnDeviceSelect() -// -// helper function to set up "Device:" and "Sample Rate:" fields when -// we click on a line in the list of devices box -//------------------------------------------------------------------------- -void AudioOptsDialog::OnDeviceSelect(wxComboBox *cbSampleRate, - wxTextCtrl *textCtrl, - int *devNum, - wxListCtrl *listCtrlDevices, - int index, - int in_out) -{ - - wxString devName = listCtrlDevices->GetItemText(index, 0); - if (devName.IsSameAs("none")) { - *devNum = -1; - textCtrl->SetValue("none"); - } - else { - *devNum = wxAtoi(listCtrlDevices->GetItemText(index, 1)); - textCtrl->SetValue(devName + " (" + wxString::Format(wxT("%i"),*devNum) + ")"); - - int numSampleRates = buildListOfSupportedSampleRates(cbSampleRate, *devNum, in_out); - if (numSampleRates) { - wxString defSampleRate = listCtrlDevices->GetItemText(index, 3); - cbSampleRate->SetValue(defSampleRate); - } - else { - cbSampleRate->SetValue("None"); - } - } -} - -//------------------------------------------------------------------------- -// OnRxInDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxInDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateRxIn, - m_textCtrlRxIn, - &rxInAudioDeviceNum, - m_listCtrlRxInDevices, - evt.GetIndex(), - AUDIO_IN); -} - -//------------------------------------------------------------------------- -// OnRxOutDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxOutDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateRxOut, - m_textCtrlRxOut, - &rxOutAudioDeviceNum, - m_listCtrlRxOutDevices, - evt.GetIndex(), - AUDIO_OUT); -} - -//------------------------------------------------------------------------- -// OnTxInDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxInDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateTxIn, - m_textCtrlTxIn, - &txInAudioDeviceNum, - m_listCtrlTxInDevices, - evt.GetIndex(), - AUDIO_IN); -} - -//------------------------------------------------------------------------- -// OnTxOutDeviceSelect() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxOutDeviceSelect(wxListEvent& evt) -{ - OnDeviceSelect(m_cbSampleRateTxOut, - m_textCtrlTxOut, - &txOutAudioDeviceNum, - m_listCtrlTxOutDevices, - evt.GetIndex(), - AUDIO_OUT); -} - -//------------------------------------------------------------------------- -// plotDeviceInputForAFewSecs() -// -// opens a record device and plots the input speech for a few seconds. This is "modal" using -// synchronous portaudio functions, so the GUI will not respond until after test sample has been -// taken -//------------------------------------------------------------------------- -void AudioOptsDialog::plotDeviceInputForAFewSecs(int devNum, PlotScalar *plotScalar) { - PaStreamParameters inputParameters; - const PaDeviceInfo *deviceInfo = NULL; - PaStream *stream = NULL; - PaError err; - short in48k_stereo_short[2*TEST_BUF_SIZE]; - short in48k_short[TEST_BUF_SIZE]; - short in8k_short[TEST_BUF_SIZE]; - int numDevices, nBufs, i, j, src_error,inputChannels; - float t; - SRC_STATE *src; - FIFO *fifo; - - // a basic sanity check - numDevices = Pa_GetDeviceCount(); - if (devNum >= numDevices) - return; - if (devNum < 0) - return; - printf("devNum %d\n", devNum); - - fifo = fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL); - src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL); - - // work out how many input channels this device supports. - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card "), wxT("Error"), wxOK); - return; - } - if (deviceInfo->maxInputChannels == 1) - inputChannels = 1; - else - inputChannels = 2; - - // open device - - inputParameters.device = devNum; - inputParameters.channelCount = inputChannels; - inputParameters.sampleFormat = paInt16; - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - nBufs = TEST_WAVEFORM_PLOT_TIME*TEST_FS/TEST_BUF_SIZE; - printf("inputChannels: %d nBufs %d\n", inputChannels, nBufs); - - err = Pa_OpenStream( - &stream, - &inputParameters, - NULL, - TEST_FS, - TEST_BUF_SIZE, - paClipOff, - NULL, // no callback, use blocking API - NULL ); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't initialise sound device."), wxT("Error"), wxOK); - return; - } - - err = Pa_StartStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't start sound device."), wxT("Error"), wxOK); - return; - } - - for(i=0, t=0.0; i TEST_DT) { - t -= TEST_DT; - short plotSamples[TEST_WAVEFORM_PLOT_BUF]; - if (fifo_read(fifo, plotSamples, TEST_WAVEFORM_PLOT_BUF)) - memset(plotSamples, 0, TEST_WAVEFORM_PLOT_BUF*sizeof(short)); - plotScalar->add_new_short_samples(0, plotSamples, TEST_WAVEFORM_PLOT_BUF, 32767); - plotScalar->Refresh(); - } - } - - err = Pa_StopStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't stop sound device."), wxT("Error"), wxOK); - return; - } - Pa_CloseStream(stream); - - fifo_destroy(fifo); - src_delete(src); -} - -//------------------------------------------------------------------------- -// plotDeviceOutputForAFewSecs() -// -// opens a play device and plays a tone for a few seconds. This is "modal" using -// synchronous portaudio functions, so the GUI will not respond until after test sample has been -// taken. Also plots a pretty picture like the record versions -//------------------------------------------------------------------------- -void AudioOptsDialog::plotDeviceOutputForAFewSecs(int devNum, PlotScalar *plotScalar) { - PaStreamParameters outputParameters; - const PaDeviceInfo *deviceInfo = NULL; - PaStream *stream = NULL; - PaError err; - short out48k_stereo_short[2*TEST_BUF_SIZE]; - short out48k_short[TEST_BUF_SIZE]; - short out8k_short[TEST_BUF_SIZE]; - int numDevices, nBufs, i, j, src_error, n, outputChannels; - float t; - SRC_STATE *src; - FIFO *fifo; - - // a basic sanity check - numDevices = Pa_GetDeviceCount(); - if (devNum >= numDevices) - return; - if (devNum < 0) - return; - - fifo = fifo_create((int)(DT*TEST_WAVEFORM_PLOT_FS*2)); assert(fifo != NULL); - src = src_new(SRC_SINC_FASTEST, 1, &src_error); assert(src != NULL); - - // work out how many output channels this device supports. - - deviceInfo = Pa_GetDeviceInfo(devNum); - if (deviceInfo == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card "), wxT("Error"), wxOK); - return; - } - if (deviceInfo->maxOutputChannels == 1) - outputChannels = 1; - else - outputChannels = 2; - - printf("outputChannels: %d\n", outputChannels); - - outputParameters.device = devNum; - outputParameters.channelCount = outputChannels; - outputParameters.sampleFormat = paInt16; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - nBufs = TEST_WAVEFORM_PLOT_TIME*TEST_FS/TEST_BUF_SIZE; - - err = Pa_OpenStream( - &stream, - NULL, - &outputParameters, - TEST_FS, - TEST_BUF_SIZE, - paClipOff, - NULL, // no callback, use blocking API - NULL ); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't initialise sound device."), wxT("Error"), wxOK); - return; - } - - err = Pa_StartStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't start sound device."), wxT("Error"), wxOK); - return; - } - - for(i=0, t=0.0, n=0; i TEST_DT) { - t -= TEST_DT; - short plotSamples[TEST_WAVEFORM_PLOT_BUF]; - if (fifo_read(fifo, plotSamples, TEST_WAVEFORM_PLOT_BUF)) - memset(plotSamples, 0, TEST_WAVEFORM_PLOT_BUF*sizeof(short)); - plotScalar->add_new_short_samples(0, plotSamples, TEST_WAVEFORM_PLOT_BUF, 32767); - plotScalar->Refresh(); - } - } - - err = Pa_StopStream(stream); - if (err != paNoError) { - wxMessageBox(wxT("Couldn't stop sound device."), wxT("Error"), wxOK); - return; - } - Pa_CloseStream(stream); - - fifo_destroy(fifo); - src_delete(src); -} - -//------------------------------------------------------------------------- -// OnRxInTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxInTest(wxCommandEvent& event) -{ - plotDeviceInputForAFewSecs(rxInAudioDeviceNum, m_plotScalarRxIn); -} - -//------------------------------------------------------------------------- -// OnRxOutTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRxOutTest(wxCommandEvent& event) -{ - plotDeviceOutputForAFewSecs(rxOutAudioDeviceNum, m_plotScalarRxOut); -} - -//------------------------------------------------------------------------- -// OnTxInTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxInTest(wxCommandEvent& event) -{ - plotDeviceInputForAFewSecs(txInAudioDeviceNum, m_plotScalarTxIn); -} - -//------------------------------------------------------------------------- -// OnTxOutTest() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnTxOutTest(wxCommandEvent& event) -{ - plotDeviceOutputForAFewSecs(txOutAudioDeviceNum, m_plotScalarTxOut); -} - -//------------------------------------------------------------------------- -// OnRefreshClick() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnRefreshClick(wxCommandEvent& event) -{ - // restart portaudio, to re-sample available devices - - Pa_Terminate(); - Pa_Init(); - - m_notebook1->SetSelection(0); - showAPIInfo(); - populateParams(m_RxInDevices); - populateParams(m_RxOutDevices); - populateParams(m_TxInDevices); - populateParams(m_TxOutDevices); - - // some devices may have dissapeared, so possibily change sound - // card config - - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// OnApplyAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnApplyAudioParameters(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } -} - -//------------------------------------------------------------------------- -// OnCancelAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnCancelAudioParameters(wxCommandEvent& event) -{ - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } - EndModal(wxCANCEL); -} - -//------------------------------------------------------------------------- -// OnOkAudioParameters() -//------------------------------------------------------------------------- -void AudioOptsDialog::OnOkAudioParameters(wxCommandEvent& event) -{ - int status = ExchangeData(EXCHANGE_DATA_OUT); - - // We only accept OK if config sucessful - - printf("status: %d m_isPaInitialized: %d\n", status, m_isPaInitialized); - if (status == 0) { - if(m_isPaInitialized) - { - if((pa_err = Pa_Terminate()) == paNoError) - { - printf("terminated OK\n"); - m_isPaInitialized = false; - } - else - { - wxMessageBox(wxT("Port Audio failed to Terminate"), wxT("Pa_Terminate"), wxOK); - } - } - EndModal(wxOK); - } - -} diff --git a/freedv/tags/1.2.2/src/dlg_audiooptions.h b/freedv/tags/1.2.2/src/dlg_audiooptions.h deleted file mode 100644 index 5aa6741d..00000000 --- a/freedv/tags/1.2.2/src/dlg_audiooptions.h +++ /dev/null @@ -1,176 +0,0 @@ -//========================================================================= -// Name: AudioInfoDisplay.h -// Purpose: Declares simple wxWidgets application with GUI -// created using wxFormBuilder. -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================= -#ifndef __AudioOptsDialog__ -#define __AudioOptsDialog__ - -#include "fdmdv2_main.h" - -#define ID_AUDIO_OPTIONS 1000 -#define AUDIO_IN 0 -#define AUDIO_OUT 1 - -#include "portaudio.h" -#ifdef WIN32 -#if PA_USE_ASIO -#include "pa_asio.h" -#endif -#endif -#include "codec2_fifo.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// AudioInfoDisplay -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class AudioInfoDisplay -{ - public: - wxListCtrl* m_listDevices; - int direction; - wxTextCtrl* m_textDevice; - wxComboBox* m_cbSampleRate; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class AudioOptsDialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class AudioOptsDialog : public wxDialog -{ - private: - - protected: - PaError pa_err; - bool m_isPaInitialized; - - int rxInAudioDeviceNum; - int rxOutAudioDeviceNum; - int txInAudioDeviceNum; - int txOutAudioDeviceNum; - - void buildTestControls(PlotScalar **plotScalar, wxButton **btnTest, - wxPanel *parentPanel, wxBoxSizer *bSizer, wxString buttonLabel); - void plotDeviceInputForAFewSecs(int devNum, PlotScalar *plotScalar); - void plotDeviceOutputForAFewSecs(int devNum, PlotScalar *plotScalar); - - int buildListOfSupportedSampleRates(wxComboBox *cbSampleRate, int devNum, int in_out); - void populateParams(AudioInfoDisplay); - void showAPIInfo(); - int setTextCtrlIfDevNumValid(wxTextCtrl *textCtrl, wxListCtrl *listCtrl, int devNum); - void Pa_Init(void); - void OnDeviceSelect(wxComboBox *cbSampleRate, - wxTextCtrl *textCtrl, - int *devNum, - wxListCtrl *listCtrlDevices, - int index, - int in_out); - - AudioInfoDisplay m_RxInDevices; - AudioInfoDisplay m_RxOutDevices; - AudioInfoDisplay m_TxInDevices; - AudioInfoDisplay m_TxOutDevices; - wxPanel* m_panel1; - wxNotebook* m_notebook1; - - wxPanel* m_panelRx; - - wxListCtrl* m_listCtrlRxInDevices; - wxStaticText* m_staticText51; - wxTextCtrl* m_textCtrlRxIn; - wxStaticText* m_staticText6; - wxComboBox* m_cbSampleRateRxIn; - - wxButton* m_btnRxInTest; - PlotScalar* m_plotScalarRxIn; - - wxListCtrl* m_listCtrlRxOutDevices; - wxStaticText* m_staticText9; - wxTextCtrl* m_textCtrlRxOut; - wxStaticText* m_staticText10; - wxComboBox* m_cbSampleRateRxOut; - - wxButton* m_btnRxOutTest; - PlotScalar* m_plotScalarRxOut; - - wxPanel* m_panelTx; - - wxListCtrl* m_listCtrlTxInDevices; - wxStaticText* m_staticText12; - wxTextCtrl* m_textCtrlTxIn; - wxStaticText* m_staticText11; - wxComboBox* m_cbSampleRateTxIn; - - wxButton* m_btnTxInTest; - PlotScalar* m_plotScalarTxIn; - - wxListCtrl* m_listCtrlTxOutDevices; - wxStaticText* m_staticText81; - wxTextCtrl* m_textCtrlTxOut; - wxStaticText* m_staticText71; - wxComboBox* m_cbSampleRateTxOut; - - wxButton* m_btnTxOutTest; - PlotScalar* m_plotScalarTxOut; - - wxPanel* m_panelAPI; - - wxStaticText* m_staticText7; - wxStaticText* m_textStringVer; - wxStaticText* m_staticText8; - wxStaticText* m_textIntVer; - wxStaticText* m_staticText5; - wxStaticText* m_textCDevCount; - wxStaticText* m_staticText4; - wxStaticText* m_textAPICount; - wxButton* m_btnRefresh; - wxStdDialogButtonSizer* m_sdbSizer1; - wxButton* m_sdbSizer1OK; - wxButton* m_sdbSizer1Apply; - wxButton* m_sdbSizer1Cancel; - - // Virtual event handlers, overide them in your derived class - //virtual void OnActivateApp( wxActivateEvent& event ) { event.Skip(); } -// virtual void OnCloseFrame( wxCloseEvent& event ) { event.Skip(); } - - void OnRxInDeviceSelect( wxListEvent& event ); - - void OnRxInTest( wxCommandEvent& event ); - void OnRxOutTest( wxCommandEvent& event ); - void OnTxInTest( wxCommandEvent& event ); - void OnTxOutTest( wxCommandEvent& event ); - - void OnRxOutDeviceSelect( wxListEvent& event ); - void OnTxInDeviceSelect( wxListEvent& event ); - void OnTxOutDeviceSelect( wxListEvent& event ); - void OnRefreshClick( wxCommandEvent& event ); - void OnApplyAudioParameters( wxCommandEvent& event ); - void OnCancelAudioParameters( wxCommandEvent& event ); - void OnOkAudioParameters( wxCommandEvent& event ); - // Virtual event handlers, overide them in your derived class - void OnClose( wxCloseEvent& event ) { event.Skip(); } - void OnHibernate( wxActivateEvent& event ) { event.Skip(); } - void OnIconize( wxIconizeEvent& event ) { event.Skip(); } - void OnInitDialog( wxInitDialogEvent& event ); - - public: - - AudioOptsDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Audio Config"), const wxPoint& pos = wxPoint(1,1), const wxSize& size = wxSize( 800, 650 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~AudioOptsDialog(); - int ExchangeData(int inout); -}; -#endif //__AudioOptsDialog__ diff --git a/freedv/tags/1.2.2/src/dlg_filter.cpp b/freedv/tags/1.2.2/src/dlg_filter.cpp deleted file mode 100644 index 5a5294a9..00000000 --- a/freedv/tags/1.2.2/src/dlg_filter.cpp +++ /dev/null @@ -1,785 +0,0 @@ -//========================================================================== -// Name: dlg_filter.cpp -// Purpose: Dialog for controlling Codec audio filtering -// Date: Nov 25 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "dlg_filter.h" - -#define SLIDER_MAX 100 -#define SLIDER_LENGTH 100 - -#define FILTER_MIN_MAG_DB -20.0 -#define FILTER_MAX_MAG_DB 20.0 - -#define MAX_FREQ_BASS 600.00 -#define MAX_FREQ_TREBLE 3900.00 -#define MAX_FREQ_DEF 3000.00 - -#define MIN_GAIN -20 -#define MAX_GAIN 20 - -#define MAX_LOG10_Q 1.0 -#define MIN_LOG10_Q -1.0 - -// DFT parameters - -#define IMP_AMP 2000.0 // amplitude of impulse -#define NIMP 50 // number of samples in impulse response -#define F_STEP_DFT 10.0 // frequency steps to sample spectrum -#define F_MAG_N (int)(MAX_F_HZ/F_STEP_DFT) // number of frequency steps - -extern struct freedv *g_pfreedv; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class FilterDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -FilterDlg::FilterDlg(wxWindow* parent, bool running, bool *newMicInFilter, bool *newSpkOutFilter, - wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - m_running = running; - m_newMicInFilter = newMicInFilter; - m_newSpkOutFilter = newSpkOutFilter; - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* bSizer30; - bSizer30 = new wxBoxSizer(wxVERTICAL); - - // LPC Post Filter -------------------------------------------------------- - - wxStaticBoxSizer* lpcpfs = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("LPC Post Filter")), wxHORIZONTAL); - - wxBoxSizer* left = new wxBoxSizer(wxVERTICAL); - - m_codec2LPCPostFilterEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - left->Add(m_codec2LPCPostFilterEnable); - - m_codec2LPCPostFilterBassBoost = new wxCheckBox(this, wxID_ANY, _("0-1 kHz 3dB Boost"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - left->Add(m_codec2LPCPostFilterBassBoost); - lpcpfs->Add(left, 0, wxALL, 5); - - newLPCPFControl(&m_codec2LPCPostFilterBeta, &m_staticTextBeta, lpcpfs, "Beta"); - newLPCPFControl(&m_codec2LPCPostFilterGamma, &m_staticTextGamma, lpcpfs, "Gamma"); - - m_LPCPostFilterDefault = new wxButton(this, wxID_ANY, wxT("Default")); - lpcpfs->Add(m_LPCPostFilterDefault, 0, wxALL|wxALIGN_CENTRE_HORIZONTAL|wxALIGN_CENTRE_VERTICAL, 5); - - bSizer30->Add(lpcpfs, 0, wxALL, 0); - - // Speex pre-processor -------------------------------------------------- - - wxStaticBoxSizer* sbSizer_speexpp; - wxStaticBox *sb_speexpp = new wxStaticBox(this, wxID_ANY, _("Speex Mic Audio Pre-Processor")); - sbSizer_speexpp = new wxStaticBoxSizer(sb_speexpp, wxVERTICAL); - - m_ckboxSpeexpp = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_speexpp->SetToolTip(_("Enable noise supression, dereverberation, AGC of mic signal")); - sbSizer_speexpp->Add(m_ckboxSpeexpp, wxALIGN_LEFT, 2); - - bSizer30->Add(sbSizer_speexpp, 0, wxALL, 0); - - // EQ Filters ----------------------------------------------------------- - - wxStaticBoxSizer* eqMicInSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Mic In Equaliser")), wxVERTICAL); - wxBoxSizer* eqMicInSizer1 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* eqMicInSizer2 = new wxBoxSizer(wxHORIZONTAL); - - m_MicInBass = newEQ(eqMicInSizer1, "Bass" , MAX_FREQ_BASS, disableQ); - m_MicInTreble = newEQ(eqMicInSizer1, "Treble", MAX_FREQ_TREBLE, disableQ); - eqMicInSizer->Add(eqMicInSizer1); - - m_MicInEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - eqMicInSizer2->Add(m_MicInEnable,0,wxALIGN_CENTRE_VERTICAL|wxRIGHT,10); - m_MicInMid = newEQ(eqMicInSizer2, "Mid" , MAX_FREQ_DEF, enableQ); - m_MicInDefault = new wxButton(this, wxID_ANY, wxT("Default")); - eqMicInSizer2->Add(m_MicInDefault,0,wxALIGN_CENTRE_VERTICAL|wxLEFT,20); - eqMicInSizer->Add(eqMicInSizer2); - - wxStaticBoxSizer* eqSpkOutSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Speaker Out Equaliser")), wxVERTICAL); - wxBoxSizer* eqSpkOutSizer1 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* eqSpkOutSizer2 = new wxBoxSizer(wxHORIZONTAL); - - m_SpkOutBass = newEQ(eqSpkOutSizer1, "Bass" , MAX_FREQ_BASS, disableQ); - m_SpkOutTreble = newEQ(eqSpkOutSizer1, "Treble", MAX_FREQ_TREBLE, disableQ); - eqSpkOutSizer->Add(eqSpkOutSizer1); - - m_SpkOutEnable = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition,wxDefaultSize, wxCHK_2STATE); - eqSpkOutSizer2->Add(m_SpkOutEnable,0,wxALIGN_CENTRE_VERTICAL|wxRIGHT,10); - m_SpkOutMid = newEQ(eqSpkOutSizer2, "Mid" , MAX_FREQ_DEF, enableQ); - m_SpkOutDefault = new wxButton(this, wxID_ANY, wxT("Default")); - eqSpkOutSizer2->Add(m_SpkOutDefault,0,wxALIGN_CENTRE_VERTICAL|wxLEFT,20); - eqSpkOutSizer->Add(eqSpkOutSizer2); - - bSizer30->Add(eqMicInSizer, 0, wxALL, 0); - bSizer30->Add(eqSpkOutSizer, 0, wxALL, 0); - - // Storgage for spectrum magnitude plots ------------------------------------ - - m_MicInMagdB = new float[F_MAG_N]; - for(int i=0; iSetFont(wxFont(8, 70, 90, 90, false, wxEmptyString)); - - bSizer30->Add(m_auiNotebook, 0, wxEXPAND|wxALL, 3); - - m_MicInFreqRespPlot = new PlotSpectrum((wxFrame*) m_auiNotebook, m_MicInMagdB, F_MAG_N, FILTER_MIN_MAG_DB, FILTER_MAX_MAG_DB); - m_auiNotebook->AddPage(m_MicInFreqRespPlot, _("Microphone In Equaliser")); - - m_SpkOutFreqRespPlot = new PlotSpectrum((wxFrame*)m_auiNotebook, m_SpkOutMagdB, F_MAG_N, FILTER_MIN_MAG_DB, FILTER_MAX_MAG_DB); - m_auiNotebook->AddPage(m_SpkOutFreqRespPlot, _("Speaker Out Equaliser")); - - // OK - Cancel buttons at the bottom -------------------------- - - wxBoxSizer* bSizer31 = new wxBoxSizer(wxHORIZONTAL); - - m_sdbSizer5OK = new wxButton(this, wxID_OK); - bSizer31->Add(m_sdbSizer5OK, 0, wxALL, 2); - - m_sdbSizer5Cancel = new wxButton(this, wxID_CANCEL); - bSizer31->Add(m_sdbSizer5Cancel, 0, wxALL, 2); - - bSizer30->Add(bSizer31, 0, wxALIGN_RIGHT|wxALL, 0); - - this->SetSizer(bSizer30); - this->Layout(); - - this->Centre(wxBOTH); - - // Connect Events ------------------------------------------------------- - - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(FilterDlg::OnInitDialog)); - - m_codec2LPCPostFilterEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnEnable), NULL, this); - m_codec2LPCPostFilterBassBoost->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnBassBoost), NULL, this); - m_codec2LPCPostFilterBeta->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnBetaScroll), NULL, this); - m_codec2LPCPostFilterGamma->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnGammaScroll), NULL, this); - m_LPCPostFilterDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnLPCPostFilterDefault), NULL, this); - - m_ckboxSpeexpp->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpeexppEnable), NULL, this); - - m_MicInBass.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassFreqScroll), NULL, this); - m_MicInBass.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassGainScroll), NULL, this); - m_MicInTreble.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleFreqScroll), NULL, this); - m_MicInTreble.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleGainScroll), NULL, this); - m_MicInMid.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidFreqScroll), NULL, this); - m_MicInMid.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidGainScroll), NULL, this); - m_MicInMid.sliderQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidQScroll), NULL, this); - m_MicInEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnMicInEnable), NULL, this); - m_MicInDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnMicInDefault), NULL, this); - - m_SpkOutBass.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassFreqScroll), NULL, this); - m_SpkOutBass.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassGainScroll), NULL, this); - m_SpkOutTreble.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleFreqScroll), NULL, this); - m_SpkOutTreble.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleGainScroll), NULL, this); - m_SpkOutMid.sliderFreq->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidFreqScroll), NULL, this); - m_SpkOutMid.sliderGain->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidGainScroll), NULL, this); - m_SpkOutMid.sliderQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidQScroll), NULL, this); - m_SpkOutEnable->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpkOutEnable), NULL, this); - m_SpkOutDefault->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnSpkOutDefault), NULL, this); - - m_sdbSizer5Cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnCancel), NULL, this); - m_sdbSizer5OK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnOK), NULL, this); - -} - -//------------------------------------------------------------------------- -// ~FilterDlg() -//------------------------------------------------------------------------- -FilterDlg::~FilterDlg() -{ - delete m_MicInMagdB; - delete m_SpkOutMagdB; - - // Disconnect Events - - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(FilterDlg::OnInitDialog)); - - m_codec2LPCPostFilterEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnEnable), NULL, this); - m_codec2LPCPostFilterBassBoost->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnBassBoost), NULL, this); - m_codec2LPCPostFilterBeta->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnBetaScroll), NULL, this); - m_codec2LPCPostFilterGamma->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnGammaScroll), NULL, this); - m_LPCPostFilterDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnLPCPostFilterDefault), NULL, this); - - m_MicInBass.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassFreqScroll), NULL, this); - m_MicInBass.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInBassGainScroll), NULL, this); - m_MicInTreble.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleFreqScroll), NULL, this); - m_MicInTreble.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInTrebleGainScroll), NULL, this); - m_MicInMid.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidFreqScroll), NULL, this); - m_MicInMid.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidGainScroll), NULL, this); - m_MicInMid.sliderQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnMicInMidQScroll), NULL, this); - m_MicInEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnMicInEnable), NULL, this); - m_MicInDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnMicInDefault), NULL, this); - - m_SpkOutBass.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassFreqScroll), NULL, this); - m_SpkOutBass.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutBassGainScroll), NULL, this); - m_SpkOutTreble.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleFreqScroll), NULL, this); - m_SpkOutTreble.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutTrebleGainScroll), NULL, this); - m_SpkOutMid.sliderFreq->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidFreqScroll), NULL, this); - m_SpkOutMid.sliderGain->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidGainScroll), NULL, this); - m_SpkOutMid.sliderQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(FilterDlg::OnSpkOutMidQScroll), NULL, this); - m_SpkOutEnable->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(FilterDlg::OnSpkOutEnable), NULL, this); - m_SpkOutDefault->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnSpkOutDefault), NULL, this); - - m_sdbSizer5Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnCancel), NULL, this); - m_sdbSizer5OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FilterDlg::OnOK), NULL, this); -} - -void FilterDlg::newLPCPFControl(wxSlider **slider, wxStaticText **stValue, wxSizer *s, wxString controlName) -{ - wxBoxSizer *bs = new wxBoxSizer(wxHORIZONTAL); - - wxStaticText* st = new wxStaticText(this, wxID_ANY, controlName, wxDefaultPosition, wxSize(70,-1), wxALIGN_RIGHT); - bs->Add(st, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 2); - - *slider = new wxSlider(this, wxID_ANY, 0, 0, SLIDER_MAX, wxDefaultPosition, wxSize(SLIDER_LENGTH,wxDefaultCoord)); - bs->Add(*slider, 1, wxALIGN_CENTER_VERTICAL|wxALL, 2); - - *stValue = new wxStaticText(this, wxID_ANY, wxT("0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - bs->Add(*stValue, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxALL, 2); - - s->Add(bs, 0); -} - -void FilterDlg::newEQControl(wxSlider** slider, wxStaticText** value, wxStaticBoxSizer *bs, wxString controlName) -{ - wxStaticText* label = new wxStaticText(this, wxID_ANY, controlName, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); - bs->Add(label, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 0); - - *slider = new wxSlider(this, wxID_ANY, 0, 0, SLIDER_MAX, wxDefaultPosition, wxSize(SLIDER_LENGTH,wxDefaultCoord)); - bs->Add(*slider, 1, wxALIGN_CENTER_VERTICAL|wxALL, 0); - - *value = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(40,-1), wxALIGN_LEFT); - bs->Add(*value, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxRIGHT, 5); -} - -EQ FilterDlg::newEQ(wxSizer *bs, wxString eqName, float maxFreqHz, bool enableQ) -{ - EQ eq; - - wxStaticBoxSizer *bsEQ = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, eqName), wxHORIZONTAL); - - newEQControl(&eq.sliderFreq, &eq.valueFreq, bsEQ, "Freq"); - eq.maxFreqHz = maxFreqHz; - eq.sliderFreqId = eq.sliderFreq->GetId(); - - newEQControl(&eq.sliderGain, &eq.valueGain, bsEQ, "Gain"); - if (enableQ) - newEQControl(&eq.sliderQ, &eq.valueQ, bsEQ, "Q"); - else - eq.sliderQ = NULL; - - bs->Add(bsEQ); - - return eq; -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void FilterDlg::ExchangeData(int inout, bool storePersistent) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - if(inout == EXCHANGE_DATA_IN) - { - // LPC Post filter - - m_codec2LPCPostFilterEnable->SetValue(wxGetApp().m_codec2LPCPostFilterEnable); - m_codec2LPCPostFilterBassBoost->SetValue(wxGetApp().m_codec2LPCPostFilterBassBoost); - m_beta = wxGetApp().m_codec2LPCPostFilterBeta; setBeta(); - m_gamma = wxGetApp().m_codec2LPCPostFilterGamma; setGamma(); - - // Speex Pre-Processor - - m_ckboxSpeexpp->SetValue(wxGetApp().m_speexpp_enable); - - // Mic In Equaliser - - m_MicInBass.freqHz = wxGetApp().m_MicInBassFreqHz; - m_MicInBass.freqHz = limit(m_MicInBass.freqHz, 1.0, MAX_FREQ_BASS); - setFreq(&m_MicInBass); - m_MicInBass.gaindB = wxGetApp().m_MicInBassGaindB; - m_MicInBass.gaindB = limit(m_MicInBass.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInBass); - - m_MicInTreble.freqHz = wxGetApp().m_MicInTrebleFreqHz; - m_MicInTreble.freqHz = limit(m_MicInTreble.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_MicInTreble); - m_MicInTreble.gaindB = wxGetApp().m_MicInTrebleGaindB; - m_MicInTreble.gaindB = limit(m_MicInTreble.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInTreble); - - m_MicInMid.freqHz = wxGetApp().m_MicInMidFreqHz; - m_MicInMid.freqHz = limit(m_MicInMid.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_MicInMid); - m_MicInMid.gaindB = wxGetApp().m_MicInMidGaindB; - m_MicInMid.gaindB = limit(m_MicInMid.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_MicInMid); - m_MicInMid.Q = wxGetApp().m_MicInMidQ; - m_MicInMid.Q = limit(m_MicInMid.Q, pow(10.0,MIN_LOG10_Q), pow(10.0, MAX_LOG10_Q)); - setQ(&m_MicInMid); - - m_MicInEnable->SetValue(wxGetApp().m_MicInEQEnable); - - plotMicInFilterSpectrum(); - - // Spk Out Equaliser - - m_SpkOutBass.freqHz = wxGetApp().m_SpkOutBassFreqHz; - m_SpkOutBass.freqHz = limit(m_SpkOutBass.freqHz, 1.0, MAX_FREQ_BASS); - setFreq(&m_SpkOutBass); - m_SpkOutBass.gaindB = wxGetApp().m_SpkOutBassGaindB; - m_SpkOutBass.gaindB = limit(m_SpkOutBass.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutBass); - - m_SpkOutTreble.freqHz = wxGetApp().m_SpkOutTrebleFreqHz; - m_SpkOutTreble.freqHz = limit(m_SpkOutTreble.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_SpkOutTreble); - m_SpkOutTreble.gaindB = wxGetApp().m_SpkOutTrebleGaindB; - m_SpkOutTreble.gaindB = limit(m_SpkOutTreble.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutTreble); - - m_SpkOutMid.freqHz = wxGetApp().m_SpkOutMidFreqHz; - m_SpkOutMid.freqHz = limit(m_SpkOutMid.freqHz, 1.0, MAX_FREQ_TREBLE); - setFreq(&m_SpkOutMid); - m_SpkOutMid.gaindB = wxGetApp().m_SpkOutMidGaindB; - m_SpkOutMid.gaindB = limit(m_SpkOutMid.gaindB, MIN_GAIN, MAX_GAIN); - setGain(&m_SpkOutMid); - m_SpkOutMid.Q = wxGetApp().m_SpkOutMidQ; - m_SpkOutMid.Q = limit(m_SpkOutMid.Q, pow(10.0,MIN_LOG10_Q), pow(10.0, MAX_LOG10_Q)); - setQ(&m_SpkOutMid); - - m_SpkOutEnable->SetValue(wxGetApp().m_SpkOutEQEnable); - - plotSpkOutFilterSpectrum(); - } - if(inout == EXCHANGE_DATA_OUT) - { - // LPC Post filter - - wxGetApp().m_codec2LPCPostFilterEnable = m_codec2LPCPostFilterEnable->GetValue(); - wxGetApp().m_codec2LPCPostFilterBassBoost = m_codec2LPCPostFilterBassBoost->GetValue(); - wxGetApp().m_codec2LPCPostFilterBeta = m_beta; - wxGetApp().m_codec2LPCPostFilterGamma = m_gamma; - - // Speex Pre-Processor - - wxGetApp().m_speexpp_enable = m_ckboxSpeexpp->GetValue(); - - // Mic In Equaliser - - wxGetApp().m_MicInBassFreqHz = m_MicInBass.freqHz; - wxGetApp().m_MicInBassGaindB = m_MicInBass.gaindB; - - wxGetApp().m_MicInTrebleFreqHz = m_MicInTreble.freqHz; - wxGetApp().m_MicInTrebleGaindB = m_MicInTreble.gaindB; - - wxGetApp().m_MicInMidFreqHz = m_MicInMid.freqHz; - wxGetApp().m_MicInMidGaindB = m_MicInMid.gaindB; - wxGetApp().m_MicInMidQ = m_MicInMid.Q; - - // Spk Out Equaliser - - wxGetApp().m_SpkOutBassFreqHz = m_SpkOutBass.freqHz; - wxGetApp().m_SpkOutBassGaindB = m_SpkOutBass.gaindB; - - wxGetApp().m_SpkOutTrebleFreqHz = m_SpkOutTreble.freqHz; - wxGetApp().m_SpkOutTrebleGaindB = m_SpkOutTreble.gaindB; - - wxGetApp().m_SpkOutMidFreqHz = m_SpkOutMid.freqHz; - wxGetApp().m_SpkOutMidGaindB = m_SpkOutMid.gaindB; - wxGetApp().m_SpkOutMidQ = m_SpkOutMid.Q; - - if (storePersistent) { - pConfig->Write(wxT("/Filter/codec2LPCPostFilterEnable"), wxGetApp().m_codec2LPCPostFilterEnable); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterBassBoost"), wxGetApp().m_codec2LPCPostFilterBassBoost); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterBeta"), (int)(m_beta*100.0)); - pConfig->Write(wxT("/Filter/codec2LPCPostFilterGamma"), (int)(m_gamma*100.0)); - - pConfig->Write(wxT("/Filter/speexpp_enable"), wxGetApp().m_speexpp_enable); - - pConfig->Write(wxT("/Filter/MicInBassFreqHz"), (int)m_MicInBass.freqHz); - pConfig->Write(wxT("/Filter/MicInBassGaindB"), (int)(10.0*m_MicInBass.gaindB)); - pConfig->Write(wxT("/Filter/MicInTrebleFreqHz"), (int)m_MicInTreble.freqHz); - pConfig->Write(wxT("/Filter/MicInTrebleGaindB"), (int)(10.0*m_MicInTreble.gaindB)); - pConfig->Write(wxT("/Filter/MicInMidFreqHz"), (int)m_MicInMid.freqHz); - pConfig->Write(wxT("/Filter/MicInMidGaindB"), (int)(10.0*m_MicInMid.gaindB)); - pConfig->Write(wxT("/Filter/MicInMidQ"), (int)(100.0*m_MicInMid.Q)); - - pConfig->Write(wxT("/Filter/SpkOutBassFreqHz"), (int)m_SpkOutBass.freqHz); - pConfig->Write(wxT("/Filter/SpkOutBassGaindB"), (int)(10.0*m_SpkOutBass.gaindB)); - pConfig->Write(wxT("/Filter/SpkOutTrebleFreqHz"), (int)m_SpkOutTreble.freqHz); - pConfig->Write(wxT("/Filter/SpkOutTrebleGaindB"), (int)(10.0*m_SpkOutTreble.gaindB)); - pConfig->Write(wxT("/Filter/SpkOutMidQ"), (int)(100.0*m_SpkOutMid.Q)); - pConfig->Write(wxT("/Filter/SpkOutMidFreqHz"), (int)m_SpkOutMid.freqHz); - pConfig->Write(wxT("/Filter/SpkOutMidGaindB"), (int)(10.0*m_SpkOutMid.gaindB)); - - pConfig->Flush(); - } - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -float FilterDlg::limit(float value, float min, float max) { - if (value < min) return min; - if (value > max) return max; - return value; -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void FilterDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnDefault() -//------------------------------------------------------------------------- - -void FilterDlg::OnLPCPostFilterDefault(wxCommandEvent& event) -{ - m_beta = CODEC2_LPC_PF_BETA; setBeta(); - m_gamma = CODEC2_LPC_PF_GAMMA; setGamma(); - m_codec2LPCPostFilterEnable->SetValue(true); - m_codec2LPCPostFilterBassBoost->SetValue(true); -} - -void FilterDlg::OnMicInDefault(wxCommandEvent& event) -{ - m_MicInBass.freqHz = 100.0; - m_MicInBass.gaindB = 0.0; - setFreq(&m_MicInBass); setGain(&m_MicInBass); - - m_MicInTreble.freqHz = 3000.0; - m_MicInTreble.gaindB = 0.0; - setFreq(&m_MicInTreble); setGain(&m_MicInTreble); - - m_MicInMid.freqHz = 1500.0; - m_MicInMid.gaindB = 0.0; - m_MicInMid.Q = 1.0; - setFreq(&m_MicInMid); setGain(&m_MicInMid); setQ(&m_MicInMid); - - plotMicInFilterSpectrum(); -} - -void FilterDlg::OnSpkOutDefault(wxCommandEvent& event) -{ - m_SpkOutBass.freqHz = 100.0; - m_SpkOutBass.gaindB = 0.0; - setFreq(&m_SpkOutBass); setGain(&m_SpkOutBass); - - m_SpkOutTreble.freqHz = 3000.0; - m_SpkOutTreble.gaindB = 0.0; - setFreq(&m_SpkOutTreble); setGain(&m_SpkOutTreble); - - m_SpkOutMid.freqHz = 1500.0; - m_SpkOutMid.gaindB = 0.0; - m_SpkOutMid.Q = 1.0; - setFreq(&m_SpkOutMid); setGain(&m_SpkOutMid); setQ(&m_SpkOutMid); - - plotSpkOutFilterSpectrum(); -} - -//------------------------------------------------------------------------- -// OnOK() -//------------------------------------------------------------------------- -void FilterDlg::OnOK(wxCommandEvent& event) -{ - //printf("FilterDlg::OnOK\n"); - ExchangeData(EXCHANGE_DATA_OUT, true); - this->EndModal(wxID_OK); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void FilterDlg::OnClose(wxCloseEvent& event) -{ - this->EndModal(wxID_OK); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void FilterDlg::OnInitDialog(wxInitDialogEvent& event) -{ - //printf("FilterDlg::OnInitDialog\n"); - ExchangeData(EXCHANGE_DATA_IN, false); - //printf("m_beta: %f\n", m_beta); -} - -void FilterDlg::setBeta(void) { - wxString buf; - buf.Printf(wxT("%3.2f"), m_beta); - m_staticTextBeta->SetLabel(buf); - int slider = (int)(m_beta*SLIDER_MAX + 0.5); - m_codec2LPCPostFilterBeta->SetValue(slider); -} - -void FilterDlg::setCodec2(void) { - if (m_running) { - codec2_set_lpc_post_filter(freedv_get_codec2(g_pfreedv), - m_codec2LPCPostFilterEnable->GetValue(), - m_codec2LPCPostFilterBassBoost->GetValue(), - m_beta, m_gamma); - } -} - -void FilterDlg::setGamma(void) { - wxString buf; - buf.Printf(wxT("%3.2f"), m_gamma); - m_staticTextGamma->SetLabel(buf); - int slider = (int)(m_gamma*SLIDER_MAX + 0.5); - m_codec2LPCPostFilterGamma->SetValue(slider); -} - -void FilterDlg::OnEnable(wxScrollEvent& event) { - setCodec2(); -} - -void FilterDlg::OnBassBoost(wxScrollEvent& event) { - setCodec2(); -} - -void FilterDlg::OnBetaScroll(wxScrollEvent& event) { - m_beta = (float)m_codec2LPCPostFilterBeta->GetValue()/SLIDER_MAX; - setBeta(); - setCodec2(); -} - -void FilterDlg::OnGammaScroll(wxScrollEvent& event) { - m_gamma = (float)m_codec2LPCPostFilterGamma->GetValue()/SLIDER_MAX; - setGamma(); - setCodec2(); -} - -// immediately change enable flags rather using ExchangeData() so we can switch on and off at run time - -void FilterDlg::OnSpeexppEnable(wxScrollEvent& event) { - wxGetApp().m_speexpp_enable = m_ckboxSpeexpp->GetValue(); -} - -void FilterDlg::OnMicInEnable(wxScrollEvent& event) { - wxGetApp().m_MicInEQEnable = m_MicInEnable->GetValue(); -} - -void FilterDlg::OnSpkOutEnable(wxScrollEvent& event) { - wxGetApp().m_SpkOutEQEnable = m_SpkOutEnable->GetValue(); - //printf("wxGetApp().m_SpkOutEQEnable: %d\n", wxGetApp().m_SpkOutEQEnable); -} - -void FilterDlg::setFreq(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%3.0f"), eq->freqHz); - eq->valueFreq->SetLabel(buf); - int slider = (int)((eq->freqHz/eq->maxFreqHz)*SLIDER_MAX + 0.5); - eq->sliderFreq->SetValue(slider); -} - -void FilterDlg::sliderToFreq(EQ *eq, bool micIn) -{ - eq->freqHz = ((float)eq->sliderFreq->GetValue()/SLIDER_MAX)*eq->maxFreqHz; - if (eq->freqHz < 1.0) eq->freqHz = 1.0; // sox doesn't like 0 Hz; - setFreq(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } -} - -void FilterDlg::setGain(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%3.1f"), eq->gaindB); - eq->valueGain->SetLabel(buf); - int slider = (int)(((eq->gaindB-MIN_GAIN)/(MAX_GAIN-MIN_GAIN))*SLIDER_MAX + 0.5); - eq->sliderGain->SetValue(slider); -} - -void FilterDlg::sliderToGain(EQ *eq, bool micIn) -{ - float range = MAX_GAIN-MIN_GAIN; - - eq->gaindB = MIN_GAIN + range*((float)eq->sliderGain->GetValue()/SLIDER_MAX); - //printf("gaindB: %f\n", eq->gaindB); - setGain(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } - -} - -void FilterDlg::setQ(EQ *eq) -{ - wxString buf; - buf.Printf(wxT("%2.1f"), eq->Q); - eq->valueQ->SetLabel(buf); - - float log10_range = MAX_LOG10_Q - MIN_LOG10_Q; - - int slider = (int)(((log10(eq->Q+1E-6)-MIN_LOG10_Q)/log10_range)*SLIDER_MAX + 0.5); - eq->sliderQ->SetValue(slider); -} - -void FilterDlg::sliderToQ(EQ *eq, bool micIn) -{ - float log10_range = MAX_LOG10_Q - MIN_LOG10_Q; - - float sliderNorm = (float)eq->sliderQ->GetValue()/SLIDER_MAX; - float log10Q = MIN_LOG10_Q + sliderNorm*(log10_range); - eq->Q = pow(10.0, log10Q); - //printf("log10Q: %f eq->Q: %f\n", log10Q, eq->Q); - setQ(eq); - if (micIn) { - plotMicInFilterSpectrum(); - adjRunTimeMicInFilter(); - } - else { - plotSpkOutFilterSpectrum(); - adjRunTimeSpkOutFilter(); - } -} - -void FilterDlg::plotMicInFilterSpectrum(void) { - plotFilterSpectrum(&m_MicInBass, &m_MicInMid, &m_MicInTreble, m_MicInFreqRespPlot, m_MicInMagdB); -} - -void FilterDlg::plotSpkOutFilterSpectrum(void) { - plotFilterSpectrum(&m_SpkOutBass, &m_SpkOutMid, &m_SpkOutTreble, m_SpkOutFreqRespPlot, m_SpkOutMagdB); -} - -void FilterDlg::adjRunTimeMicInFilter(void) { - // signal an adjustment in running filter coeffs - - if (m_running) { - ExchangeData(EXCHANGE_DATA_OUT, false); - *m_newMicInFilter = true; - } -} - -void FilterDlg::adjRunTimeSpkOutFilter(void) { - // signal an adjustment in running filter coeffs - - if (m_running) { - ExchangeData(EXCHANGE_DATA_OUT, false); - *m_newSpkOutFilter = true; - } -} - - -void FilterDlg::plotFilterSpectrum(EQ *eqBass, EQ *eqMid, EQ *eqTreble, PlotSpectrum* freqRespPlot, float *magdB) { - char *argBass[10]; - char *argTreble[10]; - char *argMid[10]; - char argstorage[10][80]; - float magBass[F_MAG_N]; - float magTreble[F_MAG_N]; - float magMid[F_MAG_N]; - int i; - - for(i=0; i<10; i++) { - argBass[i] = &argstorage[i][0]; - argTreble[i] = &argstorage[i][0]; - argMid[i] = &argstorage[i][0]; - } - sprintf(argBass[0], "bass"); - sprintf(argBass[1], "%f", eqBass->gaindB+1E-6); - sprintf(argBass[2], "%f", eqBass->freqHz); - - calcFilterSpectrum(magBass, 2, argBass); - - sprintf(argTreble[0], "treble"); - sprintf(argTreble[1], "%f", eqTreble->gaindB+1E-6); - sprintf(argTreble[2], "%f", eqTreble->freqHz); - - calcFilterSpectrum(magTreble, 2, argTreble); - - sprintf(argTreble[0], "equalizer"); - sprintf(argTreble[1], "%f", eqMid->freqHz); - sprintf(argTreble[2], "%f", eqMid->Q); - sprintf(argTreble[3], "%f", eqMid->gaindB+1E-6); - - calcFilterSpectrum(magMid, 3, argMid); - - for(i=0; im_newdata = true; - freqRespPlot->Refresh(); -} - -void FilterDlg::calcFilterSpectrum(float magdB[], int argc, char *argv[]) { - void *sbq; - short in[NIMP]; - short out[NIMP]; - COMP X[F_MAG_N]; - float f, w; - int i, k; - - // find impulse response ----------------------------------- - - for(i=0; i. -// -//========================================================================== - -#ifndef __FILTER_DIALOG__ -#define __FILTER_DIALOG__ - -#include "fdmdv2_main.h" - -enum {disableQ = false, enableQ = true}; - -typedef struct { - wxSlider *sliderFreq; - wxStaticText *valueFreq; - wxSlider *sliderGain; - wxStaticText *valueGain; - wxSlider *sliderQ; - wxStaticText *valueQ; - - int sliderFreqId; - int sliderGainId; - int sliderQId; - - float freqHz; - float gaindB; - float Q; - - float maxFreqHz; -} EQ; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class FilterDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class FilterDlg : public wxDialog -{ - public: - FilterDlg( wxWindow* parent, bool running, bool *newMicInFilter, bool *newSpkOutFilter, - wxWindowID id = wxID_ANY, const wxString& title = _("Filter"), - const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 800, 630 ), - long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~FilterDlg(); - - void ExchangeData(int inout, bool storePersistent); - - protected: - // Handlers for events. - void OnCancel(wxCommandEvent& event); - void OnOK(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void OnInitDialog(wxInitDialogEvent& event); - void OnLPCPostFilterDefault(wxCommandEvent& event); - - void OnBetaScroll(wxScrollEvent& event); - void OnGammaScroll(wxScrollEvent& event); - void OnEnable(wxScrollEvent& event); - void OnBassBoost(wxScrollEvent& event); - - void OnSpeexppEnable(wxScrollEvent& event); - - void OnMicInBassFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInBass, true); } - void OnMicInBassGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInBass, true); } - void OnMicInTrebleFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInTreble, true); } - void OnMicInTrebleGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInTreble, true); } - void OnMicInMidFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_MicInMid, true); } - void OnMicInMidGainScroll(wxScrollEvent& event) { sliderToGain(&m_MicInMid, true); } - void OnMicInMidQScroll(wxScrollEvent& event) { sliderToQ(&m_MicInMid, true); } - void OnMicInEnable(wxScrollEvent& event); - void OnMicInDefault(wxCommandEvent& event); - - void OnSpkOutBassFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutBass, false); } - void OnSpkOutBassGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutBass, false); } - void OnSpkOutTrebleFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutTreble, false); } - void OnSpkOutTrebleGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutTreble, false); } - void OnSpkOutMidFreqScroll(wxScrollEvent& event) { sliderToFreq(&m_SpkOutMid, false); } - void OnSpkOutMidGainScroll(wxScrollEvent& event) { sliderToGain(&m_SpkOutMid, false); } - void OnSpkOutMidQScroll(wxScrollEvent& event) { sliderToQ(&m_SpkOutMid, false); } - void OnSpkOutEnable(wxScrollEvent& event); - void OnSpkOutDefault(wxCommandEvent& event); - - wxStaticText* m_staticText8; - wxCheckBox* m_codec2LPCPostFilterEnable; - wxStaticText* m_staticText9; - wxCheckBox* m_codec2LPCPostFilterBassBoost; - wxStaticText* m_staticText91; - wxSlider* m_codec2LPCPostFilterBeta; - wxStaticText* m_staticTextBeta; - wxStaticText* m_staticText911; - wxSlider* m_codec2LPCPostFilterGamma; - wxStaticText* m_staticTextGamma; - wxButton* m_LPCPostFilterDefault; - - wxCheckBox* m_ckboxSpeexpp; - - wxStdDialogButtonSizer* m_sdbSizer5; - wxButton* m_sdbSizer5OK; - wxButton* m_sdbSizer5Cancel; - PlotSpectrum* m_MicInFreqRespPlot; - PlotSpectrum* m_SpkOutFreqRespPlot; - - wxCheckBox* m_MicInEnable; - wxButton* m_MicInDefault; - wxCheckBox* m_SpkOutEnable; - wxButton* m_SpkOutDefault; - - float *m_MicInMagdB; - float *m_SpkOutMagdB; - - private: - bool m_running; - float m_beta; - float m_gamma; - - void setBeta(void); // sets slider and static text from m_beta - void setGamma(void); // sets slider and static text from m_gamma - void setCodec2(void); - - void newEQControl(wxSlider** slider, wxStaticText** value, wxStaticBoxSizer *bs, wxString controlName); - EQ newEQ(wxSizer *bs, wxString eqName, float maxFreqHz, bool enableQ); - void newLPCPFControl(wxSlider **slider, wxStaticText **stValue, wxSizer *sbs, wxString controlName); - wxAuiNotebook *m_auiNotebook; - void setFreq(EQ *eq); - void setGain(EQ *eq); - void setQ(EQ *eq); - void sliderToFreq(EQ *eq, bool micIn); - void sliderToGain(EQ *eq, bool micIn); - void sliderToQ(EQ *eq, bool micIn); - void plotFilterSpectrum(EQ *eqBass, EQ *eqMid, EQ* eqTreble, PlotSpectrum* freqRespPlot, float *magdB); - void calcFilterSpectrum(float magdB[], int arc, char *argv[]); - void plotMicInFilterSpectrum(void); - void plotSpkOutFilterSpectrum(void); - void adjRunTimeMicInFilter(void); - void adjRunTimeSpkOutFilter(void); - - EQ m_MicInBass; - EQ m_MicInMid; - EQ m_MicInTreble; - - EQ m_SpkOutBass; - EQ m_SpkOutMid; - EQ m_SpkOutTreble; - - float limit(float value, float min, float max); - - bool *m_newMicInFilter; - bool *m_newSpkOutFilter; - -}; - -#endif // __FILTER_DIALOG__ diff --git a/freedv/tags/1.2.2/src/dlg_options.cpp b/freedv/tags/1.2.2/src/dlg_options.cpp deleted file mode 100644 index 79b6c87f..00000000 --- a/freedv/tags/1.2.2/src/dlg_options.cpp +++ /dev/null @@ -1,616 +0,0 @@ -//========================================================================== -// Name: dlg_options.cpp -// Purpose: Dialog for controlling misc FreeDV options -// Date: May 24 2013 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "dlg_options.h" - -extern bool g_modal; -extern struct freedv *g_pfreedv; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class OptionsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -OptionsDlg::OptionsDlg(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* bSizer30; - bSizer30 = new wxBoxSizer(wxVERTICAL); - - //------------------------------ - // Txt Msg Text Box - //------------------------------ - - wxStaticBoxSizer* sbSizer_callSign; - wxStaticBox *sb_textMsg = new wxStaticBox(this, wxID_ANY, _("Txt Msg")); - sbSizer_callSign = new wxStaticBoxSizer(sb_textMsg, wxVERTICAL); - - m_txtCtrlCallSign = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - m_txtCtrlCallSign->SetToolTip(_("Txt Msg you can send along with Voice")); - sbSizer_callSign->Add(m_txtCtrlCallSign, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 3); - - bSizer30->Add(sbSizer_callSign,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //---------------------------------------------------------------------- - // Voice Keyer - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer28a = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Voice Keyer")), wxHORIZONTAL); - - wxStaticText *m_staticText28b = new wxStaticText(this, wxID_ANY, _("Wave File: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28b, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtCtrlVoiceKeyerWaveFile = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300,-1), 0); - m_txtCtrlVoiceKeyerWaveFile->SetToolTip(_("Wave file to play for Voice Keyer")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerWaveFile, 0, 0, 5); - - m_buttonChooseVoiceKeyerWaveFile = new wxButton(this, wxID_APPLY, _("Choose"), wxDefaultPosition, wxSize(-1,-1), 0); - staticBoxSizer28a->Add(m_buttonChooseVoiceKeyerWaveFile, 0, wxALIGN_CENTER_VERTICAL, 5); - - wxStaticText *m_staticText28c = new wxStaticText(this, wxID_ANY, _(" Rx Pause: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28c, 0, wxALIGN_CENTER_VERTICAL , 5); - m_txtCtrlVoiceKeyerRxPause = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0); - m_txtCtrlVoiceKeyerRxPause->SetToolTip(_("How long to wait in Rx mode before repeat")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerRxPause, 0, 0, 5); - - wxStaticText *m_staticText28d = new wxStaticText(this, wxID_ANY, _(" Repeats: "), wxDefaultPosition, wxDefaultSize, 0); - staticBoxSizer28a->Add(m_staticText28d, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtCtrlVoiceKeyerRepeats = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0); - m_txtCtrlVoiceKeyerRepeats->SetToolTip(_("How long to wait in Rx mode before repeat")); - staticBoxSizer28a->Add(m_txtCtrlVoiceKeyerRepeats, 0, 0, 5); - - bSizer30->Add(staticBoxSizer28a,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - -#ifdef __WXMSW__ - //------------------------------ - // debug console, for WIndows build make console pop up for debug messages - //------------------------------ - - wxStaticBoxSizer* sbSizer_console; - wxStaticBox *sb_console = new wxStaticBox(this, wxID_ANY, _("Debug")); - sbSizer_console = new wxStaticBoxSizer(sb_console, wxHORIZONTAL); - - m_ckboxDebugConsole = new wxCheckBox(this, wxID_ANY, _("Show Console"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_console->Add(m_ckboxDebugConsole, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_console,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); -#endif - - //------------------------------ - // FreeDV 700 Options - //------------------------------ - - wxStaticBoxSizer* sbSizer_freedv700; - wxStaticBox *sb_freedv700 = new wxStaticBox(this, wxID_ANY, _("FreeDV 700 Options")); - sbSizer_freedv700 = new wxStaticBoxSizer(sb_freedv700, wxHORIZONTAL); - - m_ckboxFreeDV700txClip = new wxCheckBox(this, wxID_ANY, _("Clipping"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_freedv700->Add(m_ckboxFreeDV700txClip, 0, wxALIGN_LEFT, 0); - m_ckboxFreeDV700Combine = new wxCheckBox(this, wxID_ANY, _("Diversity Combine for plots"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_freedv700->Add(m_ckboxFreeDV700Combine, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_freedv700, 0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Half/Full duplex selection - //------------------------------ - - wxStaticBox *sb_duplex = new wxStaticBox(this, wxID_ANY, _("Half/Full Duplex Operation")); - wxStaticBoxSizer* sbSizer_duplex = new wxStaticBoxSizer(sb_duplex, wxHORIZONTAL); - m_ckHalfDuplex = new wxCheckBox(this, wxID_ANY, _("Half Duplex"), wxDefaultPosition, wxSize(-1,-1), 0); - sbSizer_duplex->Add(m_ckHalfDuplex, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5); - bSizer30->Add(sbSizer_duplex,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Test Frames/Channel simulation check box - //------------------------------ - - wxStaticBoxSizer* sbSizer_testFrames; - wxStaticBox *sb_testFrames = new wxStaticBox(this, wxID_ANY, _("Testing and Channel Simulation")); - sbSizer_testFrames = new wxStaticBoxSizer(sb_testFrames, wxHORIZONTAL); - - m_ckboxTestFrame = new wxCheckBox(this, wxID_ANY, _("Test Frames"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxTestFrame, 0, wxALIGN_LEFT, 0); - - m_ckboxChannelNoise = new wxCheckBox(this, wxID_ANY, _("Channel Noise SNR (dB):"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxChannelNoise, 0, wxALIGN_LEFT, 0); - m_txtNoiseSNR = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_testFrames->Add(m_txtNoiseSNR, 0, wxALIGN_LEFT, 0); - - m_ckboxAttnCarrierEn = new wxCheckBox(this, wxID_ANY, _("Attn Carrier Carrier:"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxAttnCarrierEn, 0, wxALIGN_LEFT, 0); - m_txtAttnCarrier = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_testFrames->Add(m_txtAttnCarrier, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_testFrames,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Interfering tone - //------------------------------ - - wxStaticBoxSizer* sbSizer_tone; - wxStaticBox *sb_tone = new wxStaticBox(this, wxID_ANY, _("Simulated Interference Tone")); - sbSizer_tone = new wxStaticBoxSizer(sb_tone, wxHORIZONTAL); - - m_ckboxTone = new wxCheckBox(this, wxID_ANY, _("Tone Freq (Hz):"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_tone->Add(m_ckboxTone, 0, wxALIGN_LEFT, 0); - m_txtToneFreqHz = new wxTextCtrl(this, wxID_ANY, "1000", wxDefaultPosition, wxSize(60,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_tone->Add(m_txtToneFreqHz, 0, wxALIGN_LEFT, 0); - wxStaticText *m_staticTextta = new wxStaticText(this, wxID_ANY, _(" Amplitude (pk): "), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_tone->Add(m_staticTextta, 0, wxALIGN_CENTER_VERTICAL, 5); - m_txtToneAmplitude = new wxTextCtrl(this, wxID_ANY, "1000", wxDefaultPosition, wxSize(60,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_tone->Add(m_txtToneAmplitude, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_tone,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - -#ifdef __EXPERIMENTAL_UDP__ - //------------------------------ - // Txt Encoding - //------------------------------ - - wxStaticBoxSizer* sbSizer_encoding = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Text Encoding")), wxHORIZONTAL); - -#ifdef SHORT_VARICODE - m_rb_textEncoding1 = new wxRadioButton( this, wxID_ANY, wxT("Long varicode"), wxDefaultPosition, wxDefaultSize, 0); - m_rb_textEncoding1->SetValue(true); - sbSizer_encoding->Add(m_rb_textEncoding1, 0, wxALIGN_LEFT|wxALL, 1); - m_rb_textEncoding2 = new wxRadioButton( this, wxID_ANY, wxT("Short Varicode"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_encoding->Add(m_rb_textEncoding2, 0, wxALIGN_LEFT|wxALL, 1); -#endif - - m_ckboxEnableChecksum = new wxCheckBox(this, wxID_ANY, _("Use Checksum on Rx"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_encoding->Add(m_ckboxEnableChecksum, 0, wxALIGN_LEFT, 0); - - bSizer30->Add(sbSizer_encoding,0, wxALL|wxEXPAND, 3); - - //------------------------------ - // Event processing - //------------------------------ - - wxStaticBoxSizer* sbSizer_events; - wxStaticBox *sb_events = new wxStaticBox(this, wxID_ANY, _("Event Processing")); - sbSizer_events = new wxStaticBoxSizer(sb_events, wxVERTICAL); - - // event processing enable and spam timer - - wxStaticBoxSizer* sbSizer_events_top; - wxStaticBox* sb_events1 = new wxStaticBox(this, wxID_ANY, _("")); - sbSizer_events_top = new wxStaticBoxSizer(sb_events1, wxHORIZONTAL); - - m_ckbox_events = new wxCheckBox(this, wxID_ANY, _("Enable System Calls Syscall Spam Timer"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_events->SetToolTip(_("Enable processing of events and generation of system calls")); - sbSizer_events_top->Add(m_ckbox_events, 0, 0, 5); - m_txt_spam_timer = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(40,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - m_txt_spam_timer->SetToolTip(_("Many matching events can cause a flood of syscalls. Set minimum time (seconds) between syscalls for each event here")); - sbSizer_events_top->Add(m_txt_spam_timer, 0, 0, 5); - m_rb_spam_timer = new wxRadioButton( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - m_rb_spam_timer->SetForegroundColour( wxColour(0, 255, 0 ) ); - sbSizer_events_top->Add(m_rb_spam_timer, 0, 0, 10); - sbSizer_events->Add(sbSizer_events_top, 0, 0, 5); - - // list of regexps - - wxStaticBoxSizer* sbSizer_regexp = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Regular Expressions to Process Events")), wxHORIZONTAL); - m_txt_events_regexp_match = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,100), wxTE_MULTILINE); - m_txt_events_regexp_match->SetToolTip(_("Enter regular expressions to match events")); - sbSizer_regexp->Add(m_txt_events_regexp_match, 1, wxEXPAND, 5); - m_txt_events_regexp_replace = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,100), wxTE_MULTILINE); - m_txt_events_regexp_replace->SetToolTip(_("Enter regular expressions to replace events")); - sbSizer_regexp->Add(m_txt_events_regexp_replace, 1, wxEXPAND, 5); - sbSizer_events->Add(sbSizer_regexp, 1, wxEXPAND, 5); - - // log of events and responses - - wxStaticBoxSizer* sbSizer_event_log = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Log of Events and Responses")), wxVERTICAL); - wxBoxSizer* bSizer33 = new wxBoxSizer(wxHORIZONTAL); - m_txt_events_in = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,50), wxTE_MULTILINE | wxTE_READONLY); - bSizer33->Add(m_txt_events_in, 1, wxEXPAND, 5); - m_txt_events_out = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,50), wxTE_MULTILINE | wxTE_READONLY); - bSizer33->Add(m_txt_events_out, 1, wxEXPAND, 5); - sbSizer_event_log->Add(bSizer33, 1, wxEXPAND, 5); - sbSizer_events->Add(sbSizer_event_log, 1, wxEXPAND, 5); - - bSizer30->Add(sbSizer_events,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // UDP control port - //------------------------------ - - wxStaticBoxSizer* sbSizer_udp; - wxStaticBox* sb_udp = new wxStaticBox(this, wxID_ANY, _("UDP Control Port")); - sbSizer_udp = new wxStaticBoxSizer(sb_udp, wxHORIZONTAL); - m_ckbox_udp_enable = new wxCheckBox(this, wxID_ANY, _("Enable UDP Control Port UDP Port Number:"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sb_udp->SetToolTip(_("Enable control of FreeDV via UDP port")); - sbSizer_udp->Add(m_ckbox_udp_enable, 0, wxALIGN_CENTER_HORIZONTAL, 5); - m_txt_udp_port = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(50,-1), 0, wxTextValidator(wxFILTER_DIGITS)); - sbSizer_udp->Add(m_txt_udp_port, 0, wxALIGN_CENTER_HORIZONTAL, 5); - - bSizer30->Add(sbSizer_udp,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); -#endif - - //------------------------------ - // OK - Cancel - Apply Buttons - //------------------------------ - - wxBoxSizer* bSizer31 = new wxBoxSizer(wxHORIZONTAL); - - m_sdbSizer5OK = new wxButton(this, wxID_OK); - bSizer31->Add(m_sdbSizer5OK, 0, wxALL, 2); - - m_sdbSizer5Cancel = new wxButton(this, wxID_CANCEL); - bSizer31->Add(m_sdbSizer5Cancel, 0, wxALL, 2); - - m_sdbSizer5Apply = new wxButton(this, wxID_APPLY); - bSizer31->Add(m_sdbSizer5Apply, 0, wxALL, 2); - - bSizer30->Add(bSizer31, 0, wxALIGN_CENTER, 0); - - this->SetSizer(bSizer30); - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - this->Layout(); - - this->Centre(wxBOTH); - - // Connect Events ------------------------------------------------------- - - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(OptionsDlg::OnInitDialog)); - - m_sdbSizer5OK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnOK), NULL, this); - m_sdbSizer5Cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnCancel), NULL, this); - m_sdbSizer5Apply->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnApply), NULL, this); - - m_ckboxTestFrame->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnTestFrame), NULL, this); - m_ckboxChannelNoise->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnChannelNoise), NULL, this); - m_ckboxAttnCarrierEn->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnAttnCarrierEn), NULL, this); - - m_ckboxFreeDV700txClip->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700txClip), NULL, this); - m_ckboxFreeDV700Combine->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700Combine), NULL, this); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnDebugConsole), NULL, this); -#endif - - m_buttonChooseVoiceKeyerWaveFile->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnChooseVoiceKeyerWaveFile), NULL, this); - - event_in_serial = 0; - event_out_serial = 0; -} - -//------------------------------------------------------------------------- -// ~OptionsDlg() -//------------------------------------------------------------------------- -OptionsDlg::~OptionsDlg() -{ - - // Disconnect Events - - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(OptionsDlg::OnInitDialog)); - - m_sdbSizer5OK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnOK), NULL, this); - m_sdbSizer5Cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnCancel), NULL, this); - m_sdbSizer5Apply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnApply), NULL, this); - - m_ckboxTestFrame->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnTestFrame), NULL, this); - m_ckboxChannelNoise->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnChannelNoise), NULL, this); - m_ckboxAttnCarrierEn->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnAttnCarrierEn), NULL, this); - - m_ckboxFreeDV700txClip->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700txClip), NULL, this); - m_ckboxFreeDV700Combine->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnFreeDV700Combine), NULL, this); - m_buttonChooseVoiceKeyerWaveFile->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(OptionsDlg::OnChooseVoiceKeyerWaveFile), NULL, this); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxScrollEventHandler(OptionsDlg::OnDebugConsole), NULL, this); -#endif -} - - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void OptionsDlg::ExchangeData(int inout, bool storePersistent) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - - if(inout == EXCHANGE_DATA_IN) - { - m_txtCtrlCallSign->SetValue(wxGetApp().m_callSign); - - /* Voice Keyer */ - - m_txtCtrlVoiceKeyerWaveFile->SetValue(wxGetApp().m_txtVoiceKeyerWaveFile); - m_txtCtrlVoiceKeyerRxPause->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intVoiceKeyerRxPause)); - m_txtCtrlVoiceKeyerRepeats->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intVoiceKeyerRepeats)); - - m_ckHalfDuplex->SetValue(wxGetApp().m_boolHalfDuplex); - - m_ckboxTestFrame->SetValue(wxGetApp().m_testFrames); - - m_ckboxChannelNoise->SetValue(wxGetApp().m_channel_noise); - m_txtNoiseSNR->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_noise_snr)); - - m_ckboxTone->SetValue(wxGetApp().m_tone); - m_txtToneFreqHz->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_tone_freq_hz)); - m_txtToneAmplitude->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_tone_amplitude)); - - m_ckboxAttnCarrierEn->SetValue(wxGetApp().m_attn_carrier_en); - m_txtAttnCarrier->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_attn_carrier)); - -#ifdef __EXPERIMENTAL_UDP__ - m_ckbox_events->SetValue(wxGetApp().m_events); - m_txt_spam_timer->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_events_spam_timer)); - - m_txt_events_regexp_match->SetValue(wxGetApp().m_events_regexp_match); - m_txt_events_regexp_replace->SetValue(wxGetApp().m_events_regexp_replace); - - m_ckbox_udp_enable->SetValue(wxGetApp().m_udp_enable); - m_txt_udp_port->SetValue(wxString::Format(wxT("%i"),wxGetApp().m_udp_port)); - -#ifdef SHORT_VARICODE - if (wxGetApp().m_textEncoding == 1) - m_rb_textEncoding1->SetValue(true); - if (wxGetApp().m_textEncoding == 2) - m_rb_textEncoding2->SetValue(true); -#endif - m_ckboxEnableChecksum->SetValue(wxGetApp().m_enable_checksum); -#endif - - m_ckboxFreeDV700txClip->SetValue(wxGetApp().m_FreeDV700txClip); - m_ckboxFreeDV700Combine->SetValue(wxGetApp().m_FreeDV700Combine); - -#ifdef __WXMSW__ - m_ckboxDebugConsole->SetValue(wxGetApp().m_debug_console); -#endif - } - - if(inout == EXCHANGE_DATA_OUT) - { - wxGetApp().m_callSign = m_txtCtrlCallSign->GetValue(); - - wxGetApp().m_boolHalfDuplex = m_ckHalfDuplex->GetValue(); - pConfig->Write(wxT("/Rig/HalfDuplex"), wxGetApp().m_boolHalfDuplex); - - /* Voice Keyer */ - - wxGetApp().m_txtVoiceKeyerWaveFile = m_txtCtrlVoiceKeyerWaveFile->GetValue(); - pConfig->Write(wxT("/VoiceKeyer/WaveFile"), wxGetApp().m_txtVoiceKeyerWaveFile); - long tmp; - m_txtCtrlVoiceKeyerRxPause->GetValue().ToLong(&tmp); if (tmp < 0) tmp = 0; wxGetApp().m_intVoiceKeyerRxPause = (int)tmp; - pConfig->Write(wxT("/VoiceKeyer/RxPause"), wxGetApp().m_intVoiceKeyerRxPause); - m_txtCtrlVoiceKeyerRepeats->GetValue().ToLong(&tmp); - if (tmp < 0) tmp = 0; if (tmp > 100) tmp = 100; - wxGetApp().m_intVoiceKeyerRepeats = (int)tmp; - pConfig->Write(wxT("/VoiceKeyer/Repeats"), wxGetApp().m_intVoiceKeyerRepeats); - - wxGetApp().m_testFrames = m_ckboxTestFrame->GetValue(); - - wxGetApp().m_channel_noise = m_ckboxChannelNoise->GetValue(); - long noise_snr; - m_txtNoiseSNR->GetValue().ToLong(&noise_snr); - wxGetApp().m_noise_snr = (int)noise_snr; - - wxGetApp().m_tone = m_ckboxTone->GetValue(); - long tone_freq_hz, tone_amplitude; - m_txtToneFreqHz->GetValue().ToLong(&tone_freq_hz); - wxGetApp().m_tone_freq_hz = (int)tone_freq_hz; - m_txtToneAmplitude->GetValue().ToLong(&tone_amplitude); - wxGetApp().m_tone_amplitude = (int)tone_amplitude; - - wxGetApp().m_attn_carrier_en = m_ckboxAttnCarrierEn->GetValue(); - long attn_carrier; - m_txtAttnCarrier->GetValue().ToLong(&attn_carrier); - wxGetApp().m_attn_carrier = (int)attn_carrier; - -#ifdef __EXPERIMENTAL_UDP__ - wxGetApp().m_events = m_ckbox_events->GetValue(); - long spam_timer; - m_txt_spam_timer->GetValue().ToLong(&spam_timer); - wxGetApp().m_events_spam_timer = (int)spam_timer; - - // make sure regexp lists are terminated by a \n - - if (m_txt_events_regexp_match->GetValue().Last() != '\n') { - m_txt_events_regexp_match->SetValue(m_txt_events_regexp_match->GetValue()+'\n'); - } - if (m_txt_events_regexp_replace->GetValue().Last() != '\n') { - m_txt_events_regexp_replace->SetValue(m_txt_events_regexp_replace->GetValue()+'\n'); - } - wxGetApp().m_events_regexp_match = m_txt_events_regexp_match->GetValue(); - wxGetApp().m_events_regexp_replace = m_txt_events_regexp_replace->GetValue(); - - wxGetApp().m_udp_enable = m_ckbox_udp_enable->GetValue(); - long port; - m_txt_udp_port->GetValue().ToLong(&port); - wxGetApp().m_udp_port = (int)port; - -#ifdef SHORT_VARICODE - if (m_rb_textEncoding1->GetValue()) - wxGetApp().m_textEncoding = 1; - if (m_rb_textEncoding2->GetValue()) - wxGetApp().m_textEncoding = 2; -#endif - wxGetApp().m_enable_checksum = m_ckboxEnableChecksum->GetValue(); -#endif - - wxGetApp().m_FreeDV700txClip = m_ckboxFreeDV700txClip->GetValue(); - wxGetApp().m_FreeDV700Combine = m_ckboxFreeDV700Combine->GetValue(); - -#ifdef __WXMSW__ - wxGetApp().m_debug_console = m_ckboxDebugConsole->GetValue(); -#endif - - if (storePersistent) { - pConfig->Write(wxT("/Data/CallSign"), wxGetApp().m_callSign); -#ifdef SHORT_VARICODE - pConfig->Write(wxT("/Data/TextEncoding"), wxGetApp().m_textEncoding); -#endif - pConfig->Write(wxT("/Data/EnableChecksumOnMsgRx"), wxGetApp().m_enable_checksum); - - pConfig->Write(wxT("/Events/enable"), wxGetApp().m_events); - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - pConfig->Write(wxT("/Events/regexp_match"), wxGetApp().m_events_regexp_match); - pConfig->Write(wxT("/Events/regexp_replace"), wxGetApp().m_events_regexp_replace); - - pConfig->Write(wxT("/UDP/enable"), wxGetApp().m_udp_enable); - pConfig->Write(wxT("/UDP/port"), wxGetApp().m_udp_port); - - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - - pConfig->Write(wxT("/FreeDV700/txClip"), wxGetApp().m_FreeDV700txClip); - - pConfig->Write(wxT("/Noise/noise_snr"), wxGetApp().m_noise_snr); - -#ifdef __WXMSW__ - pConfig->Write(wxT("/Debug/console"), wxGetApp().m_debug_console); -#endif - - pConfig->Flush(); - } - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -//------------------------------------------------------------------------- -// OnOK() -//------------------------------------------------------------------------- -void OptionsDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT, true); - //this->EndModal(wxID_OK); - g_modal = false; - this->Show(false); -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void OptionsDlg::OnCancel(wxCommandEvent& event) -{ - //this->EndModal(wxID_CANCEL); - g_modal = false; - this->Show(false); -} - -//------------------------------------------------------------------------- -// OnApply() -//------------------------------------------------------------------------- -void OptionsDlg::OnApply(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT, true); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void OptionsDlg::OnInitDialog(wxInitDialogEvent& event) -{ - ExchangeData(EXCHANGE_DATA_IN, false); -} - -// immediately change flags rather using ExchangeData() so we can switch on and off at run time - -void OptionsDlg::OnTestFrame(wxScrollEvent& event) { - wxGetApp().m_testFrames = m_ckboxTestFrame->GetValue(); -} - -void OptionsDlg::OnChannelNoise(wxScrollEvent& event) { - wxGetApp().m_channel_noise = m_ckboxChannelNoise->GetValue(); -} - - -void OptionsDlg::OnChooseVoiceKeyerWaveFile(wxCommandEvent& event) { - wxFileDialog openFileDialog( - this, - wxT("Voice Keyer wave file"), - wxGetApp().m_txtVoiceKeyerWaveFilePath, - wxEmptyString, - wxT("WAV files (*.wav)|*.wav"), - wxFD_OPEN - ); - if(openFileDialog.ShowModal() == wxID_CANCEL) { - return; // the user changed their mind... - } - - wxString fileName, extension; - wxGetApp().m_txtVoiceKeyerWaveFile = openFileDialog.GetPath(); - wxFileName::SplitPath(wxGetApp().m_txtVoiceKeyerWaveFile, &wxGetApp().m_txtVoiceKeyerWaveFilePath, &fileName, &extension); - m_txtCtrlVoiceKeyerWaveFile->SetValue(wxGetApp().m_txtVoiceKeyerWaveFile); -} - -// Run time update of carrier amplitude attenuation - -void OptionsDlg::OnAttnCarrierEn(wxScrollEvent& event) { - long attn_carrier; - m_txtAttnCarrier->GetValue().ToLong(&attn_carrier); - wxGetApp().m_attn_carrier = (int)attn_carrier; - - /* uncheck -> checked, attenuate selected carrier */ - - if (m_ckboxAttnCarrierEn->GetValue() && !wxGetApp().m_attn_carrier_en) { - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C) { - freedv_set_carrier_ampl(g_pfreedv, wxGetApp().m_attn_carrier, 0.25); - } else { - wxMessageBox("Carrier attenuation feature only works on 700C", wxT("Warning"), wxOK | wxICON_WARNING, this); - } - } - - /* checked -> unchecked, reset selected carrier */ - - if (!m_ckboxAttnCarrierEn->GetValue() && wxGetApp().m_attn_carrier_en) { - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C) { - freedv_set_carrier_ampl(g_pfreedv, wxGetApp().m_attn_carrier, 1.0); - } - } - - wxGetApp().m_attn_carrier_en = m_ckboxAttnCarrierEn->GetValue(); -} - -void OptionsDlg::OnFreeDV700txClip(wxScrollEvent& event) { - wxGetApp().m_FreeDV700txClip = m_ckboxFreeDV700txClip->GetValue(); -} - -void OptionsDlg::OnFreeDV700Combine(wxScrollEvent& event) { - wxGetApp().m_FreeDV700Combine = m_ckboxFreeDV700Combine->GetValue(); -} - -void OptionsDlg::updateEventLog(wxString event_in, wxString event_out) { - wxString event_in_with_serial, event_out_with_serial; - event_in_with_serial.Printf(_T("[%d] %s"), event_in_serial++, event_in); - event_out_with_serial.Printf(_T("[%d] %s"), event_out_serial++, event_out); - - m_txt_events_in->AppendText(event_in_with_serial+"\n"); - m_txt_events_out->AppendText(event_out_with_serial+"\n"); -} - - -void OptionsDlg::OnDebugConsole(wxScrollEvent& event) { - wxGetApp().m_debug_console = m_ckboxDebugConsole->GetValue(); -#ifdef __WXMSW__ - // somewhere to send printfs while developing, causes conmsole to pop up on Windows - if (wxGetApp().m_debug_console) { - int ret = AllocConsole(); - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - fprintf(stderr, "AllocConsole: %d m_debug_console: %d\n", ret, wxGetApp().m_debug_console); - } -#endif -} diff --git a/freedv/tags/1.2.2/src/dlg_options.h b/freedv/tags/1.2.2/src/dlg_options.h deleted file mode 100644 index 081448cb..00000000 --- a/freedv/tags/1.2.2/src/dlg_options.h +++ /dev/null @@ -1,137 +0,0 @@ -//========================================================================== -// Name: dlg_options.h -// Purpose: Dialog for controlling misc FreeDV options -// Created: Nov 25 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#ifndef __OPTIONS_DIALOG__ -#define __OPTIONS_DIALOG__ - -#include "fdmdv2_main.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class OptionsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class OptionsDlg : public wxDialog -{ - public: - OptionsDlg( wxWindow* parent, - wxWindowID id = wxID_ANY, const wxString& title = _("Options"), - const wxPoint& pos = wxDefaultPosition, -#ifdef __WXMSW__ - /* we add debug console check box for windows */ - const wxSize& size = wxSize(600,410), -#else - const wxSize& size = wxSize(600,380), -#endif - long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~OptionsDlg(); - - void ExchangeData(int inout, bool storePersistent); - void updateEventLog(wxString event_in, wxString event_out); - - bool enableEventsChecked() {return m_ckbox_events->GetValue();} - - void SetSpamTimerLight(bool state) { - - // Colours don't work on Windows - - if (state) { - m_rb_spam_timer->SetForegroundColour( wxColour( 255,0 , 0 ) ); // red - m_rb_spam_timer->SetValue(true); - } - else { - m_rb_spam_timer->SetForegroundColour( wxColour( 0, 255, 0 ) ); // green - m_rb_spam_timer->SetValue(false); - } - } - - - protected: - - // Handlers for events. - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnApply(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void OnInitDialog(wxInitDialogEvent& event); - - void OnTestFrame(wxScrollEvent& event); - void OnChannelNoise(wxScrollEvent& event); - void OnAttnCarrierEn(wxScrollEvent& event); - void OnFreeDV700txClip(wxScrollEvent& event); - void OnFreeDV700Combine(wxScrollEvent& event); - void OnDebugConsole(wxScrollEvent& event); - - wxTextCtrl *m_txtCtrlCallSign; // TODO: this should be renamed to tx_txtmsg, and rename all related incl persis strge - - wxCheckBox* m_ckHalfDuplex; - - /* Voice Keyer */ - - wxButton *m_buttonChooseVoiceKeyerWaveFile; - wxTextCtrl *m_txtCtrlVoiceKeyerWaveFile; - wxTextCtrl *m_txtCtrlVoiceKeyerRxPause; - wxTextCtrl *m_txtCtrlVoiceKeyerRepeats; - - /* test frames, other simulated channel impairments */ - - wxCheckBox *m_ckboxTestFrame; - wxCheckBox *m_ckboxChannelNoise; - wxTextCtrl *m_txtNoiseSNR; - wxCheckBox *m_ckboxAttnCarrierEn; - wxTextCtrl *m_txtAttnCarrier; - - wxCheckBox *m_ckboxTone; - wxTextCtrl *m_txtToneFreqHz; - wxTextCtrl *m_txtToneAmplitude; - - wxCheckBox *m_ckboxFreeDV700txClip; - wxCheckBox *m_ckboxFreeDV700Combine; - - wxRadioButton *m_rb_textEncoding1; - wxRadioButton *m_rb_textEncoding2; - wxCheckBox *m_ckboxEnableChecksum; - - wxCheckBox *m_ckbox_events; - wxTextCtrl *m_txt_events_regexp_match; - wxTextCtrl *m_txt_events_regexp_replace; - wxTextCtrl *m_txt_events_in; - wxTextCtrl *m_txt_events_out; - wxTextCtrl *m_txt_spam_timer; - wxRadioButton *m_rb_spam_timer; - - wxCheckBox *m_ckbox_udp_enable; - wxTextCtrl *m_txt_udp_port; - - wxButton* m_sdbSizer5OK; - wxButton* m_sdbSizer5Cancel; - wxButton* m_sdbSizer5Apply; - - wxCheckBox *m_ckboxDebugConsole; - - unsigned int event_in_serial, event_out_serial; - - void OnChooseVoiceKeyerWaveFile(wxCommandEvent& event); - - private: -}; - -#endif // __OPTIONS_DIALOG__ diff --git a/freedv/tags/1.2.2/src/dlg_plugin.cpp b/freedv/tags/1.2.2/src/dlg_plugin.cpp deleted file mode 100644 index 68610f42..00000000 --- a/freedv/tags/1.2.2/src/dlg_plugin.cpp +++ /dev/null @@ -1,148 +0,0 @@ -//========================================================================== -// Name: dlg_plugin.cpp -// Purpose: Subclasses dialog GUI for PlugIn Config. Creates simple -// wxWidgets dialog GUI to set a few text strings. -// Date: Jan 2016 -// Authors: David Rowe -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "dlg_plugin.h" -#include "fdmdv2_main.h" - -#ifdef __WIN32__ -#include -#endif -#if defined(__FreeBSD__) || defined(__WXOSX__) -#include -#include -#endif - -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlugInDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlugInDlg::PlugInDlg(const wxString& title, int numParam, wxString paramName[], wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - m_name = title; - m_numParam = numParam; - assert(m_numParam <= PLUGIN_MAX_PARAMS); - - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - this->SetSizer(mainSizer); - - int i; - for (i=0; iAdd(m_txtCtrlParam[i], 0, 0, 5); - mainSizer->Add(staticBoxSizer28a, 0, wxEXPAND, 5); - } - - //---------------------------------------------------------------------- - // OK - Cancel - Apply - //---------------------------------------------------------------------- - - wxBoxSizer* boxSizer12 = new wxBoxSizer(wxHORIZONTAL); - - m_buttonOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxSize(-1,-1), 0); - m_buttonOK->SetDefault(); - boxSizer12->Add(m_buttonOK, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonCancel, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - mainSizer->Add(boxSizer12, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL, 5); - - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - Centre(wxBOTH); - - // Connect events - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(PlugInDlg::OnInitDialog), NULL, this); - m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnOK), NULL, this); - m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnCancel), NULL, this); - -} - -//------------------------------------------------------------------------- -// ~PlugInDlg() -//------------------------------------------------------------------------- -PlugInDlg::~PlugInDlg() -{ - // Disconnect Events - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(PlugInDlg::OnInitDialog), NULL, this); - m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnOK), NULL, this); - m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PlugInDlg::OnCancel), NULL, this); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void PlugInDlg::OnInitDialog(wxInitDialogEvent& event) -{ - ExchangeData(EXCHANGE_DATA_IN); -} - - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void PlugInDlg::ExchangeData(int inout) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - wxString str; - int i; - - if(inout == EXCHANGE_DATA_IN) - { - for (i=0; iSetValue(wxGetApp().m_txtPlugInParam[i]); - } - } - if(inout == EXCHANGE_DATA_OUT) - { - for (i=0; iGetValue(); - wxString configStr = "/" + m_name + "/" + m_paramName[i]; - pConfig->Write(configStr, wxGetApp().m_txtPlugInParam[i]); - } - pConfig->Flush(); - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void PlugInDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void PlugInDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - this->EndModal(wxID_OK); -} diff --git a/freedv/tags/1.2.2/src/dlg_plugin.h b/freedv/tags/1.2.2/src/dlg_plugin.h deleted file mode 100644 index 72efc7bb..00000000 --- a/freedv/tags/1.2.2/src/dlg_plugin.h +++ /dev/null @@ -1,65 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.h -// Purpose: Subclasses dialog GUI for PTT Config. -// -// Created: May. 11, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __PLUGIN_DIALOG__ -#define __PLUGIN_DIALOG__ - -#include "fdmdv2_main.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlugInDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlugInDlg : public wxDialog -{ - public: - PlugInDlg(const wxString& title = _("PTT Config"), int numParam = 0, wxString paramNames[]=NULL, wxWindow* parent=NULL, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(450,300), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); - virtual ~PlugInDlg(); - void ExchangeData(int inout); - - protected: - wxString m_name; - int m_numParam; - wxString m_paramName[PLUGIN_MAX_PARAMS]; - - wxTextCtrl* m_txtCtrlParam[PLUGIN_MAX_PARAMS]; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - - -protected: - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - virtual void OnInitDialog(wxInitDialogEvent& event); -}; - -#endif // __PLUGIN_DIALOG__ diff --git a/freedv/tags/1.2.2/src/dlg_ptt.cpp b/freedv/tags/1.2.2/src/dlg_ptt.cpp deleted file mode 100644 index 1a04c9c4..00000000 --- a/freedv/tags/1.2.2/src/dlg_ptt.cpp +++ /dev/null @@ -1,570 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.cpp -// Purpose: Subclasses dialog GUI for PTT Config. Creates simple -// wxWidgets dialog GUI to select real/virtual Comm ports. -// Date: May 11 2012 -// Authors: David Rowe, David Witten, Joel Stanley -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "dlg_ptt.h" -#include "fdmdv2_main.h" - -#ifdef __WIN32__ -#include -#endif -#if defined(__FreeBSD__) || defined(__WXOSX__) -#include -#include -#endif - -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class ComPortsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -ComPortsDlg::ComPortsDlg(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) -{ - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - this->SetSizer(mainSizer); - - //---------------------------------------------------------------------- - // Vox tone option - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer28 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("VOX PTT Settings")), wxHORIZONTAL); - m_ckLeftChannelVoxTone = new wxCheckBox(this, wxID_ANY, _("Left Channel Vox Tone"), wxDefaultPosition, wxSize(-1,-1), 0); - staticBoxSizer28->Add(m_ckLeftChannelVoxTone, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5); - - mainSizer->Add(staticBoxSizer28, 0, wxEXPAND, 5); - - //---------------------------------------------------------------------- - // Hamlib for CAT PTT - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer18 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Hamlib Settings")), wxHORIZONTAL); - wxGridSizer* gridSizerhl = new wxGridSizer(5, 2, 0, 0); - staticBoxSizer18->Add(gridSizerhl, 1, wxEXPAND|wxALIGN_LEFT, 5); - - /* Use Hamlib for PTT checkbox. */ - - m_ckUseHamlibPTT = new wxCheckBox(this, wxID_ANY, _("Use Hamlib PTT"), wxDefaultPosition, wxSize(-1, -1), 0); - m_ckUseHamlibPTT->SetValue(false); - gridSizerhl->Add(m_ckUseHamlibPTT, 0, wxALIGN_CENTER_VERTICAL, 0); - gridSizerhl->Add(new wxStaticText(this, -1, wxT("")), 0, wxEXPAND); - - /* Hamlib Rig Type combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Rig Model:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbRigName = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1), 0, NULL, wxCB_DROPDOWN); - wxGetApp().m_hamlib->populateComboBox(m_cbRigName); - m_cbRigName->SetSelection(wxGetApp().m_intHamlibRig); - gridSizerhl->Add(m_cbRigName, 0, wxALIGN_CENTER_VERTICAL, 0); - - /* Hamlib Serial Port combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Device:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialPort = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizerhl->Add(m_cbSerialPort, 0, wxALIGN_CENTER_VERTICAL, 0); - - /* Hamlib Serial Rate combobox. */ - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Rate:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialRate = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizerhl->Add(m_cbSerialRate, 0, wxALIGN_CENTER_VERTICAL, 0); - - gridSizerhl->Add(new wxStaticText(this, wxID_ANY, _("Serial Params:"), wxDefaultPosition, wxDefaultSize, 0), - 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 20); - m_cbSerialParams = new wxStaticText(this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, 0); - gridSizerhl->Add(m_cbSerialParams, 0, wxALIGN_CENTER_VERTICAL, 0); - - mainSizer->Add(staticBoxSizer18, 0, wxEXPAND, 5); - - //---------------------------------------------------------------------- - // Serial port PTT - //---------------------------------------------------------------------- - - wxStaticBoxSizer* staticBoxSizer17 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Serial Port Settings")), wxVERTICAL); - mainSizer->Add(staticBoxSizer17, 1, wxEXPAND, 5); - wxStaticBoxSizer* staticBoxSizer31 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("PTT Port")), wxVERTICAL); - staticBoxSizer17->Add(staticBoxSizer31, 1, wxEXPAND, 5); - -#ifdef __WXMSW__ - m_ckUseSerialPTT = new wxCheckBox(this, wxID_ANY, _("Use Serial Port PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckUseSerialPTT->SetValue(false); - staticBoxSizer31->Add(m_ckUseSerialPTT, 0, wxALIGN_LEFT, 20); - - wxArrayString m_listCtrlPortsArr; - m_listCtrlPorts = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1,45), m_listCtrlPortsArr, wxLB_SINGLE | wxLB_SORT); - staticBoxSizer31->Add(m_listCtrlPorts, 1, wxALIGN_CENTER, 0); -#endif - -#if defined(__WXOSX__) || defined(__WXGTK__) - wxBoxSizer* bSizer83; - bSizer83 = new wxBoxSizer(wxHORIZONTAL); - - wxGridSizer* gridSizer200 = new wxGridSizer(1, 3, 0, 0); - - m_ckUseSerialPTT = new wxCheckBox(this, wxID_ANY, _("Use Serial Port PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckUseSerialPTT->SetValue(false); - gridSizer200->Add(m_ckUseSerialPTT, 1, wxALIGN_CENTER|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 2); - - m_staticText12 = new wxStaticText(this, wxID_ANY, _("Serial Device: "), wxDefaultPosition, wxDefaultSize, 0); - m_staticText12->Wrap(-1); - gridSizer200->Add(m_staticText12, 1,wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 2); - - m_cbCtlDevicePath = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(140, -1), 0, NULL, wxCB_DROPDOWN); - gridSizer200->Add(m_cbCtlDevicePath, 1, wxEXPAND|wxALIGN_CENTER|wxALIGN_RIGHT, 2); - - bSizer83->Add(gridSizer200, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 2); - staticBoxSizer31->Add(bSizer83, 1, wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - wxBoxSizer* boxSizer19 = new wxBoxSizer(wxVERTICAL); - staticBoxSizer17->Add(boxSizer19, 1, wxEXPAND, 5); - wxStaticBoxSizer* staticBoxSizer16 = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY, _("Signal polarity")), wxHORIZONTAL); - boxSizer19->Add(staticBoxSizer16, 1, wxEXPAND|wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - wxGridSizer* gridSizer17 = new wxGridSizer(2, 2, 0, 0); - staticBoxSizer16->Add(gridSizer17, 1, wxEXPAND|wxALIGN_RIGHT, 5); - - m_rbUseDTR = new wxRadioButton(this, wxID_ANY, _("Use DTR"), wxDefaultPosition, wxSize(-1,-1), 0); - m_rbUseDTR->SetToolTip(_("Toggle DTR line for PTT")); - m_rbUseDTR->SetValue(1); - gridSizer17->Add(m_rbUseDTR, 0, wxALIGN_CENTER|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5); - - m_ckDTRPos = new wxCheckBox(this, wxID_ANY, _("DTR = +V"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckDTRPos->SetToolTip(_("Set Polarity of the DTR line")); - m_ckDTRPos->SetValue(false); - gridSizer17->Add(m_ckDTRPos, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - m_rbUseRTS = new wxRadioButton(this, wxID_ANY, _("Use RTS"), wxDefaultPosition, wxSize(-1,-1), 0); - m_rbUseRTS->SetToolTip(_("Toggle the RTS pin for PTT")); - m_rbUseRTS->SetValue(1); - gridSizer17->Add(m_rbUseRTS, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - m_ckRTSPos = new wxCheckBox(this, wxID_ANY, _("RTS = +V"), wxDefaultPosition, wxSize(-1,-1), 0); - m_ckRTSPos->SetValue(false); - m_ckRTSPos->SetToolTip(_("Set Polarity of the RTS line")); - gridSizer17->Add(m_ckRTSPos, 0, wxALIGN_CENTER|wxALIGN_RIGHT, 5); - - //---------------------------------------------------------------------- - // OK - Cancel - Apply - //---------------------------------------------------------------------- - - wxBoxSizer* boxSizer12 = new wxBoxSizer(wxHORIZONTAL); - - m_buttonTest = new wxButton(this, wxID_APPLY, _("Test PTT"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonTest, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxSize(-1,-1), 0); - m_buttonOK->SetDefault(); - boxSizer12->Add(m_buttonOK, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonCancel, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - m_buttonApply = new wxButton(this, wxID_APPLY, _("Apply"), wxDefaultPosition, wxSize(-1,-1), 0); - boxSizer12->Add(m_buttonApply, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM, 5); - - mainSizer->Add(boxSizer12, 0, wxLEFT|wxRIGHT|wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL, 5); - - if ( GetSizer() ) - { - GetSizer()->Fit(this); - } - Centre(wxBOTH); - - // Connect events - this->Connect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(ComPortsDlg::OnInitDialog), NULL, this); - m_ckUseHamlibPTT->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseHamLibClicked), NULL, this); - m_ckUseSerialPTT->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseSerialClicked), NULL, this); - m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnOK), NULL, this); - m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnCancel), NULL, this); - m_buttonApply->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnApply), NULL, this); - m_buttonTest->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnTest), NULL, this); -} - -//------------------------------------------------------------------------- -// ~ComPortsDlg() -//------------------------------------------------------------------------- -ComPortsDlg::~ComPortsDlg() -{ - // Disconnect Events - this->Disconnect(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(ComPortsDlg::OnInitDialog), NULL, this); - m_ckUseHamlibPTT->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseHamLibClicked), NULL, this); - m_ckUseSerialPTT->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(ComPortsDlg::PTTUseSerialClicked), NULL, this); - m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnOK), NULL, this); - m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnCancel), NULL, this); - m_buttonApply->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnApply), NULL, this); - m_buttonTest->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ComPortsDlg::OnTest), NULL, this); -} - -//------------------------------------------------------------------------- -// OnInitDialog() -//------------------------------------------------------------------------- -void ComPortsDlg::OnInitDialog(wxInitDialogEvent& event) -{ - populatePortList(); - ExchangeData(EXCHANGE_DATA_IN); -} - -//------------------------------------------------------------------------- -// populatePortList() -//------------------------------------------------------------------------- -void ComPortsDlg::populatePortList() -{ - - /* populate Hamlib serial rate combo box */ - - wxString serialRates[] = {"default", "300", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"}; - for(int i=0; iAppend(serialRates[i]); - } - -#ifdef __WXMSW__ - m_listCtrlPorts->Clear(); - m_cbSerialPort->Clear(); - wxArrayString aStr; - wxRegKey key(wxRegKey::HKLM, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM")); - if(!key.Exists()) - { - return; - } - else - { - // Get the number of subkeys and enumerate them. - if(!key.Open(wxRegKey::Read)) - { - return; - } - size_t subkeys; - size_t values; - if(!key.GetKeyInfo(&subkeys, NULL, &values, NULL)) - { - return; - } - if(!key.HasValues()) - { - return; - } - wxString key_name; - long el = 1; - key.GetFirstValue(key_name, el); - wxString valType; - wxString key_data; - for(unsigned int i = 0; i < values; i++) - { - key.QueryValue(key_name, key_data); - //wxPrintf("Value: %s Data: %s\n", key_name, key_data); - aStr.Add(key_data, 1); - key.GetNextValue(key_name, el); - } - } - m_listCtrlPorts->Append(aStr); - m_cbSerialPort->Append(aStr); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - m_cbSerialPort->Clear(); - m_cbCtlDevicePath->Clear(); -#if defined(__FreeBSD__) || defined(__WXOSX__) - glob_t gl; -#ifdef __FreeBSD__ - if(glob("/dev/tty*", GLOB_MARK, NULL, &gl)==0) { -#else - if(glob("/dev/tty.*", GLOB_MARK, NULL, &gl)==0) { -#endif - for(unsigned int i=0; i= 'l' && gl.gl_pathv[i][8] <= 's') - continue; - if(gl.gl_pathv[i][8] >= 'L' && gl.gl_pathv[i][8] <= 'S') - continue; - - /* Exclude virtual TTYs */ - if(gl.gl_pathv[i][8] == 'v') - continue; - - /* Exclude initial-state and lock-state devices */ -#ifndef __WXOSX__ - if(strchr(gl.gl_pathv[i], '.') != NULL) - continue; -#endif - - m_cbSerialPort->Append(gl.gl_pathv[i]); - m_cbCtlDevicePath->Append(gl.gl_pathv[i]); - } - globfree(&gl); - } -#else - /* TODO(Joel): http://stackoverflow.com/questions/2530096/how-to-find-all-serial-devices-ttys-ttyusb-on-linux-without-opening-them */ - m_cbSerialPort->Append("/dev/ttyUSB0"); - m_cbSerialPort->Append("/dev/ttyUSB1"); - m_cbSerialPort->Append("/dev/ttyS0"); - m_cbSerialPort->Append("/dev/ttyS1"); - - m_cbCtlDevicePath->Clear(); - m_cbCtlDevicePath->Append("/dev/ttyUSB0"); - m_cbCtlDevicePath->Append("/dev/ttyUSB1"); - m_cbCtlDevicePath->Append("/dev/ttyS0"); - m_cbCtlDevicePath->Append("/dev/ttyS1"); -#endif -#endif - - -} - -//------------------------------------------------------------------------- -// ExchangeData() -//------------------------------------------------------------------------- -void ComPortsDlg::ExchangeData(int inout) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - wxString str; - - if(inout == EXCHANGE_DATA_IN) { - m_ckLeftChannelVoxTone->SetValue(wxGetApp().m_leftChannelVoxTone); - - /* Hamlib */ - - m_ckUseHamlibPTT->SetValue(wxGetApp().m_boolHamlibUseForPTT); - m_cbRigName->SetSelection(wxGetApp().m_intHamlibRig); - m_cbSerialPort->SetValue(wxGetApp().m_strHamlibSerialPort); - - if (wxGetApp().m_intHamlibSerialRate == 0) { - m_cbSerialRate->SetSelection(0); - } else { - m_cbSerialRate->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_intHamlibSerialRate)); - } - - /* Serial PTT */ - - m_ckUseSerialPTT->SetValue(wxGetApp().m_boolUseSerialPTT); - str = wxGetApp().m_strRigCtrlPort; -#ifdef __WXMSW__ - m_listCtrlPorts->SetStringSelection(str); -#endif -#if defined(__WXOSX__) || defined(__WXGTK__) - m_cbCtlDevicePath->SetValue(str); -#endif - m_rbUseRTS->SetValue(wxGetApp().m_boolUseRTS); - m_ckRTSPos->SetValue(wxGetApp().m_boolRTSPos); - m_rbUseDTR->SetValue(wxGetApp().m_boolUseDTR); - m_ckDTRPos->SetValue(wxGetApp().m_boolDTRPos); - } - - if (inout == EXCHANGE_DATA_OUT) { - wxGetApp().m_leftChannelVoxTone = m_ckLeftChannelVoxTone->GetValue(); - pConfig->Write(wxT("/Rig/leftChannelVoxTone"), wxGetApp().m_leftChannelVoxTone); - - /* Hamlib settings. */ - - wxGetApp().m_boolHamlibUseForPTT = m_ckUseHamlibPTT->GetValue(); - wxGetApp().m_intHamlibRig = m_cbRigName->GetSelection(); - wxGetApp().m_strHamlibSerialPort = m_cbSerialPort->GetValue(); - - wxString s = m_cbSerialRate->GetValue(); - if (s == "default") { - wxGetApp().m_intHamlibSerialRate = 0; - } else { - long tmp; - m_cbSerialRate->GetValue().ToLong(&tmp); - wxGetApp().m_intHamlibSerialRate = tmp; - } - fprintf(stderr, "serial rate: %d\n", wxGetApp().m_intHamlibSerialRate); - - pConfig->Write(wxT("/Hamlib/UseForPTT"), wxGetApp().m_boolHamlibUseForPTT); - pConfig->Write(wxT("/Hamlib/RigName"), wxGetApp().m_intHamlibRig); - pConfig->Write(wxT("/Hamlib/SerialPort"), wxGetApp().m_strHamlibSerialPort); - pConfig->Write(wxT("/Hamlib/SerialRate"), wxGetApp().m_intHamlibSerialRate); - - /* Serial settings */ - - wxGetApp().m_boolUseSerialPTT = m_ckUseSerialPTT->IsChecked(); -#ifdef __WXMSW__ - wxGetApp().m_strRigCtrlPort = m_listCtrlPorts->GetStringSelection(); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - wxGetApp().m_strRigCtrlPort = m_cbCtlDevicePath->GetValue(); -#endif - wxGetApp().m_boolUseRTS = m_rbUseRTS->GetValue(); - wxGetApp().m_boolRTSPos = m_ckRTSPos->IsChecked(); - wxGetApp().m_boolUseDTR = m_rbUseDTR->GetValue(); - wxGetApp().m_boolDTRPos = m_ckDTRPos->IsChecked(); - - pConfig->Write(wxT("/Rig/UseSerialPTT"), wxGetApp().m_boolUseSerialPTT); - pConfig->Write(wxT("/Rig/Port"), wxGetApp().m_strRigCtrlPort); - pConfig->Write(wxT("/Rig/UseRTS"), wxGetApp().m_boolUseRTS); - pConfig->Write(wxT("/Rig/RTSPolarity"), wxGetApp().m_boolRTSPos); - pConfig->Write(wxT("/Rig/UseDTR"), wxGetApp().m_boolUseDTR); - pConfig->Write(wxT("/Rig/DTRPolarity"), wxGetApp().m_boolDTRPos); - - pConfig->Flush(); - } - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - -//------------------------------------------------------------------------- -// PTTUseHamLibClicked() -//------------------------------------------------------------------------- -void ComPortsDlg::PTTUseHamLibClicked(wxCommandEvent& event) -{ - m_ckUseSerialPTT->SetValue(false); -} - - -/* Attempt to toggle PTT for 1 second */ - -void ComPortsDlg::OnTest(wxCommandEvent& event) { - - /* Tone PTT */ - - if (m_ckLeftChannelVoxTone->GetValue()) { - wxMessageBox("Testing of tone based PTT not supported; try PTT after pressing Start on main window", - wxT("Error"), wxOK | wxICON_ERROR, this); - } - - /* Hamlib PTT */ - - if (m_ckUseHamlibPTT->GetValue()) { - - // set up current hamlib config from GUI - - int rig = m_cbRigName->GetSelection(); - wxString port = m_cbSerialPort->GetValue(); - wxString s = m_cbSerialRate->GetValue(); - int serial_rate; - if (s == "default") { - serial_rate = 0; - } else { - long tmp; - m_cbSerialRate->GetValue().ToLong(&tmp); - serial_rate = tmp; - } - - // display serial params - - fprintf(stderr, "serial rate: %d\n", serial_rate); - - // try to open rig - - Hamlib *hamlib = wxGetApp().m_hamlib; - bool status = hamlib->connect(rig, port.mb_str(wxConvUTF8), serial_rate); - if (status == false) { - wxMessageBox("Couldn't connect to Radio with hamlib", wxT("Error"), wxOK | wxICON_ERROR, this); - return; - } - else { - wxString hamlib_serial_config; - hamlib_serial_config.sprintf(" %d, %d, %d", - hamlib->get_serial_rate(), - hamlib->get_data_bits(), - hamlib->get_stop_bits()); - m_cbSerialParams->SetLabel(hamlib_serial_config); - } - - // toggle PTT - - wxString hamlibError; - if (hamlib->ptt(true, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - return; - } - - wxSleep(1); - - if (hamlib->ptt(false, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - } - - /* Serial PTT */ - - if (m_ckUseSerialPTT->IsChecked()) { - Serialport *serialport = wxGetApp().m_serialport; - - wxString ctrlport; -#ifdef __WXMSW__ - ctrlport = m_listCtrlPorts->GetStringSelection(); -#endif -#if defined(__WXGTK__) || defined(__WXOSX__) - ctrlport = m_cbCtlDevicePath->GetValue(); -#endif - fprintf(stderr, "opening serial port\n"); - - bool success = serialport->openport(ctrlport.c_str(), - m_rbUseRTS->GetValue(), - m_ckRTSPos->IsChecked(), - m_rbUseDTR->GetValue(), - m_ckDTRPos->IsChecked()); - - fprintf(stderr, "serial port open\n"); - - if (!success) { - wxMessageBox("Couldn't open serial port", wxT("Error"), wxOK | wxICON_ERROR, this); - } - - // assert PTT port for 1 sec - - serialport->ptt(true); - wxSleep(1); - serialport->ptt(false); - - fprintf(stderr, "closing serial port\n"); - serialport->closeport(); - fprintf(stderr, "serial port closed\n"); - } - -} - - -//------------------------------------------------------------------------- -// PTTUseSerialClicked() -//------------------------------------------------------------------------- -void ComPortsDlg::PTTUseSerialClicked(wxCommandEvent& event) -{ - m_ckUseHamlibPTT->SetValue(false); -} - -//------------------------------------------------------------------------- -// OnApply() -//------------------------------------------------------------------------- -void ComPortsDlg::OnApply(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); -} - -//------------------------------------------------------------------------- -// OnCancel() -//------------------------------------------------------------------------- -void ComPortsDlg::OnCancel(wxCommandEvent& event) -{ - this->EndModal(wxID_CANCEL); -} - -//------------------------------------------------------------------------- -// OnClose() -//------------------------------------------------------------------------- -void ComPortsDlg::OnOK(wxCommandEvent& event) -{ - ExchangeData(EXCHANGE_DATA_OUT); - this->EndModal(wxID_OK); -} diff --git a/freedv/tags/1.2.2/src/dlg_ptt.h b/freedv/tags/1.2.2/src/dlg_ptt.h deleted file mode 100644 index 26ab17e8..00000000 --- a/freedv/tags/1.2.2/src/dlg_ptt.h +++ /dev/null @@ -1,93 +0,0 @@ -//========================================================================== -// Name: dlg_ptt.h -// Purpose: Subclasses dialog GUI for PTT Config. -// -// Created: May. 11, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __COMPORTS_DIALOG__ -#define __COMPORTS_DIALOG__ - -#include "fdmdv2_main.h" -#include "hamlib.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class ComPortsDlg -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class ComPortsDlg : public wxDialog -{ - public: - ComPortsDlg(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("PTT Config"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(450,300), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); - virtual ~ComPortsDlg(); - void ExchangeData(int inout); - - protected: - wxCheckBox* m_ckLeftChannelVoxTone; - - /* Hamlib settings.*/ - - wxCheckBox *m_ckUseHamlibPTT; - wxComboBox *m_cbRigName; - wxComboBox *m_cbSerialPort; - wxStaticText *m_cbSerialParams; - Hamlib *m_hamlib; - - /* Serial Settings */ - - wxListBox *m_listCtrlPorts; - wxCheckBox *m_ckUseSerialPTT; - wxStaticText *m_staticText12; - wxComboBox *m_cbCtlDevicePath; - wxRadioButton *m_rbUseDTR; - wxCheckBox *m_ckRTSPos; - wxRadioButton *m_rbUseRTS; - wxCheckBox *m_ckDTRPos; - - /* Test - Ok - Cancel - Apply */ - - wxButton* m_buttonTest; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - wxButton* m_buttonApply; - - -protected: - void populatePortList(); - - void PTTUseHamLibClicked(wxCommandEvent& event); - void PTTUseSerialClicked(wxCommandEvent& event); - - void OnTest(wxCommandEvent& event); - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnApply(wxCommandEvent& event); - virtual void OnInitDialog(wxInitDialogEvent& event); -}; - -#endif // __COMPORTS_DIALOG__ diff --git a/freedv/tags/1.2.2/src/fdmdv2_defines.h b/freedv/tags/1.2.2/src/fdmdv2_defines.h deleted file mode 100644 index a6878890..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_defines.h +++ /dev/null @@ -1,106 +0,0 @@ -//========================================================================== -// Name: fdmdv2_defines.h -// Purpose: Definitions used by plots derived from fdmdv2_plot class. -// Created: August 27, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_DEFINES__ -#define __FDMDV2_DEFINES__ - -#include "wx/wx.h" -#include "freedv_api.h" -#include "modem_stats.h" - -// Spectrogram and Waterfall - -#define MIN_MAG_DB -40.0 // min of spectrogram/waterfall magnitude axis -#define MAX_MAG_DB 0.0 // max of spectrogram/waterfall magnitude axis -#define STEP_MAG_DB 5.0 // magnitude axis step -#define BETA 0.95 // constant for time averaging spectrum data -#define MIN_F_HZ 0 // min freq on Waterfall and Spectrum -#define MAX_F_HZ 3000 // max freq on Waterfall and Spectrum -#define STEP_F_HZ 500 // major (e.g. text legend) freq step on Waterfall and Spectrum graticule -#define STEP_MINOR_F_HZ 100 // minor (ticks) freq step on Waterfall and Spectrum graticule -#define WATERFALL_SECS_Y 30 // number of seconds respresented by y axis of waterfall -#define WATERFALL_SECS_STEP 5 // graticule y axis steps of waterfall -#define DT 0.1 // time between real time graphing updates -#define FS 8000 // FDMDV modem sample rate - -// Scatter diagram - -#define SCATTER_MEM_SECS 10 -// (symbols/frame)/(graphics update period) = symbols/s sent to scatter memory -// memory (symbols) = secs of memory * symbols/sec -#define SCATTER_MEM_SYMS_MAX ((int)(SCATTER_MEM_SECS*((MODEM_STATS_NC_MAX+1)/DT))) -#define SCATTER_EYE_MEM_ROWS ((int)(SCATTER_MEM_SECS/DT)) - -// Waveform plotting constants - -#define WAVEFORM_PLOT_FS 400 // sample rate (points/s) of waveform plotted to screen -#define WAVEFORM_PLOT_TIME 5 // length or entire waveform on screen -#define WAVEFORM_PLOT_BUF ((int)(DT*WAVEFORM_PLOT_FS)) // number of new samples we plot per DT - -// sample rate I/O & conversion constants - -#define MAX_FPB 8096 // maximum value of portAudio framesPerBuffer -#define PA_FPB 1024 // nominal value of portAudio framesPerBuffer -#define SAMPLE_RATE 48000 // 48 kHz sampling rate rec. as we can trust accuracy of sound card -#define N8 160 // processing buffer size at 8 kHz -#define MEM8 (FDMDV_OS_TAPS/FDMDV_OS) -#define N48 (N8*SAMPLE_RATE/FS) // processing buffer size at 48 kHz -#define NUM_CHANNELS 2 // I think most sound cards prefer stereo we will convert to mono -#define VOX_TONE_FREQ 1000.0 // optional left channel vox tone freq -#define VOX_TONE_AMP 30000 // optional left channel vox tone amp - -#define MAX_BITS_PER_CODEC_FRAME 64 // 1600 bit/s mode -#define MAX_BYTES_PER_CODEC_FRAME (MAX_BITS_PER_CODEC_FRAME/8) -#define MAX_BITS_PER_FDMDV_FRAME 40 // 2000 bit/s mode - -// Squelch -#define SQ_DEFAULT_SNR 2.0 - -// Level Gauge -#define FROM_RADIO_MAX 0.8 -#define FROM_MIC_MAX 0.8 -#define LEVEL_BETA 0.99 - -// SNR -#define SNRSLOW_BETA 0.5 // time constant for slow SNR for display - -// Text messaging Data -#define MAX_CALLSIGN 80 -#define MAX_EVENT_LOG 10 -#define MAX_EVENT_RULES 100 - -enum -{ - ID_ROTATE_LEFT = wxID_HIGHEST + 1, - ID_ROTATE_RIGHT, - ID_RESIZE, - ID_PAINT_BG -}; - -// Codec 2 LPC Post Filter defaults, from codec-dev/src/quantise.c - -#define CODEC2_LPC_PF_GAMMA 0.5 -#define CODEC2_LPC_PF_BETA 0.2 - -// PlugIns ... - -#define PLUGIN_MAX_PARAMS 4 - -#endif //__FDMDV2_DEFINES__ diff --git a/freedv/tags/1.2.2/src/fdmdv2_main.cpp b/freedv/tags/1.2.2/src/fdmdv2_main.cpp deleted file mode 100644 index 47a6e77c..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_main.cpp +++ /dev/null @@ -1,3943 +0,0 @@ -//========================================================================== -// Name: fdmdv2_main.cpp -// -// Purpose: FreeDV main() -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include "fdmdv2_main.h" - -#define wxUSE_FILEDLG 1 -#define wxUSE_LIBPNG 1 -#define wxUSE_LIBJPEG 1 -#define wxUSE_GIF 1 -#define wxUSE_PCX 1 -#define wxUSE_LIBTIFF 1 - -//------------------------------------------------------------------- -// Bunch of globals used for communication with sound card call -// back functions -// ------------------------------------------------------------------ - -int g_in, g_out; - -// Global Codec2 & modem states - just one reqd for tx & rx -int g_Nc; -int g_mode; -struct freedv *g_pfreedv; -struct MODEM_STATS g_stats; -float g_pwr_scale; -int g_clip; - -// test Frames -int g_testFrames; -int g_test_frame_sync_state; -int g_test_frame_count; -int g_total_bits; -int g_total_bit_errors; -int g_channel_noise; -float g_sig_pwr_av = 0.0; -struct FIFO *g_error_pattern_fifo; -short *g_error_hist; - -// time averaged magnitude spectrum used for waterfall and spectrum display -float g_avmag[MODEM_STATS_NSPEC]; - -// GUI controls that affect rx and tx processes -int g_SquelchActive; -float g_SquelchLevel; -int g_analog; -int g_split; -int g_tx; -float g_snr; -bool g_half_duplex; -bool g_modal; - -// sending and receiving Call Sign data -struct FIFO *g_txDataInFifo; -struct FIFO *g_rxDataOutFifo; - -// tx/rx processing states -int g_State; -paCallBackData *g_rxUserdata; - -// FIFOs used for plotting waveforms -struct FIFO *g_plotDemodInFifo; -struct FIFO *g_plotSpeechOutFifo; -struct FIFO *g_plotSpeechInFifo; - -// Soundcard config -int g_nSoundCards; -int g_soundCard1InDeviceNum; -int g_soundCard1OutDeviceNum; -int g_soundCard1SampleRate; -int g_soundCard2InDeviceNum; -int g_soundCard2OutDeviceNum; -int g_soundCard2SampleRate; - -// playing and recording from sound files - -SNDFILE *g_sfPlayFile; -bool g_playFileToMicIn; -bool g_loopPlayFileToMicIn; -int g_playFileToMicInEventId; - -SNDFILE *g_sfRecFile; -bool g_recFileFromRadio; -unsigned int g_recFromRadioSamples; -int g_recFileFromRadioEventId; - -SNDFILE *g_sfPlayFileFromRadio; -bool g_playFileFromRadio; -int g_sfFs; -bool g_loopPlayFileFromRadio; -int g_playFileFromRadioEventId; -float g_blink; - -wxWindow *g_parent; - -// Click to tune rx and tx frequency offset states -float g_RxFreqOffsetHz; -COMP g_RxFreqOffsetPhaseRect; -float g_TxFreqOffsetHz; -COMP g_TxFreqOffsetPhaseRect; - -// experimental mutex to make sound card callbacks mutually exclusive -// TODO: review code and see if we need this any more, as fifos should -// now be thread safe - -wxMutex g_mutexProtectingCallbackData; - -// Speex pre-processor states - -SpeexPreprocessState *g_speex_st; - -// WxWidgets - initialize the application -IMPLEMENT_APP(MainApp); - -//FILE *ftest; -FILE *g_logfile; - -//------------------------------------------------------------------------- -// OnInit() -//------------------------------------------------------------------------- -bool MainApp::OnInit() -{ - if(!wxApp::OnInit()) - { - return false; - } - SetVendorName(wxT("CODEC2-Project")); - SetAppName(wxT("FreeDV")); // not needed, it's the default value - -#ifdef FILE_RATHER_THAN_REGISTRY - // Force use of file-based configuration persistance on Windows platforma - wxConfig *pConfig = new wxConfig(); - wxFileConfig *pFConfig = new wxFileConfig(wxT("FreeDV"), wxT("CODEC2-Project"), wxT("FreeDV.conf"), wxT("FreeDV.conf"), wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH); - pConfig->Set(pFConfig); - pConfig->SetRecordDefaults(); -#else - wxConfigBase *pConfig = wxConfigBase::Get(); - pConfig->SetRecordDefaults(); -#endif - - m_rTopWindow = wxRect(0, 0, 0, 0); - m_strRxInAudio.Empty(); - m_strRxOutAudio.Empty(); - m_textVoiceInput.Empty(); - m_textVoiceOutput.Empty(); - m_strSampleRate.Empty(); - m_strBitrate.Empty(); - - // Look for Plug In - - m_plugIn = false; - #ifdef __WXMSW__ - wchar_t dll_path[] = L"afreedvplugin.dll"; - m_plugInHandle = LoadLibrary(dll_path); - #else - m_plugInHandle = dlopen("afreedvplugin.so", RTLD_LAZY); - #endif - - if (m_plugInHandle) { - printf("plugin: .so found\n"); - - // lets get some information abt the plugIn - - void (*plugin_namefp)(char s[]); - void *(*plugin_openfp)(char *param_names[], int *nparams, int (*aplugin_get_persistant)(char *, char *)); - - #ifdef __WXMSW__ - plugin_namefp = (void (*)(char*))GetProcAddress((HMODULE)m_plugInHandle, "plugin_name"); - plugin_openfp = (void* (*)(char**,int *, int (*)(char *, char *)))GetProcAddress((HMODULE)m_plugInHandle, "plugin_open"); - m_plugin_startfp = (void (*)(void *))GetProcAddress((HMODULE)m_plugInHandle, "plugin_start"); - m_plugin_stopfp = (void (*)(void *))GetProcAddress((HMODULE)m_plugInHandle, "plugin_stop"); - m_plugin_rx_samplesfp = (void (*)(void *, short *, int))GetProcAddress((HMODULE)m_plugInHandle, "plugin_rx_samples"); - #else - plugin_namefp = (void (*)(char*))dlsym(m_plugInHandle, "plugin_name"); - plugin_openfp = (void* (*)(char**,int *, int (*)(char *, char *)))dlsym(m_plugInHandle, "plugin_open"); - m_plugin_startfp = (void (*)(void *))dlsym(m_plugInHandle, "plugin_start"); - m_plugin_stopfp = (void (*)(void *))dlsym(m_plugInHandle, "plugin_stop"); - m_plugin_rx_samplesfp = (void (*)(void *, short *, int))dlsym(m_plugInHandle, "plugin_rx_samples"); - #endif - - if ((plugin_namefp != NULL) && (plugin_openfp != NULL)) { - - char s[256]; - m_plugIn = true; - (plugin_namefp)(s); - fprintf(stderr, "plugin name: %s\n", s); - m_plugInName = s; - - char param_name1[80], param_name2[80]; - char *param_names[2] = {param_name1, param_name2}; - int nparams, i; - m_plugInStates = (plugin_openfp)(param_names, &nparams, plugin_get_persistant); - m_numPlugInParam = nparams; - for(i=0; iRead(configStr, wxT("")); - //fprintf(stderr, " plugin param name[%d]: %s\n", i, param_names[i]); - fprintf(stderr, " plugin param name[%d]: %s values: %s\n", i, m_plugInParamName[i].mb_str().data(), m_txtPlugInParam[i].mb_str().data()); - } - } - - else { - fprintf(stderr, "plugin: fps not found...\n"); - } - } - else { - fprintf(stderr, "plugin not found...\n"); - } - - // Create the main application window - - frame = new MainFrame(m_plugInName, NULL); - SetTopWindow(frame); - - // Should guarantee that the first plot tab defined is the one - // displayed. But it doesn't when built from command line. Why? - - frame->m_auiNbookCtrl->ChangeSelection(0); - frame->Layout(); - frame->Show(); - g_parent =frame; - - - return true; -} - -//------------------------------------------------------------------------- -// OnExit() -//------------------------------------------------------------------------- -int MainApp::OnExit() -{ - fprintf(stderr, "MainApp::OnExit\n"); - if (m_plugIn) { - #ifdef __WXMSW__ - FreeLibrary((HMODULE)m_plugInHandle); - #else - dlclose(m_plugInHandle); - #endif - } - - return 0; -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainFrame(wxFrame* pa->ent) : TopFrame(parent) -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -MainFrame::MainFrame(wxString plugInName, wxWindow *parent) : TopFrame(plugInName, parent) -{ - m_zoom = 1.; - - #ifdef __WXMSW__ - g_logfile = stderr; - #else - g_logfile = stderr; - #endif - - - SetMinSize(wxSize(400,400)); - - // Init Hamlib library, but we dont start talking to any rigs yet - - wxGetApp().m_hamlib = new Hamlib(); - - // Init Serialport library, but as for Hamlib we dont start talking to any rigs yet - - wxGetApp().m_serialport = new Serialport(); - - tools->AppendSeparator(); - wxMenuItem* m_menuItemToolsConfigDelete; - m_menuItemToolsConfigDelete = new wxMenuItem(tools, wxID_ANY, wxString(_("&Restore defaults")) , wxT("Delete config file/keys and restore defaults"), wxITEM_NORMAL); - this->Connect(m_menuItemToolsConfigDelete->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::OnDeleteConfig)); - - tools->Append(m_menuItemToolsConfigDelete); - - wxConfigBase *pConfig = wxConfigBase::Get(); - - // restore frame position and size - int x = pConfig->Read(wxT("/MainFrame/left"), 20); - int y = pConfig->Read(wxT("/MainFrame/top"), 20); - int w = pConfig->Read(wxT("/MainFrame/width"), 800); - int h = pConfig->Read(wxT("/MainFrame/height"), 550); - - // sanitise frame position as a first pass at Win32 registry bug - - fprintf(g_logfile, "x = %d y = %d w = %d h = %d\n", x,y,w,h); - if (x < 0 || x > 2048) x = 20; - if (y < 0 || y > 2048) y = 20; - if (w < 0 || w > 2048) w = 800; - if (h < 0 || h > 2048) h = 550; - - wxGetApp().m_show_wf = pConfig->Read(wxT("/MainFrame/show_wf"), 1); - wxGetApp().m_show_spect = pConfig->Read(wxT("/MainFrame/show_spect"), 1); - wxGetApp().m_show_scatter = pConfig->Read(wxT("/MainFrame/show_scatter"), 1); - wxGetApp().m_show_timing = pConfig->Read(wxT("/MainFrame/show_timing"), 1); - wxGetApp().m_show_freq = pConfig->Read(wxT("/MainFrame/show_freq"), 1); - wxGetApp().m_show_speech_in = pConfig->Read(wxT("/MainFrame/show_speech_in"), 1); - wxGetApp().m_show_speech_out = pConfig->Read(wxT("/MainFrame/show_speech_out"), 1); - wxGetApp().m_show_demod_in = pConfig->Read(wxT("/MainFrame/show_demod_in"), 1); - wxGetApp().m_show_test_frame_errors = pConfig->Read(wxT("/MainFrame/show_test_frame_errors"), 1); - wxGetApp().m_show_test_frame_errors_hist = pConfig->Read(wxT("/MainFrame/show_test_frame_errors_hist"), 1); - - wxGetApp().m_rxNbookCtrl = pConfig->Read(wxT("/MainFrame/rxNbookCtrl"), (long)0); - - g_SquelchActive = pConfig->Read(wxT("/Audio/SquelchActive"), (long)0); - g_SquelchLevel = pConfig->Read(wxT("/Audio/SquelchLevel"), (int)(SQ_DEFAULT_SNR*2)); - g_SquelchLevel /= 2.0; - - Move(x, y); - SetClientSize(w, h); - - if(wxGetApp().m_show_wf) - { - // Add Waterfall Plot window - m_panelWaterfall = new PlotWaterfall((wxFrame*) m_auiNbookCtrl, false, 0); - m_panelWaterfall->SetToolTip(_("Left click to tune, Right click to toggle mono/colour")); - m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Waterfall"), true, wxNullBitmap); - } - if(wxGetApp().m_show_spect) - { - // Add Spectrum Plot window - m_panelSpectrum = new PlotSpectrum((wxFrame*) m_auiNbookCtrl, g_avmag, - MODEM_STATS_NSPEC*((float)MAX_F_HZ/MODEM_STATS_MAX_F_HZ)); - m_panelSpectrum->SetToolTip(_("Left click to tune")); - m_auiNbookCtrl->AddPage(m_panelSpectrum, _("Spectrum"), true, wxNullBitmap); - } - if(wxGetApp().m_show_scatter) - { - // Add Scatter Plot window - m_panelScatter = new PlotScatter((wxFrame*) m_auiNbookCtrl); - m_auiNbookCtrl->AddPage(m_panelScatter, _("Scatter"), true, wxNullBitmap); - } - if(wxGetApp().m_show_demod_in) - { - // Add Demod Input window - m_panelDemodIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelDemodIn, _("Frm Radio"), true, wxNullBitmap); - g_plotDemodInFifo = fifo_create(2*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_speech_in) - { - // Add Speech Input window - m_panelSpeechIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechIn, _("Frm Mic"), true, wxNullBitmap); - g_plotSpeechInFifo = fifo_create(4*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_speech_out) - { - // Add Speech Output window - m_panelSpeechOut = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechOut, _("To Spkr/Hdphns"), true, wxNullBitmap); - g_plotSpeechOutFifo = fifo_create(2*WAVEFORM_PLOT_BUF); - } - - if(wxGetApp().m_show_timing) - { - // Add Timing Offset window - m_panelTimeOffset = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 5.0, DT, -0.5, 0.5, 1, 0.1, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelTimeOffset, L"Timing \u0394", true, wxNullBitmap); - } - if(wxGetApp().m_show_freq) - { - // Add Frequency Offset window - m_panelFreqOffset = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 5.0, DT, -200, 200, 1, 50, "%3.0fHz", 0); - m_auiNbookCtrl->AddPage(m_panelFreqOffset, L"Frequency \u0394", true, wxNullBitmap); - } - - if(wxGetApp().m_show_test_frame_errors) - { - // Add Test Frame Errors window - m_panelTestFrameErrors = new PlotScalar((wxFrame*) m_auiNbookCtrl, 2*MODEM_STATS_NC_MAX, 30.0, DT, 0, 2*MODEM_STATS_NC_MAX+2, 1, 1, "", 1); - m_auiNbookCtrl->AddPage(m_panelTestFrameErrors, L"Test Frame Errors", true, wxNullBitmap); - } - - if(wxGetApp().m_show_test_frame_errors_hist) - { - // Add Test Frame Errors window - m_panelTestFrameErrorsHist = new PlotScalar((wxFrame*) m_auiNbookCtrl, 1, 1.0, 1.0/(2*FDMDV_NC_MAX), 0.0, 1.0, 1.0/FDMDV_NC_MAX, 0.1, "%3.2f", 0); - m_auiNbookCtrl->AddPage(m_panelTestFrameErrorsHist, L"Test Frame Histogram", true, wxNullBitmap); - } - - wxGetApp().m_framesPerBuffer = pConfig->Read(wxT("/Audio/framesPerBuffer"), PA_FPB); - - g_soundCard1InDeviceNum = pConfig->Read(wxT("/Audio/soundCard1InDeviceNum"), -1); - g_soundCard1OutDeviceNum = pConfig->Read(wxT("/Audio/soundCard1OutDeviceNum"), -1); - g_soundCard1SampleRate = pConfig->Read(wxT("/Audio/soundCard1SampleRate"), -1); - - g_soundCard2InDeviceNum = pConfig->Read(wxT("/Audio/soundCard2InDeviceNum"), -1); - g_soundCard2OutDeviceNum = pConfig->Read(wxT("/Audio/soundCard2OutDeviceNum"), -1); - g_soundCard2SampleRate = pConfig->Read(wxT("/Audio/soundCard2SampleRate"), -1); - - g_nSoundCards = 0; - if ((g_soundCard1InDeviceNum > -1) && (g_soundCard1OutDeviceNum > -1)) { - g_nSoundCards = 1; - if ((g_soundCard2InDeviceNum > -1) && (g_soundCard2OutDeviceNum > -1)) - g_nSoundCards = 2; - } - - wxGetApp().m_playFileToMicInPath = pConfig->Read("/File/playFileToMicInPath", wxT("")); - wxGetApp().m_recFileFromRadioPath = pConfig->Read("/File/recFileFromRadioPath", wxT("")); - wxGetApp().m_recFileFromRadioSecs = pConfig->Read("/File/recFileFromRadioSecs", 30); - wxGetApp().m_playFileFromRadioPath = pConfig->Read("/File/playFileFromRadioPath", wxT("")); - - // PTT ------------------------------------------------------------------- - - wxGetApp().m_boolHalfDuplex = pConfig->ReadBool(wxT("/Rig/HalfDuplex"), true); - wxGetApp().m_leftChannelVoxTone = pConfig->ReadBool("/Rig/leftChannelVoxTone", false); - - wxGetApp().m_txtVoiceKeyerWaveFilePath = pConfig->Read(wxT("/VoiceKeyer/WaveFilePath"), wxT("")); - wxGetApp().m_txtVoiceKeyerWaveFile = pConfig->Read(wxT("/VoiceKeyer/WaveFile"), wxT("voicekeyer.wav")); - wxGetApp().m_intVoiceKeyerRxPause = pConfig->Read(wxT("/VoiceKeyer/RxPause"), 10); - wxGetApp().m_intVoiceKeyerRepeats = pConfig->Read(wxT("/VoiceKeyer/Repeats"), 5); - - wxGetApp().m_boolHamlibUseForPTT = pConfig->ReadBool("/Hamlib/UseForPTT", false); - wxGetApp().m_intHamlibRig = pConfig->ReadLong("/Hamlib/RigName", 0); - wxGetApp().m_strHamlibSerialPort = pConfig->Read("/Hamlib/SerialPort", ""); - - wxGetApp().m_boolUseSerialPTT = pConfig->ReadBool(wxT("/Rig/UseSerialPTT"), false); - wxGetApp().m_strRigCtrlPort = pConfig->Read(wxT("/Rig/Port"), wxT("")); - wxGetApp().m_boolUseRTS = pConfig->ReadBool(wxT("/Rig/UseRTS"), true); - wxGetApp().m_boolRTSPos = pConfig->ReadBool(wxT("/Rig/RTSPolarity"), true); - wxGetApp().m_boolUseDTR = pConfig->ReadBool(wxT("/Rig/UseDTR"), false); - wxGetApp().m_boolDTRPos = pConfig->ReadBool(wxT("/Rig/DTRPolarity"), false); - - assert(wxGetApp().m_serialport != NULL); - - // ----------------------------------------------------------------------- - - bool slow = false; // prevents compile error when using default bool - wxGetApp().m_snrSlow = pConfig->Read("/Audio/snrSlow", slow); - - bool t = true; // prevents compile error when using default bool - wxGetApp().m_codec2LPCPostFilterEnable = pConfig->Read(wxT("/Filter/codec2LPCPostFilterEnable"), t); - wxGetApp().m_codec2LPCPostFilterBassBoost = pConfig->Read(wxT("/Filter/codec2LPCPostFilterBassBoost"), t); - wxGetApp().m_codec2LPCPostFilterGamma = (float)pConfig->Read(wxT("/Filter/codec2LPCPostFilterGamma"), CODEC2_LPC_PF_GAMMA*100)/100.0; - wxGetApp().m_codec2LPCPostFilterBeta = (float)pConfig->Read(wxT("/Filter/codec2LPCPostFilterBeta"), CODEC2_LPC_PF_BETA*100)/100.0; - //printf("main(): m_codec2LPCPostFilterBeta: %f\n", wxGetApp().m_codec2LPCPostFilterBeta); - - wxGetApp().m_speexpp_enable = pConfig->Read(wxT("/Filter/speexpp_enable"), t); - - wxGetApp().m_MicInBassFreqHz = (float)pConfig->Read(wxT("/Filter/MicInBassFreqHz"), 1); - wxGetApp().m_MicInBassGaindB = (float)pConfig->Read(wxT("/Filter/MicInBassGaindB"), (long)0)/10.0; - wxGetApp().m_MicInTrebleFreqHz = (float)pConfig->Read(wxT("/Filter/MicInTrebleFreqHz"), 1); - wxGetApp().m_MicInTrebleGaindB = (float)pConfig->Read(wxT("/Filter/MicInTrebleGaindB"), (long)0)/10.0; - wxGetApp().m_MicInMidFreqHz = (float)pConfig->Read(wxT("/Filter/MicInMidFreqHz"), 1); - wxGetApp().m_MicInMidGaindB = (float)pConfig->Read(wxT("/Filter/MicInMidGaindB"), (long)0)/10.0; - wxGetApp().m_MicInMidQ = (float)pConfig->Read(wxT("/Filter/MicInMidQ"), (long)100)/100.0; - - bool f = false; - wxGetApp().m_MicInEQEnable = (float)pConfig->Read(wxT("/Filter/MicInEQEnable"), f); - - wxGetApp().m_SpkOutBassFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutBassFreqHz"), 1); - wxGetApp().m_SpkOutBassGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutBassGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutTrebleFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutTrebleFreqHz"), 1); - wxGetApp().m_SpkOutTrebleGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutTrebleGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutMidFreqHz = (float)pConfig->Read(wxT("/Filter/SpkOutMidFreqHz"), 1); - wxGetApp().m_SpkOutMidGaindB = (float)pConfig->Read(wxT("/Filter/SpkOutMidGaindB"), (long)0)/10.0; - wxGetApp().m_SpkOutMidQ = (float)pConfig->Read(wxT("/Filter/SpkOutMidQ"), (long)100)/100.0; - - wxGetApp().m_SpkOutEQEnable = (float)pConfig->Read(wxT("/Filter/SpkOutEQEnable"), f); - - wxGetApp().m_callSign = pConfig->Read("/Data/CallSign", wxT("")); - wxGetApp().m_textEncoding = pConfig->Read("/Data/TextEncoding", 1); - wxGetApp().m_enable_checksum = pConfig->Read("/Data/EnableChecksumOnMsgRx", f); - - wxGetApp().m_events = pConfig->Read("/Events/enable", f); - wxGetApp().m_events_spam_timer = (int)pConfig->Read(wxT("/Events/spam_timer"), 10); - wxGetApp().m_events_regexp_match = pConfig->Read("/Events/regexp_match", wxT("s=(.*)")); - wxGetApp().m_events_regexp_replace = pConfig->Read("/Events/regexp_replace", - wxT("curl http://qso.freedv.org/cgi-bin/onspot.cgi?s=\\1")); - // make sure regexp lists are terminated by a \n - - if (wxGetApp().m_events_regexp_match.Last() != '\n') { - wxGetApp().m_events_regexp_match = wxGetApp().m_events_regexp_match+'\n'; - } - if (wxGetApp().m_events_regexp_replace.Last() != '\n') { - wxGetApp().m_events_regexp_replace = wxGetApp().m_events_regexp_replace+'\n'; - } - - wxGetApp().m_udp_enable = (float)pConfig->Read(wxT("/UDP/enable"), f); - wxGetApp().m_udp_port = (int)pConfig->Read(wxT("/UDP/port"), 3000); - - wxGetApp().m_FreeDV700txClip = (float)pConfig->Read(wxT("/FreeDV700/txClip"), t); - - wxGetApp().m_debug_console = (float)pConfig->Read(wxT("/Debug/console"), f); - - int mode = pConfig->Read(wxT("/Audio/mode"), (long)0); - if (mode == 0) - m_rb1600->SetValue(1); - if (mode == 2) - m_rb700b->SetValue(1); - if (mode == 3) - m_rb700c->SetValue(1); - if (mode == 4) - m_rb800xa->SetValue(1); - - pConfig->SetPath(wxT("/")); - -// this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - //m_togRxID->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); - //m_togTxID->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnTxIDUI), NULL, this); - m_togBtnOnOff->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnOnOffUI), NULL, this); - m_togBtnSplit->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnSplitClickUI), NULL, this); - m_togBtnAnalog->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnAnalogClickUI), NULL, this); - //m_togBtnALC->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnALCClickUI), NULL, this); - // m_btnTogPTT->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnPTT_UI), NULL, this); - - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - m_btnTogPTT->Disable(); - m_togBtnVoiceKeyer->Disable(); - //m_togBtnALC->Disable(); - - // squelch settings - char sqsnr[15]; - m_sliderSQ->SetValue((int)((g_SquelchLevel+5.0)*2.0)); - sprintf(sqsnr, "%4.1f", g_SquelchLevel); - wxString sqsnr_string(sqsnr); - m_textSQ->SetLabel(sqsnr_string); - m_ckboxSQ->SetValue(g_SquelchActive); - - // SNR settings - - m_ckboxSNR->SetValue(wxGetApp().m_snrSlow); - setsnrBeta(wxGetApp().m_snrSlow); - -#ifdef _USE_TIMER - Bind(wxEVT_TIMER, &MainFrame::OnTimer, this); // ID_MY_WINDOW); - m_plotTimer.SetOwner(this, ID_TIMER_WATERFALL); - //m_panelWaterfall->Refresh(); -#endif - - m_RxRunning = false; - -#ifdef _USE_ONIDLE - Connect(wxEVT_IDLE, wxIdleEventHandler(MainFrame::OnIdle), NULL, this); -#endif //_USE_ONIDLE - - g_sfPlayFile = NULL; - g_playFileToMicIn = false; - g_loopPlayFileToMicIn = false; - - g_sfRecFile = NULL; - g_recFileFromRadio = false; - - g_sfPlayFileFromRadio = NULL; - g_playFileFromRadio = false; - g_loopPlayFileFromRadio = false; - - // init click-tune states - - g_RxFreqOffsetHz = 0.0; - g_RxFreqOffsetPhaseRect.real = cos(0.0); - g_RxFreqOffsetPhaseRect.imag = sin(0.0); - m_panelWaterfall->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelSpectrum->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - - g_TxFreqOffsetHz = 0.0; - g_TxFreqOffsetPhaseRect.real = cos(0.0); - g_TxFreqOffsetPhaseRect.imag = sin(0.0); - - g_tx = 0; - g_split = 0; - - // data states - g_txDataInFifo = fifo_create(MAX_CALLSIGN*VARICODE_MAX_BITS); - g_rxDataOutFifo = fifo_create(MAX_CALLSIGN*VARICODE_MAX_BITS); - - sox_biquad_start(); - - g_testFrames = 0; - g_test_frame_sync_state = 0; - g_total_bit_errors = 0; - g_total_bits = 0; - wxGetApp().m_testFrames = false; - - g_modal = false; - -#ifdef __EXPERIMENTAL_UDP__ - // Start UDP listener thread - - m_UDPThread = NULL; - startUDPThread(); -#endif - - optionsDlg = new OptionsDlg(NULL); - m_schedule_restore = false; - - vk_state = VK_IDLE; - - // Init optional Windows debug console so we can see all those printfs - -#ifdef __WXMSW__ - if (wxGetApp().m_debug_console) { - // somewhere to send printfs while developing - int ret = AllocConsole(); - freopen("CONOUT$", "w", stdout); - freopen("CONOUT$", "w", stderr); - fprintf(stderr, "AllocConsole: %d m_debug_console: %d\n", ret, wxGetApp().m_debug_console); - } -#endif - - //ftest = fopen("ftest.raw", "wb"); - //assert(ftest != NULL); -} - -//------------------------------------------------------------------------- -// ~MainFrame() -//------------------------------------------------------------------------- -MainFrame::~MainFrame() -{ - int x; - int y; - int w; - int h; - - fprintf(stderr, "MainFrame::~MainFrame()\n"); - //fclose(ftest); - #ifdef __WXMSW__ - fclose(g_logfile); - #endif - - if (optionsDlg != NULL) { - delete optionsDlg; - optionsDlg = NULL; - } - -#ifdef __EXPERIMENTAL_UDP__ - stopUDPThread(); -#endif - - if (wxGetApp().m_hamlib) delete wxGetApp().m_hamlib; - if (wxGetApp().m_serialport) delete wxGetApp().m_serialport; - - wxConfigBase *pConfig = wxConfigBase::Get(); - if(pConfig) - { - if (!IsIconized()) { - GetClientSize(&w, &h); - GetPosition(&x, &y); - printf("x = %d y = %d w = %d h = %d\n", x,y,w,h); - pConfig->Write(wxT("/MainFrame/left"), (long) x); - pConfig->Write(wxT("/MainFrame/top"), (long) y); - pConfig->Write(wxT("/MainFrame/width"), (long) w); - pConfig->Write(wxT("/MainFrame/height"), (long) h); - } - pConfig->Write(wxT("/MainFrame/show_wf"), wxGetApp().m_show_wf); - pConfig->Write(wxT("/MainFrame/show_spect"), wxGetApp().m_show_spect); - pConfig->Write(wxT("/MainFrame/show_scatter"), wxGetApp().m_show_scatter); - pConfig->Write(wxT("/MainFrame/show_timing"), wxGetApp().m_show_timing); - pConfig->Write(wxT("/MainFrame/show_freq"), wxGetApp().m_show_freq); - pConfig->Write(wxT("/MainFrame/show_speech_in"), wxGetApp().m_show_speech_in); - pConfig->Write(wxT("/MainFrame/show_speech_out"), wxGetApp().m_show_speech_out); - pConfig->Write(wxT("/MainFrame/show_demod_in"), wxGetApp().m_show_demod_in); - pConfig->Write(wxT("/MainFrame/show_test_frame_errors"), wxGetApp().m_show_test_frame_errors); - pConfig->Write(wxT("/MainFrame/show_test_frame_errors_hist"), wxGetApp().m_show_test_frame_errors_hist); - - pConfig->Write(wxT("/MainFrame/rxNbookCtrl"), wxGetApp().m_rxNbookCtrl); - - pConfig->Write(wxT("/Audio/SquelchActive"), g_SquelchActive); - pConfig->Write(wxT("/Audio/SquelchLevel"), (int)(g_SquelchLevel*2.0)); - - pConfig->Write(wxT("/Audio/framesPerBuffer"), wxGetApp().m_framesPerBuffer); - - pConfig->Write(wxT("/Audio/soundCard1InDeviceNum"), g_soundCard1InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1OutDeviceNum"), g_soundCard1OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard1SampleRate"), g_soundCard1SampleRate ); - - pConfig->Write(wxT("/Audio/soundCard2InDeviceNum"), g_soundCard2InDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2OutDeviceNum"), g_soundCard2OutDeviceNum); - pConfig->Write(wxT("/Audio/soundCard2SampleRate"), g_soundCard2SampleRate ); - - pConfig->Write(wxT("/VoiceKeyer/WaveFilePath"), wxGetApp().m_txtVoiceKeyerWaveFilePath); - pConfig->Write(wxT("/VoiceKeyer/WaveFile"), wxGetApp().m_txtVoiceKeyerWaveFile); - pConfig->Write(wxT("/VoiceKeyer/RxPause"), wxGetApp().m_intVoiceKeyerRxPause); - pConfig->Write(wxT("/VoiceKeyer/Repeats"), wxGetApp().m_intVoiceKeyerRepeats); - - pConfig->Write(wxT("/Rig/HalfDuplex"), wxGetApp().m_boolHalfDuplex); - pConfig->Write(wxT("/Rig/leftChannelVoxTone"), wxGetApp().m_leftChannelVoxTone); - pConfig->Write("/Hamlib/UseForPTT", wxGetApp().m_boolHamlibUseForPTT); - pConfig->Write("/Hamlib/RigName", wxGetApp().m_intHamlibRig); - pConfig->Write("/Hamlib/SerialPort", wxGetApp().m_strHamlibSerialPort); - - - pConfig->Write(wxT("/File/playFileToMicInPath"), wxGetApp().m_playFileToMicInPath); - pConfig->Write(wxT("/File/recFileFromRadioPath"), wxGetApp().m_recFileFromRadioPath); - pConfig->Write(wxT("/File/recFileFromRadioSecs"), wxGetApp().m_recFileFromRadioSecs); - pConfig->Write(wxT("/File/playFileFromRadioPath"), wxGetApp().m_playFileFromRadioPath); - - pConfig->Write(wxT("/Audio/snrSlow"), wxGetApp().m_snrSlow); - - pConfig->Write(wxT("/Data/CallSign"), wxGetApp().m_callSign); - pConfig->Write(wxT("/Data/TextEncoding"), wxGetApp().m_textEncoding); - pConfig->Write(wxT("/Data/EnableChecksumOnMsgRx"), wxGetApp().m_enable_checksum); - pConfig->Write(wxT("/Events/enable"), wxGetApp().m_events); - pConfig->Write(wxT("/Events/spam_timer"), wxGetApp().m_events_spam_timer); - pConfig->Write(wxT("/Events/regexp_match"), wxGetApp().m_events_regexp_match); - pConfig->Write(wxT("/Events/regexp_replace"), wxGetApp().m_events_regexp_replace); - - pConfig->Write(wxT("/UDP/enable"), wxGetApp().m_udp_enable); - pConfig->Write(wxT("/UDP/port"), wxGetApp().m_udp_port); - - pConfig->Write(wxT("/Filter/MicInEQEnable"), wxGetApp().m_MicInEQEnable); - pConfig->Write(wxT("/Filter/SpkOutEQEnable"), wxGetApp().m_SpkOutEQEnable); - - pConfig->Write(wxT("/FreeDV700/txClip"), wxGetApp().m_FreeDV700txClip); - - pConfig->Write(wxT("/Debug/console"), wxGetApp().m_debug_console); - - int mode; - if (m_rb1600->GetValue()) - mode = 0; - if (m_rb700b->GetValue()) - mode = 2; - if (m_rb700c->GetValue()) - mode = 3; - if (m_rb800xa->GetValue()) - mode = 4; - pConfig->Write(wxT("/Audio/mode"), mode); - } - - //m_togRxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); - //m_togTxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnTxIDUI), NULL, this); - m_togBtnOnOff->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnOnOffUI), NULL, this); - m_togBtnSplit->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnSplitClickUI), NULL, this); - m_togBtnAnalog->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnAnalogClickUI), NULL, this); - //m_togBtnALC->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnALCClickUI), NULL, this); - //m_btnTogPTT->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnPTT_UI), NULL, this); - - sox_biquad_finish(); - - if (m_RxRunning) - { - stopRxStream(); - } - if (g_sfPlayFile != NULL) - { - sf_close(g_sfPlayFile); - g_sfPlayFile = NULL; - } - if (g_sfRecFile != NULL) - { - sf_close(g_sfRecFile); - g_sfRecFile = NULL; - } -#ifdef _USE_TIMER - if(m_plotTimer.IsRunning()) - { - m_plotTimer.Stop(); - Unbind(wxEVT_TIMER, &MainFrame::OnTimer, this); - } -#endif //_USE_TIMER - -#ifdef _USE_ONIDLE - Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainFrame::OnIdle), NULL, this); -#endif // _USE_ONIDLE - - delete wxConfigBase::Set((wxConfigBase *) NULL); -} - - -#ifdef _USE_ONIDLE -void MainFrame::OnIdle(wxIdleEvent &evt) { -} -#endif - - -#ifdef _USE_TIMER -//---------------------------------------------------------------- -// OnTimer() -// -// when the timer fires every DT seconds we update the GUI displays. -// the tabs only the plot that is visible actually gets updated, this -// keeps CPU load reasonable -//---------------------------------------------------------------- -void MainFrame::OnTimer(wxTimerEvent &evt) -{ - - int r,c; - - if (m_panelWaterfall->checkDT()) { - m_panelWaterfall->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelWaterfall->m_newdata = true; - m_panelWaterfall->Refresh(); - } - - m_panelSpectrum->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz); - m_panelSpectrum->m_newdata = true; - m_panelSpectrum->Refresh(); - - /* update scatter/eye plot ------------------------------------------------------------*/ - - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_800XA) { - /* FSK Mode - eye diagram ---------------------------------------------------------*/ - - /* add samples row by row */ - - int i; - for (i=0; iadd_new_samples_eye(&g_stats.rx_eye[i][0], g_stats.neyesamp); - } - } - else { - /* PSK Modes - scatter plot -------------------------------------------------------*/ - for (r=0; radd_new_samples_scatter(&g_stats.rx_symbols[r][0]); - } - - if ((freedv_get_mode(g_pfreedv) == FREEDV_MODE_700B) || (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C)) { - - /* - FreeDV 700 uses diversity, so combine symbols for - scatter plot, as combined symbols are used for - demodulation. Note we need to use a copy of the - symbols, as we are not sure when the stats will be - updated. - */ - - COMP rx_symbols_copy[g_Nc/2]; - - for(c=0; cadd_new_samples_scatter(rx_symbols_copy); - } - - } - } - - m_panelScatter->Refresh(); - - // Oscilliscope type speech plots ------------------------------------------------------- - - short speechInPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotSpeechInFifo, speechInPlotSamples, WAVEFORM_PLOT_BUF)) { - memset(speechInPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - //fprintf(stderr, "empty!\n"); - } - m_panelSpeechIn->add_new_short_samples(0, speechInPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelSpeechIn->Refresh(); - - short speechOutPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotSpeechOutFifo, speechOutPlotSamples, WAVEFORM_PLOT_BUF)) - memset(speechOutPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - m_panelSpeechOut->add_new_short_samples(0, speechOutPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelSpeechOut->Refresh(); - - short demodInPlotSamples[WAVEFORM_PLOT_BUF]; - if (fifo_read(g_plotDemodInFifo, demodInPlotSamples, WAVEFORM_PLOT_BUF)) { - memset(demodInPlotSamples, 0, WAVEFORM_PLOT_BUF*sizeof(short)); - } - m_panelDemodIn->add_new_short_samples(0,demodInPlotSamples, WAVEFORM_PLOT_BUF, 32767); - m_panelDemodIn->Refresh(); - - // Demod states ----------------------------------------------------------------------- - - m_panelTimeOffset->add_new_sample(0, (float)g_stats.rx_timing/FDMDV_NOM_SAMPLES_PER_FRAME); - m_panelTimeOffset->Refresh(); - - m_panelFreqOffset->add_new_sample(0, g_stats.foff); - m_panelFreqOffset->Refresh(); - - // SNR text box and gauge ------------------------------------------------------------ - - // LP filter g_stats.snr_est some more to stabilise the - // display. g_stats.snr_est already has some low pass filtering - // but we need it fairly fast to activate squelch. So we - // optionally perform some further filtering for the display - // version of SNR. The "Slow" checkbox controls the amount of - // filtering. The filtered snr also controls the squelch - - g_snr = m_snrBeta*g_snr + (1.0 - m_snrBeta)*g_stats.snr_est; - float snr_limited = g_snr; - if (snr_limited < -5.0) snr_limited = -5.0; - if (snr_limited > 20.0) snr_limited = 20.0; - - char snr[15]; - sprintf(snr, "%d", (int)(g_snr+0.5)); // round to nearest dB - - //printf("snr_est: %f m_snrBeta: %f g_snr: %f snr_limited: %f\n", g_stats.snr_est, m_snrBeta, g_snr, snr_limited); - - wxString snr_string(snr); - m_textSNR->SetLabel(snr_string); - m_gaugeSNR->SetValue((int)(snr_limited+5)); - - - // Level Gauge ----------------------------------------------------------------------- - - float tooHighThresh; - if (!g_tx && m_RxRunning) - { - // receive mode - display From Radio peaks - // peak from this DT sampling period - int maxDemodIn = 0; - for(int i=0; i m_maxLevel) - m_maxLevel = maxDemodIn; - - tooHighThresh = FROM_RADIO_MAX; - } - else - { - // transmit mode - display From Mic peaks - - // peak from this DT sampling period - int maxSpeechIn = 0; - for(int i=0; i m_maxLevel) - m_maxLevel = maxSpeechIn; - - tooHighThresh = FROM_MIC_MAX; - } - - // Peak Reading meter: updates peaks immediately, then slowly decays - int maxScaled = (int)(100.0 * ((float)m_maxLevel/32767.0)); - m_gaugeLevel->SetValue(maxScaled); - //printf("maxScaled: %d\n", maxScaled); - if (((float)maxScaled/100) > tooHighThresh) - m_textLevel->SetLabel("Too High"); - else - m_textLevel->SetLabel(""); - - m_maxLevel *= LEVEL_BETA; - - // sync LED (Colours don't work on Windows) ------------------------ - - if (g_State) { - m_rbSync->SetForegroundColour( wxColour( 0, 255, 0 ) ); // green - m_rbSync->SetValue(true); - } - else { - m_rbSync->SetForegroundColour( wxColour( 255, 0, 0 ) ); // red - m_rbSync->SetValue(false); - } - - // send Callsign ---------------------------------------------------- - - char callsign[MAX_CALLSIGN]; - strncpy(callsign, (const char*) wxGetApp().m_callSign.mb_str(wxConvUTF8), MAX_CALLSIGN-1); - - // buffer 1 txt message to ensure tx data fifo doesn't "run dry" - - if ((unsigned)fifo_used(g_txDataInFifo) < strlen(callsign)) { - unsigned int i; - - //fprintf(g_logfile, "tx callsign: %s.\n", callsign); - - /* optionally append checksum */ - - if (wxGetApp().m_enable_checksum) { - - unsigned char checksum = 0; - char callsign_checksum_cr[MAX_CALLSIGN+1]; - - for(i=0; i MAX_CALLSIGN-1)) { - // CR completes line - *m_pcallsign = 0; - - /* Checksums can be disabled, e.g. for compatability with - older vesions. In that case we print msg but don't do - any event processing. If checksums enabled, only print - out when checksum is good. */ - - if (wxGetApp().m_enable_checksum) { - // lets see if checksum is OK - - unsigned char checksum_rx = 0; - if (strlen(m_callsign) > 2) { - for(unsigned int i=0; iSetValue(s); - -#ifdef __UDP_EXPERIMENTAL__ - char s1[MAX_CALLSIGN]; - sprintf(s1,"rx_txtmsg %s", m_callsign); - processTxtEvent(s1); - - m_checksumGood++; - s.Printf("%d", m_checksumGood); - m_txtChecksumGood->SetLabel(s); -#endif - } - else { -#ifdef __UDP_EXPERIMENTAL__ - m_checksumBad++; - s.Printf("%d", m_checksumBad); - m_txtChecksumBad->SetLabel(s); -#endif - } - } - - //fprintf(g_logfile,"resetting callsign %s %ld\n", m_callsign, m_pcallsign-m_callsign); - // reset ptr to start of string - m_pcallsign = m_callsign; - } - else { - //fprintf(g_logfile, "new char %d %c\n", ashort, (char)ashort); - *m_pcallsign++ = (char)ashort; - } - - /* If checksums disabled, display txt chars as they arrive */ - - if (!wxGetApp().m_enable_checksum) { - m_txtCtrlCallSign->SetValue(m_callsign); - } - } - - // Run time update of EQ filters ----------------------------------- - if (m_newMicInFilter || m_newSpkOutFilter) { - g_mutexProtectingCallbackData.Lock(); - deleteEQFilters(g_rxUserdata); - designEQFilters(g_rxUserdata); - g_mutexProtectingCallbackData.Unlock(); - m_newMicInFilter = m_newSpkOutFilter = false; - } - g_rxUserdata->micInEQEnable = wxGetApp().m_MicInEQEnable; - g_rxUserdata->spkOutEQEnable = wxGetApp().m_SpkOutEQEnable; - - - if (g_mode != -1) { - - // Run time update of FreeDV 700 tx clipper - - freedv_set_clip(g_pfreedv, (int)wxGetApp().m_FreeDV700txClip); - - // Test Frame Bit Error Updates ------------------------------------ - - // Toggle test frame mode at run time - - if (!freedv_get_test_frames(g_pfreedv) && wxGetApp().m_testFrames) { - - // reset stats on check box off to on transition - - freedv_set_test_frames(g_pfreedv, 1); - freedv_set_total_bits(g_pfreedv, 0); - freedv_set_total_bit_errors(g_pfreedv, 0); - } - freedv_set_test_frames(g_pfreedv, wxGetApp().m_testFrames); - g_channel_noise = wxGetApp().m_channel_noise; - - if (g_State) { - char bits[80], errors[80], ber[80]; - - // update stats on main page - - sprintf(bits, "Bits: %d", freedv_get_total_bits(g_pfreedv)); wxString bits_string(bits); m_textBits->SetLabel(bits_string); - sprintf(errors, "Errs: %d", freedv_get_total_bit_errors(g_pfreedv)); wxString errors_string(errors); m_textErrors->SetLabel(errors_string); - float b = (float)freedv_get_total_bit_errors(g_pfreedv)/(1E-6+freedv_get_total_bits(g_pfreedv)); - sprintf(ber, "BER: %4.3f", b); wxString ber_string(ber); m_textBER->SetLabel(ber_string); - - // update error pattern plots if supported - - int sz_error_pattern = freedv_get_sz_error_pattern(g_pfreedv); - if (sz_error_pattern) { - short error_pattern[sz_error_pattern]; - - if (fifo_read(g_error_pattern_fifo, error_pattern, sz_error_pattern) == 0) { - int i,b; - - /* both modes map IQ to alternate bits, but one same carrier */ - - if (freedv_get_mode(g_pfreedv) == FREEDV_MODE_1600) { - /* FreeDV 1600 mapping from error pattern to bit on each carrier */ - - for(b=0; badd_new_sample(b, b + 0.8*error_pattern[i]); - g_error_hist[b] += error_pattern[i]; - } - //if (b%2) - // printf("g_error_hist[%d]: %d\n", b/2, g_error_hist[b/2]); - } - - int max_hist = 0; - for(b=0; b max_hist) - max_hist = g_error_hist[b]; - - m_panelTestFrameErrorsHist->add_new_short_samples(0, g_error_hist, 2*FDMDV_NC_MAX, max_hist); - } - - if ((freedv_get_mode(g_pfreedv) == FREEDV_MODE_700B) || (freedv_get_mode(g_pfreedv) == FREEDV_MODE_700C)) { - int c; - - /* FreeDV 700 mapping from error pattern to bit on each - carrier. Note we don't have access to carriers before - diversity re-combination, so this won't give us the full - picture, we have to assume Nc/2 carriers. */ - - for(i=0; iadd_new_sample(c, c + 0.8*error_pattern[i]); - g_error_hist[c] += error_pattern[i]; - //printf("i: %d c: %d\n", i, c); - } - - int max_hist = 0; - for(b=0; b max_hist) - max_hist = g_error_hist[b]; - m_panelTestFrameErrorsHist->add_new_short_samples(0, g_error_hist, 2*FDMDV_NC_MAX, max_hist); - } - - m_panelTestFrameErrors->Refresh(); - m_panelTestFrameErrorsHist->Refresh(); - } - } - } - } - - // command from UDP thread that is best processed in main thread to avoid seg faults - - if (m_schedule_restore) { - if (IsIconized()) - Restore(); - m_schedule_restore = false; - } - -#ifdef __UDP_EXPERIMENTAL__ - // Light Spam Timer LED if at least one timer is running - - int i; - optionsDlg->SetSpamTimerLight(false); - for(i=0; iSetSpamTimerLight(true); -#endif - - // Blink file playback status line - - if (g_playFileFromRadio) { - g_blink += DT; - //fprintf(g_logfile, "g_blink: %f\n", g_blink); - if ((g_blink >= 1.0) && (g_blink < 2.0)) - SetStatusText(wxT("Playing into from radio"), 0); - if (g_blink >= 2.0) { - SetStatusText(wxT(""), 0); - g_blink = 0.0; - } - } - - // Voice Keyer state machine - - VoiceKeyerProcessEvent(VK_DT); -} -#endif - - -//------------------------------------------------------------------------- -// OnCloseFrame() -//------------------------------------------------------------------------- -void MainFrame::OnCloseFrame(wxCloseEvent& event) -{ - fprintf(stderr, "MainFrame::OnCloseFrame()\n"); - Pa_Terminate(); - Destroy(); -} - -//------------------------------------------------------------------------- -// OnTop() -//------------------------------------------------------------------------- -void MainFrame::OnTop(wxCommandEvent& event) -{ - int style = GetWindowStyle(); - - if (style & wxSTAY_ON_TOP) - { - style &= ~wxSTAY_ON_TOP; - } - else - { - style |= wxSTAY_ON_TOP; - } - SetWindowStyle(style); -} - -//------------------------------------------------------------------------- -// OnDeleteConfig() -//------------------------------------------------------------------------- -void MainFrame::OnDeleteConfig(wxCommandEvent&) -{ - wxConfigBase *pConfig = wxConfigBase::Get(); - if(pConfig->DeleteAll()) - { - wxLogMessage(wxT("Config file/registry key successfully deleted.")); - - delete wxConfigBase::Set(NULL); - wxConfigBase::DontCreateOnDemand(); - } - else - { - wxLogError(wxT("Deleting config file/registry key failed.")); - } -} - -//------------------------------------------------------------------------- -// Paint() -//------------------------------------------------------------------------- -void MainFrame::OnPaint(wxPaintEvent& WXUNUSED(event)) -{ - wxPaintDC dc(this); - - if(GetMenuBar()->IsChecked(ID_PAINT_BG)) - { - dc.Clear(); - } - dc.SetUserScale(m_zoom, m_zoom); -} - -//------------------------------------------------------------------------- -// OnCmdSliderScroll() -//------------------------------------------------------------------------- -void MainFrame::OnCmdSliderScroll(wxScrollEvent& event) -{ - char sqsnr[15]; - g_SquelchLevel = (float)m_sliderSQ->GetValue()/2.0 - 5.0; - sprintf(sqsnr, "%4.1f", g_SquelchLevel); // 0.5 dB steps - wxString sqsnr_string(sqsnr); - m_textSQ->SetLabel(sqsnr_string); - - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnCheckSQClick() -//------------------------------------------------------------------------- -void MainFrame::OnCheckSQClick(wxCommandEvent& event) -{ - if(!g_SquelchActive) - { - g_SquelchActive = true; - } - else - { - g_SquelchActive = false; - } -} - -void MainFrame::setsnrBeta(bool snrSlow) -{ - if(snrSlow) - { - m_snrBeta = 0.95; // make this closer to 1.0 to smooth SNR est further - } - else - { - m_snrBeta = 0.0; // no smoothing of SNR estimate from demodulator - } -} - -//------------------------------------------------------------------------- -// OnCheckSQClick() -//------------------------------------------------------------------------- -void MainFrame::OnCheckSNRClick(wxCommandEvent& event) -{ - wxGetApp().m_snrSlow = m_ckboxSNR->GetValue(); - setsnrBeta(wxGetApp().m_snrSlow); - //printf("m_snrSlow: %d\n", (int)wxGetApp().m_snrSlow); -} - -// check for space bar press (only when running) - -int MainApp::FilterEvent(wxEvent& event) -{ - if ((event.GetEventType() == wxEVT_KEY_DOWN) && - (((wxKeyEvent&)event).GetKeyCode() == WXK_SPACE)) - { - // only use space to toggle PTT if we are running and no modal dialogs (like options) up - //fprintf(stderr,"frame->m_RxRunning: %d g_modal: %d\n", - // frame->m_RxRunning, g_modal); - if (frame->m_RxRunning && !g_modal) { - - // space bar controls rx/rx if keyer not running - if (frame->vk_state == VK_IDLE) { - if (frame->m_btnTogPTT->GetValue()) - frame->m_btnTogPTT->SetValue(false); - else - frame->m_btnTogPTT->SetValue(true); - - frame->togglePTT(); - } - else // spavce bar stops keyer - frame->VoiceKeyerProcessEvent(VK_SPACE_BAR); - - return true; // absorb space so we don't toggle control with focus (e.g. Start) - - } - } - - return -1; -} - -//------------------------------------------------------------------------- -// OnTogBtnPTT () -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnPTT (wxCommandEvent& event) -{ - togglePTT(); - event.Skip(); -} - -void MainFrame::togglePTT(void) { - - // Change tabbed page in centre panel depending on PTT state - - if (g_tx) - { - // tx-> rx transition, swap to the page we were on for last rx - m_auiNbookCtrl->ChangeSelection(wxGetApp().m_rxNbookCtrl); - } - else - { - // rx-> tx transition, swap to Mic In page to monitor speech - wxGetApp().m_rxNbookCtrl = m_auiNbookCtrl->GetSelection(); - m_auiNbookCtrl->ChangeSelection(m_auiNbookCtrl->GetPageIndex((wxWindow *)m_panelSpeechIn)); -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"ptt"); processTxtEvent(e); -#endif - } - - g_tx = m_btnTogPTT->GetValue(); - - // Hamlib PTT - - if (wxGetApp().m_boolHamlibUseForPTT) { - Hamlib *hamlib = wxGetApp().m_hamlib; - wxString hamlibError; - if (wxGetApp().m_boolHamlibUseForPTT && hamlib != NULL) { - if (hamlib->ptt(g_tx, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - } - } - - // Serial PTT - - if (wxGetApp().m_boolUseSerialPTT && (wxGetApp().m_serialport->isopen())) { - wxGetApp().m_serialport->ptt(g_tx); - } - - // reset level gauge - - m_maxLevel = 0; - m_textLevel->SetLabel(wxT("")); - m_gaugeLevel->SetValue(0); -} - -/* - Voice Keyer: - - + space bar turns keyer off - + 5 secs of valid sync turns it off - - [X] complete state machine and builds OK - [ ] file select dialog - [ ] test all states - [ ] restore size -*/ - -void MainFrame::OnTogBtnVoiceKeyerClick (wxCommandEvent& event) -{ - if (vk_state == VK_IDLE) - VoiceKeyerProcessEvent(VK_START); - else - VoiceKeyerProcessEvent(VK_SPACE_BAR); - - event.Skip(); -} - - -int MainFrame::VoiceKeyerStartTx(void) -{ - int next_state; - - // start playing wave file or die trying - - SF_INFO sfInfo; - sfInfo.format = 0; - - g_sfPlayFile = sf_open(wxGetApp().m_txtVoiceKeyerWaveFile, SFM_READ, &sfInfo); - if(g_sfPlayFile == NULL) { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open:") + wxGetApp().m_txtVoiceKeyerWaveFile, wxOK); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - else { - SetStatusText(wxT("Voice Keyer: Playing File") + wxGetApp().m_txtVoiceKeyerWaveFile + wxT(" to Mic Input") , 0); - g_loopPlayFileToMicIn = false; - g_playFileToMicIn = true; - - m_btnTogPTT->SetValue(true); togglePTT(); - next_state = VK_TX; - } - - return next_state; -} - - -void MainFrame::VoiceKeyerProcessEvent(int vk_event) { - int next_state = vk_state; - - switch(vk_state) { - - case VK_IDLE: - if (vk_event == VK_START) { - // sample these puppies at start just in case they are changed while VK running - vk_rx_pause = wxGetApp().m_intVoiceKeyerRxPause; - vk_repeats = wxGetApp().m_intVoiceKeyerRepeats; - fprintf(stderr, "vk_rx_pause: %d vk_repeats: %d\n", vk_rx_pause, vk_repeats); - - vk_repeat_counter = 0; - next_state = VoiceKeyerStartTx(); - } - break; - - case VK_TX: - - // In this state we are transmitting and playing a wave file - // to Mic In - - if (vk_event == VK_SPACE_BAR) { - m_btnTogPTT->SetValue(false); togglePTT(); - StopPlayFileToMicIn(); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if (vk_event == VK_PLAY_FINISHED) { - m_btnTogPTT->SetValue(false); togglePTT(); - vk_repeat_counter++; - if (vk_repeat_counter > vk_repeats) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - else { - vk_rx_time = 0.0; - next_state = VK_RX; - } - } - - break; - - case VK_RX: - - // in this state we are receiving and waiting for - // delay timer or valid sync - - if (vk_event == VK_DT) { - if (freedv_get_sync(g_pfreedv) == 1) { - // if we detect sync simulate a smooth transition to SYNC_WAIT State - TODO: review - if (vk_rx_time >= DT) { - vk_rx_time -= DT; - } else { - next_state = VK_SYNC_WAIT; - } - } else { - vk_rx_time += DT; - if (vk_rx_time >= vk_rx_pause) { - next_state = VoiceKeyerStartTx(); - } - } - } - - if (vk_event == VK_SPACE_BAR) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - break; - - case VK_SYNC_WAIT: - - // In this state we wait for valid sync to last - // VK_SYNC_WAIT_TIME seconds - - if (vk_event == VK_SPACE_BAR) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if (vk_event == VK_DT) { - if (freedv_get_sync(g_pfreedv) == 0) { - // if we lose sync simulate a smooth transition to return in RX State - TODO: review - if (vk_rx_time >= DT) { - vk_rx_time -= DT; - } else { - next_state = VK_RX; - } - } else { - vk_rx_time += DT; - } - - // drop out of voice keyer if we get a few seconds of valid sync - - if (vk_rx_time >= VK_SYNC_WAIT_TIME) { - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - } - break; - - default: - // catch anything we missed - - m_btnTogPTT->SetValue(false); togglePTT(); - m_togBtnVoiceKeyer->SetValue(false); - next_state = VK_IDLE; - } - - if ((vk_event != VK_DT) || (vk_state != next_state)) - fprintf(stderr, "VoiceKeyerProcessEvent: vk_state: %d vk_event: %d next_state: %d vk_repeat_counter: %d\n", vk_state, vk_event, next_state, vk_repeat_counter); - vk_state = next_state; -} - - -//------------------------------------------------------------------------- -// OnTogBtnRxID() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnRxID(wxCommandEvent& event) -{ - // empty any junk in rx data FIFO - short junk; - while(fifo_read(g_rxDataOutFifo,&junk,1) == 0); - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnTogBtnTxID() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnTxID(wxCommandEvent& event) -{ - event.Skip(); -} - -void MainFrame::OnTogBtnSplitClick(wxCommandEvent& event) { - if (g_split) - g_split = 0; - else - g_split = 1; - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnTogBtnAnalogClick() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnAnalogClick (wxCommandEvent& event) -{ - if (g_analog == 0) { - g_analog = 1; - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(FS/2))); - m_panelWaterfall->setFs(FS); - } - else { - g_analog = 0; - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(freedv_get_modem_sample_rate(g_pfreedv)/2))); - m_panelWaterfall->setFs(freedv_get_modem_sample_rate(g_pfreedv)); - } - - g_State = 0; - g_stats.snr_est = 0; - - event.Skip(); -} - -void MainFrame::OnCallSignReset(wxCommandEvent& event) -{ - m_pcallsign = m_callsign; - memset(m_callsign, 0, MAX_CALLSIGN); - wxString s; - s.Printf("%s", m_callsign); - m_txtCtrlCallSign->SetValue(s); -#ifdef __UDP__EXPERIMENTAL__ - m_checksumGood = m_checksumBad = 0; - m_txtChecksumGood->SetLabel(_("0")); - m_txtChecksumBad->SetLabel(_("0")); -#endif -} - -void MainFrame::OnBerReset(wxCommandEvent& event) -{ - freedv_set_total_bits(g_pfreedv, 0); - freedv_set_total_bit_errors(g_pfreedv, 0); - int i; - for(i=0; i<2*g_Nc; i++) - g_error_hist[i] = 0; - -} - -#ifdef ALC -//------------------------------------------------------------------------- -// OnTogBtnALCClick() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnALCClick(wxCommandEvent& event) -{ - wxMessageBox(wxT("Got Click!"), wxT("OnTogBtnALCClick"), wxOK); - - event.Skip(); -} -#endif - -// extra panel added to file open dialog to add loop checkbox -MyExtraPlayFilePanel::MyExtraPlayFilePanel(wxWindow *parent): wxPanel(parent) -{ - m_cb = new wxCheckBox(this, -1, wxT("Loop")); - m_cb->SetToolTip(_("When checked file will repeat forever")); - m_cb->SetValue(g_loopPlayFileToMicIn); - - // bug: I can't this to align right..... - wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL); - sizerTop->Add(m_cb, 0, wxALIGN_RIGHT, 0); - SetSizerAndFit(sizerTop); -} - -static wxWindow* createMyExtraPlayFilePanel(wxWindow *parent) -{ - return new MyExtraPlayFilePanel(parent); -} - -void MainFrame::StopPlayFileToMicIn(void) -{ - g_mutexProtectingCallbackData.Lock(); - g_playFileToMicIn = false; - sf_close(g_sfPlayFile); - SetStatusText(wxT("")); - g_mutexProtectingCallbackData.Unlock(); -} - -//------------------------------------------------------------------------- -// OnPlayFileToMicIn() -//------------------------------------------------------------------------- -void MainFrame::OnPlayFileToMicIn(wxCommandEvent& event) -{ - wxUnusedVar(event); - - if(g_playFileToMicIn) { - StopPlayFileToMicIn(); - VoiceKeyerProcessEvent(VK_PLAY_FINISHED); - } - else - { - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Play File to Mic In"), - wxGetApp().m_playFileToMicInPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_OPEN | wxFD_FILE_MUST_EXIST - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraPlayFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_playFileToMicInPath, &fileName, &extension); - //wxLogDebug("m_playFileToMicInPath: %s", wxGetApp().m_playFileToMicInPath); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = FS; - } - } - g_sfPlayFile = sf_open(soundFile.c_str(), SFM_READ, &sfInfo); - if(g_sfPlayFile == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - - // Huh?! I just copied wxWidgets-2.9.4/samples/dialogs .... - g_loopPlayFileToMicIn = static_cast(ctrl)->getLoopPlayFileToMicIn(); - - SetStatusText(wxT("Playing File: ") + fileName + wxT(" to Mic Input") , 0); - g_playFileToMicIn = true; - } -} - -//------------------------------------------------------------------------- -// OnPlayFileFromRadio() -// This puppy "plays" a recorded file into the demodulator input, allowing us -// to replay off air signals. -//------------------------------------------------------------------------- -void MainFrame::OnPlayFileFromRadio(wxCommandEvent& event) -{ - wxUnusedVar(event); - - printf("OnPlayFileFromRadio:: %d\n", (int)g_playFileFromRadio); - if (g_playFileFromRadio) - { - printf("OnPlayFileFromRadio:: Stop\n"); - g_mutexProtectingCallbackData.Lock(); - g_playFileFromRadio = false; - sf_close(g_sfPlayFileFromRadio); - SetStatusText(wxT(""),0); - SetStatusText(wxT(""),1); - g_mutexProtectingCallbackData.Unlock(); - } - else - { - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Play File - From Radio"), - wxGetApp().m_playFileFromRadioPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_OPEN | wxFD_FILE_MUST_EXIST - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraPlayFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_playFileFromRadioPath, &fileName, &extension); - //wxLogDebug("m_playFileToFromRadioPath: %s", wxGetApp().m_playFileFromRadioPath); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } - } - g_sfPlayFileFromRadio = sf_open(soundFile.c_str(), SFM_READ, &sfInfo); - g_sfFs = sfInfo.samplerate; - if(g_sfPlayFileFromRadio == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - - // Huh?! I just copied wxWidgets-2.9.4/samples/dialogs .... - g_loopPlayFileFromRadio = static_cast(ctrl)->getLoopPlayFileToMicIn(); - - SetStatusText(wxT("Playing into from radio"), 0); - if(extension == wxT("raw")) { - wxString stringnumber = wxString::Format(wxT("%d"), (int)sfInfo.samplerate); - SetStatusText(wxT("raw file assuming Fs=") + stringnumber, 1); - } - fprintf(g_logfile, "OnPlayFileFromRadio:: Playing File\n"); - g_playFileFromRadio = true; - g_blink = 0.0; - } -} - -// extra panel added to file save dialog to set number of seconds to record for - -MyExtraRecFilePanel::MyExtraRecFilePanel(wxWindow *parent): wxPanel(parent) -{ - wxBoxSizer *sizerTop = new wxBoxSizer(wxHORIZONTAL); - - wxStaticText* staticText = new wxStaticText(this, wxID_ANY, _("Seconds:"), wxDefaultPosition, wxDefaultSize, 0); - sizerTop->Add(staticText, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - m_secondsToRecord = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - m_secondsToRecord->SetToolTip(_("Number of seconds to record for")); - m_secondsToRecord->SetValue(wxString::Format(wxT("%i"), wxGetApp().m_recFileFromRadioSecs)); - sizerTop->Add(m_secondsToRecord, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5); - SetSizerAndFit(sizerTop); -} - -static wxWindow* createMyExtraRecFilePanel(wxWindow *parent) -{ - return new MyExtraRecFilePanel(parent); -} - -//------------------------------------------------------------------------- -// OnRecFileFromRadio() -//------------------------------------------------------------------------- -void MainFrame::OnRecFileFromRadio(wxCommandEvent& event) -{ - wxUnusedVar(event); - - if (g_recFileFromRadio) { - printf("Stopping Record....\n"); - g_mutexProtectingCallbackData.Lock(); - g_recFileFromRadio = false; - sf_close(g_sfRecFile); - SetStatusText(wxT("")); - g_mutexProtectingCallbackData.Unlock(); - } - else { - - wxString soundFile; - SF_INFO sfInfo; - - wxFileDialog openFileDialog( - this, - wxT("Record File From Radio"), - wxGetApp().m_recFileFromRadioPath, - wxEmptyString, - wxT("WAV and RAW files (*.wav;*.raw)|*.wav;*.raw|") - wxT("All files (*.*)|*.*"), - wxFD_SAVE - ); - - // add the loop check box - openFileDialog.SetExtraControlCreator(&createMyExtraRecFilePanel); - - if(openFileDialog.ShowModal() == wxID_CANCEL) - { - return; // the user changed their mind... - } - - wxString fileName, extension; - soundFile = openFileDialog.GetPath(); - wxFileName::SplitPath(soundFile, &wxGetApp().m_recFileFromRadioPath, &fileName, &extension); - wxLogDebug("m_recFileFromRadioPath: %s", wxGetApp().m_recFileFromRadioPath); - wxLogDebug("soundFile: %s", soundFile); - sfInfo.format = 0; - - if(!extension.IsEmpty()) - { - extension.LowerCase(); - if(extension == wxT("raw")) - { - sfInfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } - else if(extension == wxT("wav")) - { - sfInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - sfInfo.channels = 1; - sfInfo.samplerate = freedv_get_modem_sample_rate(g_pfreedv); - } else { - wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); - return; - } - } - else { - wxMessageBox(wxT("Invalid file format"), wxT("Record File From Radio"), wxOK); - return; - } - - // Bug: on Win32 I cant read m_recFileFromRadioSecs, so have hard coded it -#ifdef __WIN32__ - long secs = wxGetApp().m_recFileFromRadioSecs; - g_recFromRadioSamples = FS*(unsigned int)secs; -#else - // work out number of samples to record - - wxWindow * const ctrl = openFileDialog.GetExtraControl(); - wxString secsString = static_cast(ctrl)->getSecondsToRecord(); - wxLogDebug("test: %s secsString: %s\n", wxT("testing 123"), secsString); - - long secs; - if (secsString.ToLong(&secs)) { - wxGetApp().m_recFileFromRadioSecs = (unsigned int)secs; - //printf(" secondsToRecord: %d\n", (unsigned int)secs); - g_recFromRadioSamples = FS*(unsigned int)secs; - //printf("g_recFromRadioSamples: %d\n", g_recFromRadioSamples); - } - else { - wxMessageBox(wxT("Invalid number of Seconds"), wxT("Record File From Radio"), wxOK); - return; - } -#endif - - g_sfRecFile = sf_open(soundFile.c_str(), SFM_WRITE, &sfInfo); - if(g_sfRecFile == NULL) - { - wxString strErr = sf_strerror(NULL); - wxMessageBox(strErr, wxT("Couldn't open sound file"), wxOK); - return; - } - - SetStatusText(wxT("Recording File: ") + fileName + wxT(" From Radio") , 0); - g_recFileFromRadio = true; - } - -} - -//------------------------------------------------------------------------- -// OnExit() -//------------------------------------------------------------------------- -void MainFrame::OnExit(wxCommandEvent& event) -{ - fprintf(stderr, "MainFrame::OnExit\n"); - wxUnusedVar(event); -#ifdef _USE_TIMER - m_plotTimer.Stop(); -#endif // _USE_TIMER - if(g_sfPlayFile != NULL) - { - sf_close(g_sfPlayFile); - g_sfPlayFile = NULL; - } - if(g_sfRecFile != NULL) - { - sf_close(g_sfRecFile); - g_sfRecFile = NULL; - } - if(m_RxRunning) - { - stopRxStream(); - } - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - //m_togBtnALC->Disable(); - //m_btnTogPTT->Disable(); - Pa_Terminate(); - Destroy(); -} - -//------------------------------------------------------------------------- -// OnExitClick() -//------------------------------------------------------------------------- -void MainFrame::OnExitClick(wxCommandEvent& event) -{ - OnExit(event); -} - -//------------------------------------------------------------------------- -// OnToolsAudio() -//------------------------------------------------------------------------- -void MainFrame::OnToolsAudio(wxCommandEvent& event) -{ - wxUnusedVar(event); - int rv = 0; - AudioOptsDialog *dlg = new AudioOptsDialog(NULL); - rv = dlg->ShowModal(); - if(rv == wxID_OK) - { - dlg->ExchangeData(EXCHANGE_DATA_OUT); - } - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsAudioUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsAudioUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning); -} - -//------------------------------------------------------------------------- -// OnToolsFilter() -//------------------------------------------------------------------------- -void MainFrame::OnToolsFilter(wxCommandEvent& event) -{ - wxUnusedVar(event); - FilterDlg *dlg = new FilterDlg(NULL, m_RxRunning, &m_newMicInFilter, &m_newSpkOutFilter); - dlg->ShowModal(); - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsOptions() -//------------------------------------------------------------------------- -void MainFrame::OnToolsOptions(wxCommandEvent& event) -{ - wxUnusedVar(event); - g_modal = true; - //fprintf(stderr,"g_modal: %d\n", g_modal); - optionsDlg->Show(); -} - -//------------------------------------------------------------------------- -// OnToolsOptionsUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsOptionsUI(wxUpdateUIEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnToolsComCfg() -//------------------------------------------------------------------------- -void MainFrame::OnToolsComCfg(wxCommandEvent& event) -{ - wxUnusedVar(event); - - ComPortsDlg *dlg = new ComPortsDlg(NULL); - - dlg->ShowModal(); - - delete dlg; -} - -//------------------------------------------------------------------------- -// OnToolsComCfgUI() -//------------------------------------------------------------------------- -void MainFrame::OnToolsComCfgUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning); -} - -//------------------------------------------------------------------------- -// OnToolsPlugInCfg() -//------------------------------------------------------------------------- -void MainFrame::OnToolsPlugInCfg(wxCommandEvent& event) -{ - wxUnusedVar(event); - PlugInDlg *dlg = new PlugInDlg(wxGetApp().m_plugInName, wxGetApp().m_numPlugInParam, wxGetApp().m_plugInParamName); - dlg->ShowModal(); - delete dlg; -} - -void MainFrame::OnToolsPlugInCfgUI(wxUpdateUIEvent& event) -{ - event.Enable(!m_RxRunning && wxGetApp().m_plugIn); -} - - -//------------------------------------------------------------------------- -// OnHelpCheckUpdates() -//------------------------------------------------------------------------- -void MainFrame::OnHelpCheckUpdates(wxCommandEvent& event) -{ - wxMessageBox("Got Click!", "OnHelpCheckUpdates", wxOK); - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnHelpCheckUpdatesUI() -//------------------------------------------------------------------------- -void MainFrame::OnHelpCheckUpdatesUI(wxUpdateUIEvent& event) -{ - event.Enable(false); -} - -//------------------------------------------------------------------------- -//OnHelpAbout() -//------------------------------------------------------------------------- -void MainFrame::OnHelpAbout(wxCommandEvent& event) -{ - wxUnusedVar(event); - wxString msg; - msg.Printf( wxT("FreeDV %s\n\n") - wxT("Open Source Digital Voice\n\n") - wxT("For Help and Support visit: http://freedv.org\n\n") - - wxT("GNU Public License V2.1\n\n") - wxT("Copyright (c) David Witten KD0EAG and David Rowe VK5DGR\n\n") - wxT("svn revision: %s\n"), FREEDV_VERSION, SVN_REVISION); - - wxMessageBox(msg, wxT("About"), wxOK | wxICON_INFORMATION, this); -} - - -// Attempt to talk to rig using Hamlib - -bool MainFrame::OpenHamlibRig() { - if (wxGetApp().m_boolHamlibUseForPTT != true) - return false; - if (wxGetApp().m_intHamlibRig == 0) - return false; - if (wxGetApp().m_hamlib == NULL) - return false; - - int rig = wxGetApp().m_intHamlibRig; - wxString port = wxGetApp().m_strHamlibSerialPort; - bool status = wxGetApp().m_hamlib->connect(rig, port.mb_str(wxConvUTF8)); - if (status == false) - wxMessageBox("Couldn't connect to Radio with hamlib", wxT("Error"), wxOK | wxICON_ERROR, this); - - return status; -} - -//------------------------------------------------------------------------- -// OnTogBtnOnOff() -//------------------------------------------------------------------------- -void MainFrame::OnTogBtnOnOff(wxCommandEvent& event) -{ - wxString startStop = m_togBtnOnOff->GetLabel(); - - // we are attempting to start - - if (startStop.IsSameAs("Start")) - { - // - // Start Running ------------------------------------------------- - // - - // modify some button states when running - - m_togBtnSplit->Enable(); - m_togBtnAnalog->Enable(); - m_togBtnOnOff->SetLabel(wxT("Stop")); - m_btnTogPTT->Enable(); - m_togBtnVoiceKeyer->Enable(); - vk_state = VK_IDLE; - - m_rb1600->Disable(); - m_rb700b->Disable(); - m_rb700c->Disable(); - m_rb800xa->Disable(); - if (m_rbPlugIn != NULL) - m_rbPlugIn->Disable(); - - // determine what mode we are using - - if (m_rb1600->GetValue()) { - g_mode = FREEDV_MODE_1600; - g_Nc = 16; - m_panelScatter->setNc(g_Nc); - } - if (m_rb700b->GetValue()) { - g_mode = FREEDV_MODE_700B; - g_Nc = 14; - m_panelScatter->setNc(g_Nc/2-1); /* due to diversity, -1 due to no pilot like FreeDV 1600 */ - } - if (m_rb700c->GetValue()) { - g_mode = FREEDV_MODE_700C; - g_Nc = 14; - m_panelScatter->setNc(g_Nc/2-1); /* due to diversity, -1 due to no pilot like FreeDV 1600 */ - } - if (m_rb800xa->GetValue()) { - g_mode = FREEDV_MODE_800XA; - } - if (m_rbPlugIn != NULL) { - if (m_rbPlugIn->GetValue()) { - g_mode = -1; /* TODO; a better way of handling (enumarating?) non-freedv modes */ - - /* scale plots assuming Fs = 8000 Hz for now */ - - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ)/8000.0); - m_panelWaterfall->setFs(8000.0); - - (wxGetApp().m_plugin_startfp)(wxGetApp().m_plugInStates); - } - } - - if (g_mode != -1) { - // init freedv states - - g_pfreedv = freedv_open(g_mode); - freedv_set_callback_txt(g_pfreedv, &my_put_next_rx_char, &my_get_next_tx_char, NULL); - - freedv_set_callback_error_pattern(g_pfreedv, my_freedv_put_error_pattern, (void*)m_panelTestFrameErrors); - g_error_pattern_fifo = fifo_create(2*freedv_get_sz_error_pattern(g_pfreedv)); - g_error_hist = new short[FDMDV_NC_MAX*2]; - int i; - for(i=0; i<2*FDMDV_NC_MAX; i++) - g_error_hist[i] = 0; - - assert(g_pfreedv != NULL); - - // init Codec 2 LPC Post Filter - - codec2_set_lpc_post_filter(freedv_get_codec2(g_pfreedv), - wxGetApp().m_codec2LPCPostFilterEnable, - wxGetApp().m_codec2LPCPostFilterBassBoost, - wxGetApp().m_codec2LPCPostFilterBeta, - wxGetApp().m_codec2LPCPostFilterGamma); - - // Init Speex pre-processor states - // by inspecting Speex source it seems that only denoiser is on be default - - g_speex_st = speex_preprocess_state_init(freedv_get_n_speech_samples(g_pfreedv), FS); - - // adjust spectrum and waterfall freq scaling base on mode - - m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(freedv_get_modem_sample_rate(g_pfreedv)/2))); - m_panelWaterfall->setFs(freedv_get_modem_sample_rate(g_pfreedv)); - - // Init text msg decoding - - freedv_set_varicode_code_num(g_pfreedv, wxGetApp().m_textEncoding); - } - - modem_stats_open(&g_stats); - g_State = 0; - g_snr = 0.0; - g_half_duplex = wxGetApp().m_boolHalfDuplex; - - if (g_mode == FREEDV_MODE_800XA) { - m_panelScatter->setEyeScatter(PLOT_SCATTER_MODE_EYE); - } - else { - m_panelScatter->setEyeScatter(PLOT_SCATTER_MODE_SCATTER); - } - - m_pcallsign = m_callsign; - memset(m_callsign, 0, sizeof(m_callsign)); -#ifdef __UDP_EXPERIMENTAL__ - m_checksumGood = m_checksumBad = 0; - wxString s; - s.Printf("%d", m_checksumGood); - m_txtChecksumGood->SetLabel(s); - s.Printf("%d", m_checksumBad); - m_txtChecksumBad->SetLabel(s); -#endif - - m_maxLevel = 0; - m_textLevel->SetLabel(wxT("")); - m_gaugeLevel->SetValue(0); - - //printf("m_textEncoding = %d\n", wxGetApp().m_textEncoding); - //printf("g_stats.snr: %f\n", g_stats.snr_est); - - // attempt to start PTT ...... - - if (wxGetApp().m_boolHamlibUseForPTT) - OpenHamlibRig(); - if (wxGetApp().m_boolUseSerialPTT) { - OpenSerialPort(); - } - - // attempt to start sound cards and tx/rx processing - - startRxStream(); - - if (m_RxRunning) - { -#ifdef _USE_TIMER - m_plotTimer.Start(_REFRESH_TIMER_PERIOD, wxTIMER_CONTINUOUS); -#endif // _USE_TIMER - } -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"start"); processTxtEvent(e); -#endif - } - - // Stop was pressed or start up failed - - if (startStop.IsSameAs("Stop") || !m_RxRunning ) { - - // - // Stop Running ------------------------------------------------- - // - -#ifdef __UDP_EXPERIMENTAL__ - optionsDlg->SetSpamTimerLight(false); -#endif - -#ifdef _USE_TIMER - m_plotTimer.Stop(); -#endif // _USE_TIMER - - // ensure we are not transmitting and shut down audio processing - - if (wxGetApp().m_boolHamlibUseForPTT) { - Hamlib *hamlib = wxGetApp().m_hamlib; - wxString hamlibError; - if (wxGetApp().m_boolHamlibUseForPTT && hamlib != NULL) { - if (hamlib->ptt(false, hamlibError) == false) { - wxMessageBox(wxString("Hamlib PTT Error: ") + hamlibError, wxT("Error"), wxOK | wxICON_ERROR, this); - } - hamlib->close(); - } - } - - if (wxGetApp().m_boolUseSerialPTT) { - CloseSerialPort(); - } - - m_btnTogPTT->SetValue(false); - VoiceKeyerProcessEvent(VK_SPACE_BAR); - - stopRxStream(); - modem_stats_close(&g_stats); - - // free up states, clean up - - if (g_mode == -1) { - // PlugIn clean up - (wxGetApp().m_plugin_stopfp)(wxGetApp().m_plugInStates); - } - else { - // FreeDV clean up - delete g_error_hist; - fifo_destroy(g_error_pattern_fifo); - freedv_close(g_pfreedv); - speex_preprocess_state_destroy(g_speex_st); - } - - m_newMicInFilter = m_newSpkOutFilter = true; - - m_togBtnSplit->Disable(); - //m_togRxID->Disable(); - //m_togTxID->Disable(); - m_togBtnAnalog->Disable(); - m_btnTogPTT->Disable(); - m_togBtnVoiceKeyer->Disable(); - m_togBtnOnOff->SetLabel(wxT("Start")); - m_rb1600->Enable(); - m_rb700b->Enable(); - m_rb700c->Enable(); - m_rb800xa->Enable(); - if (m_rbPlugIn != NULL) - m_rbPlugIn->Enable(); - -#ifdef DISABLED_FEATURE - m_rb700->Enable(); - m_rb1400old->Enable(); - m_rb1600Wide->Enable(); - m_rb1400->Enable(); - m_rb2000->Enable(); -#endif -#ifdef __UDP_EXPERIMENTAL__ - char e[80]; sprintf(e,"stop"); processTxtEvent(e); -#endif - } -} - -//------------------------------------------------------------------------- -// stopRxStream() -//------------------------------------------------------------------------- -void MainFrame::stopRxStream() -{ - if(m_RxRunning) - { - m_RxRunning = false; - - fprintf(stderr, "waiting for thread to stop\n"); - m_txRxThread->m_run = 0; - m_txRxThread->Wait(); - fprintf(stderr, "thread stopped\n"); - - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(m_rxOutPa != m_rxInPa) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - - if (g_nSoundCards == 2) { - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - if(m_txInPa != m_txOutPa) { - m_txOutPa->stop(); - m_txOutPa->streamClose(); - delete m_txOutPa; - } - } - - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - } -} - -void MainFrame::destroy_fifos(void) -{ - fifo_destroy(g_rxUserdata->infifo1); - fifo_destroy(g_rxUserdata->outfifo1); - fifo_destroy(g_rxUserdata->infifo2); - fifo_destroy(g_rxUserdata->outfifo2); - fifo_destroy(g_rxUserdata->rxinfifo); - fifo_destroy(g_rxUserdata->rxoutfifo); -} - -void MainFrame::destroy_src(void) -{ - src_delete(g_rxUserdata->insrc1); - src_delete(g_rxUserdata->outsrc1); - src_delete(g_rxUserdata->insrc2); - src_delete(g_rxUserdata->outsrc2); - src_delete(g_rxUserdata->insrcsf); -} - -void MainFrame::initPortAudioDevice(PortAudioWrap *pa, int inDevice, int outDevice, - int soundCard, int sampleRate, int inputChannels) -{ - // Note all of the wrapper functions below just set values in a - // portaudio struct so can't return any errors. So no need to trap - // any errors in this function. - - // init input params - - pa->setInputDevice(inDevice); - if(inDevice != paNoDevice) { - pa->setInputChannelCount(inputChannels); // stereo input - pa->setInputSampleFormat(PA_SAMPLE_TYPE); - pa->setInputLatency(pa->getInputDefaultLowLatency()); - pa->setInputHostApiStreamInfo(NULL); - } - - pa->setOutputDevice(paNoDevice); - - // init output params - - pa->setOutputDevice(outDevice); - if(outDevice != paNoDevice) { - pa->setOutputChannelCount(2); // stereo output - pa->setOutputSampleFormat(PA_SAMPLE_TYPE); - pa->setOutputLatency(pa->getOutputDefaultLowLatency()); - pa->setOutputHostApiStreamInfo(NULL); - } - - // init params that affect input and output - - /* - On Linux, setting this to wxGetApp().m_framesPerBuffer caused - intermittant break up on the audio from my IC7200 on Ubuntu 14. - After a day of bug hunting I found that 0, as recommended by the - PortAudio docunmentation, fixed the problem. - */ - - //pa->setFramesPerBuffer(wxGetApp().m_framesPerBuffer); - pa->setFramesPerBuffer(0); - - pa->setSampleRate(sampleRate); - pa->setStreamFlags(paClipOff); -} - -//------------------------------------------------------------------------- -// startRxStream() -//------------------------------------------------------------------------- -void MainFrame::startRxStream() -{ - int src_error; - const PaDeviceInfo *deviceInfo1 = NULL, *deviceInfo2 = NULL; - int inputChannels1, inputChannels2; - bool two_rx=false; - bool two_tx=false; - - if(!m_RxRunning) { - m_RxRunning = true; - - if(Pa_Initialize()) - { - wxMessageBox(wxT("Port Audio failed to initialize"), wxT("Pa_Initialize"), wxOK); - } - - m_rxInPa = new PortAudioWrap(); - if(g_soundCard1InDeviceNum != g_soundCard1OutDeviceNum) - two_rx=true; - if(g_soundCard2InDeviceNum != g_soundCard2OutDeviceNum) - two_tx=true; - - fprintf(g_logfile, "two_rx: %d two_tx: %d\n", two_rx, two_tx); - if(two_rx) - m_rxOutPa = new PortAudioWrap(); - else - m_rxOutPa = m_rxInPa; - - if (g_nSoundCards == 0) { - wxMessageBox(wxT("No Sound Cards configured, use Tools - Audio Config to configure"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - - // Init Sound card 1 ---------------------------------------------- - // sanity check on sound card device numbers - - if ((m_rxInPa->getDeviceCount() <= g_soundCard1InDeviceNum) || - (m_rxOutPa->getDeviceCount() <= g_soundCard1OutDeviceNum)) { - wxMessageBox(wxT("Sound Card 1 not present"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - - // work out how many input channels this device supports. - - deviceInfo1 = Pa_GetDeviceInfo(g_soundCard1InDeviceNum); - if (deviceInfo1 == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card 1"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - m_RxRunning = false; - return; - } - if (deviceInfo1->maxInputChannels == 1) - inputChannels1 = 1; - else - inputChannels1 = 2; - - if(two_rx) { - initPortAudioDevice(m_rxInPa, g_soundCard1InDeviceNum, paNoDevice, 1, - g_soundCard1SampleRate, inputChannels1); - initPortAudioDevice(m_rxOutPa, paNoDevice, g_soundCard1OutDeviceNum, 1, - g_soundCard1SampleRate, inputChannels1); - } - else - initPortAudioDevice(m_rxInPa, g_soundCard1InDeviceNum, g_soundCard1OutDeviceNum, 1, - g_soundCard1SampleRate, inputChannels1); - - // Init Sound Card 2 ------------------------------------------------ - - if (g_nSoundCards == 2) { - - m_txInPa = new PortAudioWrap(); - if(two_tx) - m_txOutPa = new PortAudioWrap(); - else - m_txOutPa = m_txInPa; - - // sanity check on sound card device numbers - - //printf("m_txInPa->getDeviceCount(): %d\n", m_txInPa->getDeviceCount()); - //printf("g_soundCard2InDeviceNum: %d\n", g_soundCard2InDeviceNum); - //printf("g_soundCard2OutDeviceNum: %d\n", g_soundCard2OutDeviceNum); - - if ((m_txInPa->getDeviceCount() <= g_soundCard2InDeviceNum) || - (m_txOutPa->getDeviceCount() <= g_soundCard2OutDeviceNum)) { - wxMessageBox(wxT("Sound Card 2 not present"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - m_RxRunning = false; - return; - } - - deviceInfo2 = Pa_GetDeviceInfo(g_soundCard2InDeviceNum); - if (deviceInfo2 == NULL) { - wxMessageBox(wxT("Couldn't get device info from Port Audio for Sound Card 1"), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - m_RxRunning = false; - return; - } - if (deviceInfo2->maxInputChannels == 1) - inputChannels2 = 1; - else - inputChannels2 = 2; - - if(two_tx) { - initPortAudioDevice(m_txInPa, g_soundCard2InDeviceNum, paNoDevice, 2, - g_soundCard2SampleRate, inputChannels2); - initPortAudioDevice(m_txOutPa, paNoDevice, g_soundCard2OutDeviceNum, 2, - g_soundCard2SampleRate, inputChannels2); - } - else - initPortAudioDevice(m_txInPa, g_soundCard2InDeviceNum, g_soundCard2OutDeviceNum, 2, - g_soundCard2SampleRate, inputChannels2); - } - - // Init call back data structure ---------------------------------------------- - - g_rxUserdata = new paCallBackData; - g_rxUserdata->inputChannels1 = inputChannels1; - if (deviceInfo2 != NULL) - g_rxUserdata->inputChannels2 = inputChannels2; - - // init sample rate conversion states - - g_rxUserdata->insrc1 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrc1 != NULL); - g_rxUserdata->outsrc1 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->outsrc1 != NULL); - g_rxUserdata->insrc2 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrc2 != NULL); - g_rxUserdata->outsrc2 = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->outsrc2 != NULL); - - g_rxUserdata->insrcsf = src_new(SRC_SINC_FASTEST, 1, &src_error); - assert(g_rxUserdata->insrcsf != NULL); - - // create FIFOs used to interface between different buffer sizes - - g_rxUserdata->infifo1 = fifo_create(8*N48); - g_rxUserdata->outfifo1 = fifo_create(10*N48); - g_rxUserdata->outfifo2 = fifo_create(8*N48); - g_rxUserdata->infifo2 = fifo_create(8*N48); - printf("N48: %d\n", N48); - - g_rxUserdata->rxinfifo = fifo_create(10 * N8); - g_rxUserdata->rxoutfifo = fifo_create(10 * N8); - - // Init Equaliser Filters ------------------------------------------------------ - - m_newMicInFilter = m_newSpkOutFilter = true; - designEQFilters(g_rxUserdata); - g_rxUserdata->micInEQEnable = wxGetApp().m_MicInEQEnable; - g_rxUserdata->spkOutEQEnable = wxGetApp().m_SpkOutEQEnable; - - // optional tone in left channel to reliably trigger vox - - g_rxUserdata->leftChannelVoxTone = wxGetApp().m_leftChannelVoxTone; - g_rxUserdata->voxTonePhase = 0; - - // Start sound card 1 ---------------------------------------------------------- - - m_rxInPa->setUserData(g_rxUserdata); - m_rxErr = m_rxInPa->setCallback(rxCallback); - - m_rxErr = m_rxInPa->streamOpen(); - - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Open/Setup error."), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - m_rxErr = m_rxInPa->streamStart(); - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Stream Start Error."), wxT("Error"), wxOK); - delete m_rxInPa; - if(two_rx) - delete m_rxOutPa; - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - // Start separate output stream if needed - - if(two_rx) { - m_rxOutPa->setUserData(g_rxUserdata); - m_rxErr = m_rxOutPa->setCallback(rxCallback); - - m_rxErr = m_rxOutPa->streamOpen(); - - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Second Stream Open/Setup error."), wxT("Error"), wxOK); - delete m_rxInPa; - delete m_rxOutPa; - delete m_txOutPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - m_rxErr = m_rxOutPa->streamStart(); - if(m_rxErr != paNoError) { - wxMessageBox(wxT("Sound Card 1 Second Stream Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - delete m_rxOutPa; - delete m_txOutPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - } - - // Start sound card 2 ---------------------------------------------------------- - - if (g_nSoundCards == 2) { - - // question: can we use same callback data - // (g_rxUserdata)or both sound card callbacks? Is there a - // chance of them both being called at the same time? We - // could need a mutex ... - - m_txInPa->setUserData(g_rxUserdata); - m_txErr = m_txInPa->setCallback(txCallback); - m_txErr = m_txInPa->streamOpen(); - - if(m_txErr != paNoError) { - fprintf(stderr, "Err: %d\n", m_txErr); - wxMessageBox(wxT("Sound Card 2 Open/Setup error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - m_txErr = m_txInPa->streamStart(); - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - if(two_tx) - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - - // Start separate output stream if needed - - if (two_tx) { - - // question: can we use same callback data - // (g_rxUserdata)or both sound card callbacks? Is there a - // chance of them both being called at the same time? We - // could need a mutex ... - - m_txOutPa->setUserData(g_rxUserdata); - m_txErr = m_txOutPa->setCallback(txCallback); - m_txErr = m_txOutPa->streamOpen(); - - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Second Stream Open/Setup error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - delete m_rxInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - m_txErr = m_txOutPa->streamStart(); - if(m_txErr != paNoError) { - wxMessageBox(wxT("Sound Card 2 Second Stream Start Error."), wxT("Error"), wxOK); - m_rxInPa->stop(); - m_rxInPa->streamClose(); - m_txInPa->stop(); - m_txInPa->streamClose(); - delete m_txInPa; - if(two_rx) { - m_rxOutPa->stop(); - m_rxOutPa->streamClose(); - delete m_rxOutPa; - } - delete m_txInPa; - delete m_txOutPa; - destroy_fifos(); - destroy_src(); - deleteEQFilters(g_rxUserdata); - delete g_rxUserdata; - m_RxRunning = false; - return; - } - } - } - - // start tx/rx processing thread - - m_txRxThread = new txRxThread; - - if ( m_txRxThread->Create() != wxTHREAD_NO_ERROR ) - { - wxLogError(wxT("Can't create thread!")); - } - - m_txRxThread->SetPriority(WXTHREAD_MAX_PRIORITY); - - if ( m_txRxThread->Run() != wxTHREAD_NO_ERROR ) - { - wxLogError(wxT("Can't start thread!")); - } - - } -} - - -void MainFrame::processTxtEvent(char event[]) { - int rule = 0; - - //printf("processTxtEvent:\n"); - //printf(" event: %s\n", event); - - // process with regexp and issue system command - - // Each regexp in our list is separated by a newline. We want to try all of them. - - wxString event_str(event); - int match_end, replace_end; - match_end = replace_end = 0; - wxString regexp_match_list = wxGetApp().m_events_regexp_match; - wxString regexp_replace_list = wxGetApp().m_events_regexp_replace; - - bool found_match = false; - - while (((match_end = regexp_match_list.Find('\n')) != wxNOT_FOUND) && (rule < MAX_EVENT_RULES)) { - //printf("match_end: %d\n", match_end); - if ((replace_end = regexp_replace_list.Find('\n')) != wxNOT_FOUND) { - //printf("replace_end = %d\n", replace_end); - - // candidate match and replace regexps strings exist, so lets try them - - wxString regexp_match = regexp_match_list.SubString(0, match_end-1); - wxString regexp_replace = regexp_replace_list.SubString(0, replace_end-1); - //printf("match: %s replace: %s\n", (const char *)regexp_match.c_str(), (const char *)regexp_replace.c_str()); - wxRegEx re(regexp_match); - //printf(" checking for match against: %s\n", (const char *)regexp_match.c_str()); - - // if we found a match, lets run the replace regexp and issue the system command - - wxString event_str_rep = event_str; - - if (re.Replace(&event_str_rep, regexp_replace) != 0) { - fprintf(stderr, " found match! event_str: %s\n", (const char *)event_str.c_str()); - found_match = true; - - bool enableSystem = false; - if (wxGetApp().m_events) - enableSystem = true; - - // no syscall if spam timer still running - - if (spamTimer[rule].IsRunning()) { - enableSystem = false; - fprintf(stderr, " spam timer running\n"); - } - - const char *event_out = event_str_rep.ToUTF8(); - wxString event_out_with_return_code; - - if (enableSystem) { - int ret = wxExecute(event_str_rep); - event_out_with_return_code.Printf(_T("%s -> returned %d"), event_out, ret); - spamTimer[rule].Start((wxGetApp().m_events_spam_timer)*1000, wxTIMER_ONE_SHOT); - } - else - event_out_with_return_code.Printf(_T("%s T: %d"), event_out, spamTimer[rule].IsRunning()); - - // update event log GUI if currently displayed - - if (optionsDlg != NULL) { - optionsDlg->updateEventLog(wxString(event), event_out_with_return_code); - } - } - } - regexp_match_list = regexp_match_list.SubString(match_end+1, regexp_match_list.length()); - regexp_replace_list = regexp_replace_list.SubString(replace_end+1, regexp_replace_list.length()); - - rule++; - } - - if ((optionsDlg != NULL) && !found_match) { - optionsDlg->updateEventLog(wxString(event), _("")); - } -} - - -#define SBQ_MAX_ARGS 4 - -void *MainFrame::designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q) -{ - char *arg[SBQ_MAX_ARGS]; - char argstorage[SBQ_MAX_ARGS][80]; - void *sbq; - int i, argc; - - assert((strcmp(filterType, "bass") == 0) || - (strcmp(filterType, "treble") == 0) || - (strcmp(filterType, "equalizer") == 0)); - - for(i=0; isbqMicInBass = designAnEQFilter("bass", wxGetApp().m_MicInBassFreqHz, wxGetApp().m_MicInBassGaindB); - cb->sbqMicInTreble = designAnEQFilter("treble", wxGetApp().m_MicInTrebleFreqHz, wxGetApp().m_MicInTrebleGaindB); - cb->sbqMicInMid = designAnEQFilter("equalizer", wxGetApp().m_MicInMidFreqHz, wxGetApp().m_MicInMidGaindB, wxGetApp().m_MicInMidQ); - } - - // init Spk Out Equaliser Filters - - if (m_newSpkOutFilter) { - //printf("designing new Spk Out filters\n"); - //printf("designEQFilters: wxGetApp().m_SpkOutBassFreqHz: %f\n",wxGetApp().m_SpkOutBassFreqHz); - cb->sbqSpkOutBass = designAnEQFilter("bass", wxGetApp().m_SpkOutBassFreqHz, wxGetApp().m_SpkOutBassGaindB); - cb->sbqSpkOutTreble = designAnEQFilter("treble", wxGetApp().m_SpkOutTrebleFreqHz, wxGetApp().m_SpkOutTrebleGaindB); - cb->sbqSpkOutMid = designAnEQFilter("equalizer", wxGetApp().m_SpkOutMidFreqHz, wxGetApp().m_SpkOutMidGaindB, wxGetApp().m_SpkOutMidQ); - } -} - -void MainFrame::deleteEQFilters(paCallBackData *cb) -{ - if (m_newMicInFilter) { - sox_biquad_destroy(cb->sbqMicInBass); - sox_biquad_destroy(cb->sbqMicInTreble); - sox_biquad_destroy(cb->sbqMicInMid); - } - if (m_newSpkOutFilter) { - sox_biquad_destroy(cb->sbqSpkOutBass); - sox_biquad_destroy(cb->sbqSpkOutTreble); - sox_biquad_destroy(cb->sbqSpkOutMid); - } -} - -// returns number of output samples generated by resampling -int resample(SRC_STATE *src, - short output_short[], - short input_short[], - int output_sample_rate, - int input_sample_rate, - int length_output_short, // maximum output array length in samples - int length_input_short - ) -{ - SRC_DATA src_data; - float input[N48*4]; - float output[N48*4]; - int ret; - - assert(src != NULL); - assert(length_input_short <= N48*4); - assert(length_output_short <= N48*4); - - src_short_to_float_array(input_short, input, length_input_short); - - src_data.data_in = input; - src_data.data_out = output; - src_data.input_frames = length_input_short; - src_data.output_frames = length_output_short; - src_data.end_of_input = 0; - src_data.src_ratio = (float)output_sample_rate/input_sample_rate; - - ret = src_process(src, &src_data); - assert(ret == 0); - - assert(src_data.output_frames_gen <= length_output_short); - src_float_to_short_array(output, output_short, src_data.output_frames_gen); - - return src_data.output_frames_gen; -} - - -// Decimates samples using an algorithm that produces nice plots of -// speech signals at a low sample rate. We want a low sample rate so -// we don't hammer the graphics system too hard. Saves decimated data -// to a fifo for plotting on screen. -void resample_for_plot(struct FIFO *plotFifo, short buf[], int length, int fs) -{ - int decimation = fs/WAVEFORM_PLOT_FS; - int nSamples, sample; - int i, st, en, max, min; - short dec_samples[length]; - - nSamples = length/decimation; - - for(sample = 0; sample < nSamples; sample += 2) - { - st = decimation*sample; - en = decimation*(sample+2); - max = min = 0; - for(i=st; i buf[i]) min = buf[i]; - } - dec_samples[sample] = max; - dec_samples[sample+1] = min; - } - fifo_write(plotFifo, dec_samples, nSamples); -} - -void txRxProcessing() -{ - - paCallBackData *cbData = g_rxUserdata; - - // Buffers re-used by tx and rx processing - // signals in in48k/out48k are at a maximum sample rate of 48k, could be 44.1kHz - // depending on sound hardware. - - short in8k_short[4*N8]; - short in48k_short[4*N48]; - short out8k_short[4*N8]; - short out48k_short[4*N48]; - int nout, samplerate, n_samples; - - //fprintf(g_logfile, "start infifo1: %5d outfifo2: %5d\n", fifo_used(cbData->infifo1), fifo_used(cbData->outfifo2)); - - // FreeDV 700 uses a modem sample rate of 7500 Hz which requires some special treatment - - if (g_analog || g_mode == -1) - samplerate = FS; - else - samplerate = freedv_get_modem_sample_rate(g_pfreedv); - - // - // RX side processing -------------------------------------------- - // - - // while we have enough input samples available ... - - int nsam = g_soundCard1SampleRate * (float)N8/FS; - assert(nsam <= N48); - g_mutexProtectingCallbackData.Lock(); - while ((fifo_read(cbData->infifo1, in48k_short, nsam) == 0) && ((g_half_duplex && !g_tx) || !g_half_duplex)) - { - g_mutexProtectingCallbackData.Unlock(); - unsigned int n8k; - - n8k = resample(cbData->insrc1, in8k_short, in48k_short, samplerate, g_soundCard1SampleRate, N8, nsam); - assert(n8k <= N8); - - // optionally save "from radio" signal (write demod input to file) - // Really useful for testing and development as it allows us - // to repeat tests using off air signals - - g_mutexProtectingCallbackData.Lock(); - if (g_recFileFromRadio && (g_sfRecFile != NULL)) { - //printf("g_recFromRadioSamples: %d n8k: %d \n", g_recFromRadioSamples); - if (g_recFromRadioSamples < n8k) { - sf_write_short(g_sfRecFile, in8k_short, g_recFromRadioSamples); - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_recFileFromRadioEventId ); - // call stop/start record menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - g_recFromRadioSamples = 0; - //printf("finished recording g_recFromRadioSamples: %d n8k: %d!\n", g_recFileFromRadio, n8k); - } - else { - sf_write_short(g_sfRecFile, in8k_short, n8k); - g_recFromRadioSamples -= n8k; - } - } - g_mutexProtectingCallbackData.Unlock(); - - // optionally read "from radio" signal from file (read demod input from file) - - g_mutexProtectingCallbackData.Lock(); - if (g_playFileFromRadio && (g_sfPlayFileFromRadio != NULL)) { - unsigned int nsf = n8k*g_sfFs/samplerate; - short insf_short[nsf]; - unsigned int n = sf_read_short(g_sfPlayFileFromRadio, insf_short, nsf); - n8k = resample(cbData->insrcsf, in8k_short, insf_short, samplerate, g_sfFs, N8, nsf); - //fprintf(g_logfile, "n: %d nsf: %d n8k: %d samplerate: %d\n", n, nsf, n8k, samplerate); - assert(n8k <= N8); - - if (n == 0) { - if (g_loopPlayFileFromRadio) - sf_seek(g_sfPlayFileFromRadio, 0, SEEK_SET); - else { - printf("playFileFromRadio finished, issuing event!\n"); - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_playFileFromRadioEventId ); - // call stop/start play menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - } - } - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotDemodInFifo, in8k_short, n8k, samplerate); - - if (g_mode != -1) { - // send latest squelch level to FreeDV API, as it handles squelch internally - - freedv_set_squelch_en(g_pfreedv, g_SquelchActive); - freedv_set_snr_squelch_thresh(g_pfreedv, g_SquelchLevel); - } - - //fprintf(g_logfile, "snr_squelch_thresh: %f\n", g_pfreedv->snr_squelch_thresh); - - // compute rx spectrum - do here so update rate in constant - - COMP rx_fdm[n8k]; - float rx_spec[MODEM_STATS_NSPEC]; - unsigned int i; - - for(i=0; irxinfifo, in8k_short, n8k); - per_frame_rx_processing(cbData->rxoutfifo, cbData->rxinfifo); - memset(out8k_short, 0, sizeof(short)*N8); - fifo_read(cbData->rxoutfifo, out8k_short, N8); - //printf("out8k_short: %d\n", out8k_short[0]); - } - - - // Optional Spk Out EQ Filtering, need mutex as filter can change at run time - g_mutexProtectingCallbackData.Lock(); - if (cbData->spkOutEQEnable) { - sox_biquad_filter(cbData->sbqSpkOutBass, out8k_short, out8k_short, N8); - sox_biquad_filter(cbData->sbqSpkOutTreble, out8k_short, out8k_short, N8); - sox_biquad_filter(cbData->sbqSpkOutMid, out8k_short, out8k_short, N8); - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotSpeechOutFifo, out8k_short, N8, FS); - - g_mutexProtectingCallbackData.Lock(); - if (g_nSoundCards == 1) { - nout = resample(cbData->outsrc2, out48k_short, out8k_short, g_soundCard1SampleRate, FS, N48, N8); - fifo_write(cbData->outfifo1, out48k_short, nout); - } - else { - nout = resample(cbData->outsrc2, out48k_short, out8k_short, g_soundCard2SampleRate, FS, N48, N8); - fifo_write(cbData->outfifo2, out48k_short, nout); - } - } - g_mutexProtectingCallbackData.Unlock(); - - // - // TX side processing -------------------------------------------- - // - - if ((g_mode != -1) && ((g_nSoundCards == 2) && ((g_half_duplex && g_tx) || !g_half_duplex))) { - int ret; - - // Make sure we have q few frames of modulator output - // samples. This also locks the modulator to the sample rate - // of sound card 1. We want to make sure that modulator - // samples are uninterrupted by differences in sample rate - // between this sound card and sound card 2. - - g_mutexProtectingCallbackData.Lock(); - while((unsigned)fifo_used(cbData->outfifo1) < 6*N48) - { - g_mutexProtectingCallbackData.Unlock(); - - int nsam = g_soundCard2SampleRate * freedv_get_n_speech_samples(g_pfreedv)/FS; - assert(nsam <= 4*N48); - - // infifo2 is written to by another sound card so it may - // over or underflow, but we don't realy care. It will - // just result in a short interruption in audio being fed - // to codec2_enc, possibly making a click every now and - // again in the decoded audio at the other end. - - // zero speech input just in case infifo2 underflows - memset(in48k_short, 0, nsam*sizeof(short)); - fifo_read(cbData->infifo2, in48k_short, nsam); - - nout = resample(cbData->insrc2, in8k_short, in48k_short, FS, g_soundCard2SampleRate, 4*N8, nsam); - - // optionally use file for mic input signal - - g_mutexProtectingCallbackData.Lock(); - if (g_playFileToMicIn && (g_sfPlayFile != NULL)) { - int n = sf_read_short(g_sfPlayFile, in8k_short, nout); - //fprintf(stderr, "n: %d nout: %d\n", n, nout); - if (n != nout) { - if (g_loopPlayFileToMicIn) - sf_seek(g_sfPlayFile, 0, SEEK_SET); - else { - wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, g_playFileToMicInEventId ); - // call stop/start play menu item, should be thread safe - g_parent->GetEventHandler()->AddPendingEvent( event ); - } - } - } - g_mutexProtectingCallbackData.Unlock(); - - // Optional Speex pre-processor for acoustic noise reduction - - if (wxGetApp().m_speexpp_enable) { - speex_preprocess_run(g_speex_st, in8k_short); - } - - // Optional Mic In EQ Filtering, need mutex as filter can change at run time - - g_mutexProtectingCallbackData.Lock(); - if (cbData->micInEQEnable) { - sox_biquad_filter(cbData->sbqMicInBass, in8k_short, in8k_short, nout); - sox_biquad_filter(cbData->sbqMicInTreble, in8k_short, in8k_short, nout); - sox_biquad_filter(cbData->sbqMicInMid, in8k_short, in8k_short, nout); - } - g_mutexProtectingCallbackData.Unlock(); - - resample_for_plot(g_plotSpeechInFifo, in8k_short, nout, FS); - - n_samples = freedv_get_n_nom_modem_samples(g_pfreedv); - - if (g_analog) { - n_samples = freedv_get_n_speech_samples(g_pfreedv); - - // Boost the "from mic" -> "to radio" audio in analog - // mode. The need for the gain was found by - // experiment - analog SSB sounded too quiet compared - // to digital. With digital voice we generally drive - // the "to radio" (SSB radio mic input) at about 25% - // of the peak level for normal SSB voice. So we - // introduce 6dB gain to make analog SSB sound the - // same level as the digital. Watch out for clipping. - for(int i=0; i 32767) out = 32767.0; - if (out < -32767) out = -32767.0; - out8k_short[i] = out; - } - } - else { - COMP tx_fdm[freedv_get_n_nom_modem_samples(g_pfreedv)]; - COMP tx_fdm_offset[freedv_get_n_nom_modem_samples(g_pfreedv)]; - int i; - - if (g_mode == FREEDV_MODE_800XA) { - /* 800XA doesn't support complex output just yet */ - freedv_tx(g_pfreedv, out8k_short, in8k_short); - } - else { - freedv_comptx(g_pfreedv, tx_fdm, in8k_short); - - freq_shift_coh(tx_fdm_offset, tx_fdm, g_TxFreqOffsetHz, freedv_get_modem_sample_rate(g_pfreedv), &g_TxFreqOffsetPhaseRect, freedv_get_n_nom_modem_samples(g_pfreedv)); - for(i=0; ioutsrc1, out48k_short, out8k_short, g_soundCard1SampleRate, samplerate, N48*4, n_samples); - g_mutexProtectingCallbackData.Lock(); - ret = fifo_write(cbData->outfifo1, out48k_short, nout); - //fprintf(stderr,"nout: %d ret: %d N48*4: %d\n", nout, ret, N48*4); - - assert(ret != -1); - } - g_mutexProtectingCallbackData.Unlock(); - } - - //fprintf(g_logfile, " end infifo1: %5d outfifo2: %5d\n", fifo_used(cbData->infifo1), fifo_used(cbData->outfifo2)); - -} - -//---------------------------------------------------------------- -// per_frame_rx_processing() -//---------------------------------------------------------------- - -void per_frame_rx_processing( - FIFO *output_fifo, // decoded speech samples - FIFO *input_fifo - ) -{ - #ifdef OLDSPEC - float rx_spec[MODEM_STATS_NSPEC]; - #endif - int i; - - if (g_mode == -1) { - // PlugIn processing --------------------------------------------------- - - int nin = 160; // TODO: hard code for now - some sort of plugin supplied param in future - short input_buf[nin]; - - while (fifo_read(input_fifo, input_buf, nin) == 0) { - (wxGetApp().m_plugin_rx_samplesfp)(wxGetApp().m_plugInStates, input_buf, nin); - } - - #ifdef OLD_SPEC - COMP rx_fdm[nin]; - - for(i=0; isnr); - - // grab extended stats so we can plot spectrum, scatter diagram etc - - freedv_get_modem_extended_stats(g_pfreedv, &g_stats); - - #ifdef OLD_SPEC - // compute rx spectrum - - modem_stats_get_rx_spectrum(&g_stats, rx_spec, rx_fdm, nin_prev); - - // Average rx spectrum data using a simple IIR low pass filter - - for(i = 0; iinputChannels1) - indata[i] = rptr[0]; - if (fifo_write(cbData->infifo1, indata, framesPerBuffer)) { - //fprintf(g_logfile, "infifo1 full\n"); - } - //fifo_write(cbData->outfifo1, indata, framesPerBuffer); - } - - // OK now set up output samples for this callback - - if(wptr) { - //fprintf(g_logfile,"out %ld %d\n", framesPerBuffer, g_out++); - if (fifo_read(cbData->outfifo1, outdata, framesPerBuffer) == 0) { - - // write signal to both channels - - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - if (cbData->leftChannelVoxTone) { - cbData->voxTonePhase += 2.0*M_PI*VOX_TONE_FREQ/g_soundCard1SampleRate; - cbData->voxTonePhase -= 2.0*M_PI*floor(cbData->voxTonePhase/(2.0*M_PI)); - wptr[0] = VOX_TONE_AMP*cos(cbData->voxTonePhase); - //printf("%f %d\n", cbData->voxTonePhase, wptr[0]); - } - else - wptr[0] = outdata[i]; - - wptr[1] = outdata[i]; - } - } - else { - //fprintf(g_logfile, "outfifo1 empty\n"); - // zero output if no data available - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = 0; - wptr[1] = 0; - } - } - } - - return paContinue; -} - - -//------------------------------------------------------------------------- -// txCallback() -//------------------------------------------------------------------------- -int MainFrame::txCallback( - const void *inputBuffer, - void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ) -{ - paCallBackData *cbData = (paCallBackData*)userData; - unsigned int i; - short *rptr = (short*)inputBuffer; - short *wptr = (short*)outputBuffer; - short indata[MAX_FPB]; - short outdata[MAX_FPB]; - - wxMutexLocker lock(g_mutexProtectingCallbackData); - - // if (statusFlags) - // printf("cb2 statusFlags: 0x%x\n", (int)statusFlags); - - // assemble a mono buffer and write to FIFO - - assert(framesPerBuffer < MAX_FPB); - - if(rptr) { - for(i = 0; i < framesPerBuffer; i++, rptr += cbData->inputChannels2) - indata[i] = rptr[0]; - } - - //#define SC2_LOOPBACK -#ifdef SC2_LOOPBACK - //TODO: This doesn't work unless using the same soundcard! - - if(wptr) { - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = indata[i]; - wptr[1] = indata[i]; - } - } - -#else - if(rptr) { - if (fifo_write(cbData->infifo2, indata, framesPerBuffer)) { - //fprintf(g_logfile, "infifo2 full\n"); - } - } - - // OK now set up output samples for this callback - - if(wptr) { - if (fifo_read(cbData->outfifo2, outdata, framesPerBuffer) == 0) { - - // write signal to both channels */ - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = outdata[i]; - wptr[1] = outdata[i]; - } - } - else { - //fprintf(g_logfile, "outfifo2 empty\n"); - // zero output if no data available - for(i = 0; i < framesPerBuffer; i++, wptr += 2) { - wptr[0] = 0; - wptr[1] = 0; - } - } - } -#endif - return paContinue; -} - -// Callback from plot_spectrum & plot_waterfall. would be nice to -// work out a way to do this without globals. - -void fdmdv2_clickTune(float freq) { - - // The demod is hard-wired to expect a centre frequency of - // FDMDV_FCENTRE. So we want to take the signal centered on the - // click tune freq and re-centre it on FDMDV_FCENTRE. For example - // if the click tune freq is 1500Hz, and FDMDV_CENTRE is 1200 Hz, - // we need to shift the input signal centred on 1500Hz down to - // 1200Hz, an offset of -300Hz. - - // Bit of an "indent" as we are often trying to get it back - // exactly in the centre - - if (fabs(FDMDV_FCENTRE - freq) < 10.0) { - freq = FDMDV_FCENTRE; - fprintf(stderr, "indent!\n"); - } - - if (g_split) { - g_RxFreqOffsetHz = FDMDV_FCENTRE - freq; - } - else { - g_TxFreqOffsetHz = freq - FDMDV_FCENTRE; - g_RxFreqOffsetHz = FDMDV_FCENTRE - freq; - } -} - -//---------------------------------------------------------------- -// OpenSerialPort() -//---------------------------------------------------------------- - -void MainFrame::OpenSerialPort(void) -{ - Serialport *serialport = wxGetApp().m_serialport; - - if(!wxGetApp().m_strRigCtrlPort.IsEmpty()) { - serialport->openport(wxGetApp().m_strRigCtrlPort.c_str(), - wxGetApp().m_boolUseRTS, - wxGetApp().m_boolRTSPos, - wxGetApp().m_boolUseDTR, - wxGetApp().m_boolDTRPos); - if (serialport->isopen()) { - // always start PTT in Rx state - serialport->ptt(false); - } - else { - wxMessageBox("Couldn't open Serial Port", wxT("About"), wxOK | wxICON_ERROR, this); - } - } -} - - -//---------------------------------------------------------------- -// CloseSerialPort() -//---------------------------------------------------------------- - -void MainFrame::CloseSerialPort(void) -{ - Serialport *serialport = wxGetApp().m_serialport; - if (serialport->isopen()) { - // always end with PTT in rx state - - serialport->ptt(false); - serialport->closeport(); - } -} - - -#ifdef __UDP_SUPPORT__ - -//---------------------------------------------------------------- -// PollUDP() - see if any commands on UDP port -//---------------------------------------------------------------- - -// test this puppy with netcat: -// $ echo "hello" | nc -u -q1 localhost 3000 - -int MainFrame::PollUDP(void) -{ - // this will block until message received, so we put it in it's own thread - - char buf[1024]; - char reply[80]; - size_t n = m_udp_sock->RecvFrom(m_udp_addr, buf, sizeof(buf)).LastCount(); - - if (n) { - wxString bufstr = wxString::From8BitData(buf, n); - bufstr.Trim(); - wxString ipaddr = m_udp_addr.IPAddress(); - printf("Received: \"%s\" from %s:%u\n", - (const char *)bufstr.c_str(), - (const char *)ipaddr.c_str(), m_udp_addr.Service()); - - // for security only accept commands from local host - - sprintf(reply,"nope\n"); - if (ipaddr.Cmp(_("127.0.0.1")) == 0) { - - // process commands - - if (bufstr.Cmp(_("restore")) == 0) { - m_schedule_restore = true; // Make Restore happen in main thread to avoid crashing - sprintf(reply,"ok\n"); - } - - wxString itemToSet, val; - if (bufstr.StartsWith(_("set "), &itemToSet)) { - if (itemToSet.StartsWith("txtmsg ", &val)) { - // note: if options dialog is open this will get overwritten - wxGetApp().m_callSign = val; - } - sprintf(reply,"ok\n"); - } - if (bufstr.StartsWith(_("ptton"), &itemToSet)) { - // note: if options dialog is open this will get overwritten - m_btnTogPTT->SetValue(true); - togglePTT(); - sprintf(reply,"ok\n"); - } - if (bufstr.StartsWith(_("pttoff"), &itemToSet)) { - // note: if options dialog is open this will get overwritten - m_btnTogPTT->SetValue(false); - togglePTT(); - sprintf(reply,"ok\n"); - } - - } - else { - printf("We only accept messages from locahost!\n"); - } - - if ( m_udp_sock->SendTo(m_udp_addr, reply, strlen(reply)).LastCount() != strlen(reply)) { - printf("ERROR: failed to send data\n"); - } - } - - return n; -} - -void MainFrame::startUDPThread(void) { - fprintf(stderr, "starting UDP thread!\n"); - m_UDPThread = new UDPThread; - m_UDPThread->mf = this; - if (m_UDPThread->Create() != wxTHREAD_NO_ERROR ) { - wxLogError(wxT("Can't create thread!")); - } - if (m_UDPThread->Run() != wxTHREAD_NO_ERROR ) { - wxLogError(wxT("Can't start thread!")); - delete m_UDPThread; - } -} - -void MainFrame::stopUDPThread(void) { - printf("stopping UDP thread!\n"); - if ((m_UDPThread != NULL) && m_UDPThread->m_run) { - m_UDPThread->m_run = 0; - m_UDPThread->Wait(); - m_UDPThread = NULL; - } -} - -void *UDPThread::Entry() { - printf("UDP thread started!\n"); - while (m_run) { - if (wxGetApp().m_udp_enable) { - printf("m_udp_enable\n"); - mf->m_udp_addr.Service(wxGetApp().m_udp_port); - mf->m_udp_sock = new wxDatagramSocket(mf->m_udp_addr, wxSOCKET_NOWAIT); - - while (m_run && wxGetApp().m_udp_enable) { - if (mf->PollUDP() == 0) { - wxThread::Sleep(20); - } - } - - delete mf->m_udp_sock; - } - wxThread::Sleep(20); - } - return NULL; -} - -#endif - -char my_get_next_tx_char(void *callback_state) { - short ch = 0; - - fifo_read(g_txDataInFifo, &ch, 1); - //fprintf(stderr, "get_next_tx_char: %c\n", (char)ch); - return (char)ch; -} - -void my_put_next_rx_char(void *callback_state, char c) { - short ch = (short)c; - //fprintf(stderr, "put_next_rx_char: %c\n", (char)c); - fifo_write(g_rxDataOutFifo, &ch, 1); -} - -// Callback from FreeDv API to update error plots - -void my_freedv_put_error_pattern(void *state, short error_pattern[], int sz_error_pattern) { - fifo_write(g_error_pattern_fifo, error_pattern, sz_error_pattern); -} - -void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin) -{ - COMP foff_rect; - float mag; - int i; - - foff_rect.real = cosf(2.0*M_PI*foff/Fs); - foff_rect.imag = sinf(2.0*M_PI*foff/Fs); - for(i=0; ireal /= mag; - foff_phase_rect->imag /= mag; -} - -int plugin_get_persistant(char name[], char value[]) { - wxString n,v; - int i; - - for(i=0; i. -// -//========================================================================== -#ifndef __FDMDV2_MAIN__ -#define __FDMDV2_MAIN__ - -#include "version.h" -#ifndef _NO_AUTOTOOLS_ -#include "../config.h" -#endif -#include - -#include -#include -#include "wx/rawbmp.h" -#include "wx/file.h" -#include "wx/filename.h" -#include "wx/config.h" -#include -#include "wx/graphics.h" -#include "wx/mstream.h" -#include "wx/wfstream.h" -#include "wx/quantize.h" -#include "wx/scopedptr.h" -#include "wx/stopwatch.h" -#include "wx/versioninfo.h" -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -#include "codec2.h" -#include "codec2_fifo.h" -#include "modem_stats.h" - -#include "topFrame.h" -#include "dlg_ptt.h" -#include "dlg_options.h" -#include "fdmdv2_plot.h" -#include "fdmdv2_plot_scalar.h" -#include "fdmdv2_plot_scatter.h" -#include "fdmdv2_plot_waterfall.h" -#include "fdmdv2_plot_spectrum.h" -#include "fdmdv2_pa_wrapper.h" -#include "sndfile.h" -#include "portaudio.h" -#include "dlg_audiooptions.h" -#include "dlg_filter.h" -#include "dlg_options.h" -#include "varicode.h" -#include "sox_biquad.h" -#include "comp_prim.h" -#include "dlg_plugin.h" -#include "hamlib.h" -#include "serialport.h" - -#define _USE_TIMER 1 -#define _USE_ONIDLE 1 -#define _DUMMY_DATA 1 -//#define _AUDIO_PASSTHROUGH 1 -#define _REFRESH_TIMER_PERIOD (DT*1000) -//#define _USE_ABOUT_DIALOG 1 - -enum { - ID_START = wxID_HIGHEST, - ID_TIMER_WATERFALL, - ID_TIMER_SPECTRUM, - ID_TIMER_SCATTER, - ID_TIMER_SCALAR - }; - -#define EXCHANGE_DATA_IN 0 -#define EXCHANGE_DATA_OUT 1 - - -extern int g_nSoundCards; -extern int g_soundCard1InDeviceNum; -extern int g_soundCard1OutDeviceNum; -extern int g_soundCard1SampleRate; -extern int g_soundCard2InDeviceNum; -extern int g_soundCard2OutDeviceNum; -extern int g_soundCard2SampleRate; - -// Voice Keyer Constants - -#define VK_SYNC_WAIT_TIME 5.0 - -// Voice Keyer States - -#define VK_IDLE 0 -#define VK_TX 1 -#define VK_RX 2 -#define VK_SYNC_WAIT 3 - -// Voice Keyer Events - -#define VK_START 0 -#define VK_SPACE_BAR 1 -#define VK_PLAY_FINISHED 2 -#define VK_DT 3 -#define VK_SYNC 4 - -class MainFrame; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainApp -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MainApp : public wxApp -{ - public: - virtual bool OnInit(); - virtual int OnExit(); - - wxString m_strVendName; - wxString m_StrAppName; - - wxString m_textNumChOut; - wxString m_textNumChIn; - - wxString m_strRxInAudio; - wxString m_strRxOutAudio; - wxString m_textVoiceInput; - wxString m_textVoiceOutput; - wxString m_strSampleRate; - wxString m_strBitrate; - - // PTT ----------------------------------- - - bool m_boolHalfDuplex; - - wxString m_txtVoiceKeyerWaveFilePath; - wxString m_txtVoiceKeyerWaveFile; - int m_intVoiceKeyerRxPause; - int m_intVoiceKeyerRepeats; - - bool m_boolHamlibUseForPTT; - unsigned int m_intHamlibRig; - wxString m_strHamlibSerialPort; - unsigned int m_intHamlibSerialRate; - Hamlib *m_hamlib; - - bool m_boolUseSerialPTT; - wxString m_strRigCtrlPort; - bool m_boolUseRTS; - bool m_boolRTSPos; - bool m_boolUseDTR; - bool m_boolDTRPos; - Serialport *m_serialport; - - // Play/Rec files - - wxString m_playFileToMicInPath; - wxString m_recFileFromRadioPath; - unsigned int m_recFileFromRadioSecs; - wxString m_playFileFromRadioPath; - - // Options dialog - - wxString m_callSign; - bool m_events; - int m_events_spam_timer; - unsigned int m_textEncoding; - bool m_enable_checksum; - wxString m_events_regexp_match; - wxString m_events_regexp_replace; - - bool m_snrSlow; - - // LPC Post Filter - bool m_codec2LPCPostFilterEnable; - bool m_codec2LPCPostFilterBassBoost; - float m_codec2LPCPostFilterGamma; - float m_codec2LPCPostFilterBeta; - - // Speex Pre-Processor - bool m_speexpp_enable; - - // Mic In Equaliser - float m_MicInBassFreqHz; - float m_MicInBassGaindB; - float m_MicInTrebleFreqHz; - float m_MicInTrebleGaindB; - float m_MicInMidFreqHz; - float m_MicInMidGaindB; - float m_MicInMidQ; - bool m_MicInEQEnable; - - // Spk Out Equaliser - float m_SpkOutBassFreqHz; - float m_SpkOutBassGaindB; - float m_SpkOutTrebleFreqHz; - float m_SpkOutTrebleGaindB; - float m_SpkOutMidFreqHz; - float m_SpkOutMidGaindB; - float m_SpkOutMidQ; - bool m_SpkOutEQEnable; - - // Flags for displaying windows - int m_show_wf; - int m_show_spect; - int m_show_scatter; - int m_show_timing; - int m_show_freq; - int m_show_speech_in; - int m_show_speech_out; - int m_show_demod_in; - int m_show_test_frame_errors; - int m_show_test_frame_errors_hist; - - // optional vox trigger tone - bool m_leftChannelVoxTone; - - // UDP control port - bool m_udp_enable; - int m_udp_port; - - // notebook display after tx->rxtransition - int m_rxNbookCtrl; - - wxRect m_rTopWindow; - - int m_framesPerBuffer; - - bool loadConfig(); - bool saveConfig(); - - // Plugins ----------------------------------- - - wxString m_txtPlugInParam[PLUGIN_MAX_PARAMS]; - - // plugin details - - void *m_plugInHandle; - bool m_plugIn; - wxString m_plugInName; - int m_numPlugInParam; - wxString m_plugInParamName[PLUGIN_MAX_PARAMS]; - void *m_plugInStates; - void (*m_plugin_startfp)(void *); - void (*m_plugin_stopfp)(void *); - void (*m_plugin_rx_samplesfp)(void *, short *, int); - - // misc - - bool m_testFrames; - bool m_channel_noise; - float m_channel_snr_dB; - - int FilterEvent(wxEvent& event); - MainFrame *frame; - - // 700 options - - bool m_FreeDV700txClip; - bool m_FreeDV700Combine; - - // Noise simulation - - int m_noise_snr; - - // carrier attenuation - - bool m_attn_carrier_en; - int m_attn_carrier; - - // tone interferer simulation - - bool m_tone; - int m_tone_freq_hz; - int m_tone_amplitude; - - // Windows debug console - - bool m_debug_console; - protected: -}; - -// declare global static function wxGetApp() -DECLARE_APP(MainApp) - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// paCallBackData -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -typedef struct -{ - // libresample states for 48 to 8 kHz conversions - - SRC_STATE *insrc1; - SRC_STATE *outsrc1; - SRC_STATE *insrc2; - SRC_STATE *outsrc2; - SRC_STATE *insrcsf; - - // FIFOs attached to first sound card - - struct FIFO *infifo1; - struct FIFO *outfifo1; - - // FIFOs attached to second sound card - struct FIFO *infifo2; - struct FIFO *outfifo2; - - // FIFOs for rx process - struct FIFO *rxinfifo; - struct FIFO *rxoutfifo; - - int inputChannels1, inputChannels2; - - // EQ filter states - void *sbqMicInBass; - void *sbqMicInTreble; - void *sbqMicInMid; - void *sbqSpkOutBass; - void *sbqSpkOutTreble; - void *sbqSpkOutMid; - - bool micInEQEnable; - bool spkOutEQEnable; - - // optional loud tone on left channel to reliably trigger vox - bool leftChannelVoxTone; - float voxTonePhase; - -} paCallBackData; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// panel with custom loop checkbox for play file dialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MyExtraPlayFilePanel : public wxPanel -{ -public: - MyExtraPlayFilePanel(wxWindow *parent); - void setLoopPlayFileToMicIn(bool checked) { m_cb->SetValue(checked); } - bool getLoopPlayFileToMicIn(void) { return m_cb->GetValue(); } -private: - wxCheckBox *m_cb; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// panel with custom Seconds-to-record control for record file dialog -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MyExtraRecFilePanel : public wxPanel -{ -public: - MyExtraRecFilePanel(wxWindow *parent); - ~MyExtraRecFilePanel() - { - wxLogDebug("Destructor\n"); - } - void setSecondsToRecord(wxString value) { m_secondsToRecord->SetValue(value); } - wxString getSecondsToRecord(void) - { - wxLogDebug("getSecondsToRecord: %s\n",m_secondsToRecord->GetValue()); - return m_secondsToRecord->GetValue(); - } -private: - wxTextCtrl *m_secondsToRecord; -}; - -class txRxThread; -class UDPThread; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class MainFrame -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class MainFrame : public TopFrame -{ - public: - MainFrame(wxString plugInName, wxWindow *parent); - virtual ~MainFrame(); - - PlotSpectrum* m_panelSpectrum; - PlotWaterfall* m_panelWaterfall; - PlotScatter* m_panelScatter; - PlotScalar* m_panelTimeOffset; - PlotScalar* m_panelFreqOffset; - PlotScalar* m_panelSpeechIn; - PlotScalar* m_panelSpeechOut; - PlotScalar* m_panelDemodIn; - PlotScalar* m_panelTestFrameErrors; - PlotScalar* m_panelTestFrameErrorsHist; - - bool m_RxRunning; - - PortAudioWrap *m_rxInPa; - PortAudioWrap *m_rxOutPa; - PortAudioWrap *m_txInPa; - PortAudioWrap *m_txOutPa; - - PaError m_rxErr; - PaError m_txErr; - - txRxThread* m_txRxThread; - - bool OpenHamlibRig(); - void OpenSerialPort(void); - void CloseSerialPort(void); - void SerialPTTRx(void); - - bool m_modal; - -#ifdef _USE_TIMER - wxTimer m_plotTimer; -#endif - - void destroy_fifos(void); - void destroy_src(void); - void autoDetectSoundCards(PortAudioWrap *pa); - - static int rxCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); - - static int txCallback( - const void *inBuffer, - void *outBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *outTime, - PaStreamCallbackFlags statusFlags, - void *userData - ); - - - void initPortAudioDevice(PortAudioWrap *pa, int inDevice, int outDevice, - int soundCard, int sampleRate, int inputChannels); - - void togglePTT(void); - - wxIPV4address m_udp_addr; - wxDatagramSocket *m_udp_sock; - UDPThread *m_UDPThread; - void startUDPThread(void); - void stopUDPThread(void); - int PollUDP(); - bool m_schedule_restore; - - int vk_state; - void VoiceKeyerProcessEvent(int vk_event); - - protected: - - void setsnrBeta(bool snrSlow); - - // protected event handlers - virtual void OnCloseFrame(wxCloseEvent& event); - void OnExitClick(wxCommandEvent& event); - - void startTxStream(); - void startRxStream(); - void stopTxStream(); - void stopRxStream(); - void abortTxStream(); - void abortRxStream(); - - void OnTop(wxCommandEvent& event); - void OnExit( wxCommandEvent& event ); - - void OnToolsAudio( wxCommandEvent& event ); - void OnToolsAudioUI( wxUpdateUIEvent& event ); - void OnToolsComCfg( wxCommandEvent& event ); - void OnToolsComCfgUI( wxUpdateUIEvent& event ); - void OnToolsFilter( wxCommandEvent& event ); - void OnToolsOptions(wxCommandEvent& event); - void OnToolsOptionsUI(wxUpdateUIEvent& event); - - void OnToolsPlugInCfg( wxCommandEvent& event ); - void OnToolsPlugInCfgUI( wxUpdateUIEvent& event ); - - void OnPlayFileToMicIn( wxCommandEvent& event ); - void StopPlayFileToMicIn(void); - void OnRecFileFromRadio( wxCommandEvent& event ); - void OnPlayFileFromRadio( wxCommandEvent& event ); - - void OnHelpCheckUpdates( wxCommandEvent& event ); - void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ); - void OnHelpAbout( wxCommandEvent& event ); - void OnCmdSliderScroll( wxScrollEvent& event ); -// void OnSliderScrollBottom( wxScrollEvent& event ); -// void OnCmdSliderScrollChanged( wxScrollEvent& event ); -// void OnSliderScrollTop( wxScrollEvent& event ); - void OnCheckSQClick( wxCommandEvent& event ); - void OnCheckSNRClick( wxCommandEvent& event ); - - // Toggle Buttons - void OnTogBtnSplitClick(wxCommandEvent& event); - void OnTogBtnAnalogClick(wxCommandEvent& event); - void OnTogBtnRxID( wxCommandEvent& event ); - void OnTogBtnTxID( wxCommandEvent& event ); - void OnTogBtnPTT( wxCommandEvent& event ); - void OnTogBtnVoiceKeyerClick (wxCommandEvent& event); - void OnTogBtnOnOff( wxCommandEvent& event ); - - void OnCallSignReset( wxCommandEvent& event ); - void OnBerReset( wxCommandEvent& event ); - - //System Events - void OnPaint(wxPaintEvent& event); - void OnSize( wxSizeEvent& event ); - void OnUpdateUI( wxUpdateUIEvent& event ); - void OnDeleteConfig(wxCommandEvent&); -#ifdef _USE_TIMER - void OnTimer(wxTimerEvent &evt); -#endif -#ifdef _USE_ONIDLE - void OnIdle(wxIdleEvent &evt); -#endif - - int VoiceKeyerStartTx(void); - - private: - bool m_useMemory; - wxTextCtrl* m_tc; - int m_zoom; - float m_snrBeta; - - // Callsign/text messaging - char m_callsign[MAX_CALLSIGN]; - char *m_pcallsign; - unsigned int m_checksumGood; - unsigned int m_checksumBad; - - // Events - void processTxtEvent(char event[]); - class OptionsDlg *optionsDlg; - wxTimer spamTimer[MAX_EVENT_RULES]; - - // level Gauge - float m_maxLevel; - - // flags to indicate when new EQ filters need to be designed - - bool m_newMicInFilter; - bool m_newSpkOutFilter; - - void* designAnEQFilter(const char filterType[], float freqHz, float gaindB, float Q = 0.0); - void designEQFilters(paCallBackData *cb); - void deleteEQFilters(paCallBackData *cb); - - // Voice Keyer States - - int vk_rx_pause; - int vk_repeats, vk_repeat_counter; - float vk_rx_time; -}; - -void txRxProcessing(); - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class txRxThread - experimental tx/rx processing thread -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class txRxThread : public wxThread -{ -public: - txRxThread(void) : wxThread(wxTHREAD_JOINABLE) { m_run = 1; } - - // thread execution starts here - void *Entry() - { - while (m_run) - { - txRxProcessing(); - wxThread::Sleep(20); - } - return NULL; - } - - // called when the thread exits - whether it terminates normally or is - // stopped with Delete() (but not when it is Kill()ed!) - void OnExit() { } - -public: - bool m_run; -}; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// class UDPThread - waits for UDP messages -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class UDPThread : public wxThread -{ -public: - UDPThread(void) : wxThread(wxTHREAD_JOINABLE) { m_run = 1; } - - // thread execution starts here - void *Entry(); - - // called when the thread exits - whether it terminates normally or is - // stopped with Delete() (but not when it is Kill()ed!) - void OnExit() { } - -public: - MainFrame *mf; - bool m_run; -}; - -void resample_for_plot(struct FIFO *plotFifo, short buf[], int length, int fs); - -int resample(SRC_STATE *src, - short output_short[], - short input_short[], - int output_sample_rate, - int input_sample_rate, - int length_output_short, // maximum output array length in samples - int length_input_short - ); -void txRxProcessing(); -void per_frame_rx_processing( - FIFO *output_fifo, // decoded speech samples - FIFO *input_fifo // modem samples input to demod - ); - -// FreeDv API calls this when there is a test frame that needs a-plottin' - -void my_freedv_put_error_pattern(void *state, short error_pattern[], int sz_error_pattern); - -// FreeDv API calls these puppies when it needs/receives a text char - -char my_get_next_tx_char(void *callback_state); -void my_put_next_rx_char(void *callback_state, char c); - -// helper complex freq shift function - -void freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, COMP *foff_phase_rect, int nin); - -// Helper function called by plugin - -int plugin_get_persistant(char name[], char value[]); - -#endif //__FDMDV2_MAIN__ diff --git a/freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.cpp b/freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.cpp deleted file mode 100644 index 2f67ca26..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.cpp +++ /dev/null @@ -1,324 +0,0 @@ -//========================================================================== -// Name: fdmdv2_pa_wrapper.cpp -// Purpose: Implements a wrapper class around the PortAudio library. -// Created: August 12, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "fdmdv2_pa_wrapper.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// PortAudioWrap() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PortAudioWrap::PortAudioWrap() -{ - m_pStream = NULL; - m_pUserData = NULL; - m_samplerate = 0; - m_framesPerBuffer = 0; - m_statusFlags = 0; - m_pStreamCallback = NULL; - m_pStreamFinishedCallback = NULL; - m_pTimeInfo = 0; - m_newdata = false; - -// loadData(); -} - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// ~PortAudioWrap() -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PortAudioWrap::~PortAudioWrap() -{ -} - -//---------------------------------------------------------------- -// streamOpen() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamOpen() -{ - return Pa_OpenStream( - &m_pStream, - m_inputBuffer.device == paNoDevice ? NULL : &m_inputBuffer, - m_outputBuffer.device == paNoDevice ? NULL : &m_outputBuffer, - m_samplerate, - m_framesPerBuffer, - m_statusFlags, - *m_pStreamCallback, - m_pUserData - ); -} - -//---------------------------------------------------------------- -// streamStart() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamStart() -{ - return Pa_StartStream(m_pStream); -} - -//---------------------------------------------------------------- -// streamClose() -//---------------------------------------------------------------- -PaError PortAudioWrap::streamClose() -{ - if(isOpen()) - { - PaError rv = Pa_CloseStream(m_pStream); - return rv; - } - else - { - return paNoError; - } -} - -//---------------------------------------------------------------- -// terminate() -//---------------------------------------------------------------- -void PortAudioWrap::terminate() -{ - if(Pa_IsStreamStopped(m_pStream) != paNoError) - { - Pa_StopStream(m_pStream); - } - Pa_Terminate(); -} - -//---------------------------------------------------------------- -// stop() -//---------------------------------------------------------------- -void PortAudioWrap::stop() -{ - Pa_StopStream(m_pStream); -} - -//---------------------------------------------------------------- -// abort() -//---------------------------------------------------------------- -void PortAudioWrap::abort() -{ - Pa_AbortStream(m_pStream); -} - -//---------------------------------------------------------------- -// isStopped() -//---------------------------------------------------------------- -bool PortAudioWrap::isStopped() const -{ - PaError ret = Pa_IsStreamStopped(m_pStream); - return ret; -} - -//---------------------------------------------------------------- -// isActive() -//---------------------------------------------------------------- -bool PortAudioWrap::isActive() const -{ - PaError ret = Pa_IsStreamActive(m_pStream); - return ret; -} - -//---------------------------------------------------------------- -// isOpen() -//---------------------------------------------------------------- -bool PortAudioWrap::isOpen() const -{ - return (m_pStream != NULL); -} - -//---------------------------------------------------------------- -// getDefaultInputDevice() -//---------------------------------------------------------------- -PaDeviceIndex PortAudioWrap::getDefaultInputDevice() -{ - return Pa_GetDefaultInputDevice(); -} - -//---------------------------------------------------------------- -// getDefaultOutputDevice() -//---------------------------------------------------------------- -PaDeviceIndex PortAudioWrap::getDefaultOutputDevice() -{ - return Pa_GetDefaultOutputDevice(); -} - -//---------------------------------------------------------------- -// setInputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputChannelCount(int count) -{ - m_inputBuffer.channelCount = count; - return paNoError; -} - -//---------------------------------------------------------------- -// getInputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::getInputChannelCount() -{ - return m_inputBuffer.channelCount; -} - -//---------------------------------------------------------------- -// setInputSampleFormat() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputSampleFormat(PaSampleFormat format) -{ - m_inputBuffer.sampleFormat = format; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputLatency() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputLatency(PaTime latency) -{ - m_inputBuffer.suggestedLatency = latency; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputHostApiStreamInfo() -//---------------------------------------------------------------- -void PortAudioWrap::setInputHostApiStreamInfo(void *info) -{ - m_inputBuffer.hostApiSpecificStreamInfo = info; -} - -//---------------------------------------------------------------- -// getInputDefaultLowLatency() -//---------------------------------------------------------------- -PaTime PortAudioWrap::getInputDefaultLowLatency() -{ - return Pa_GetDeviceInfo(m_inputBuffer.device)->defaultLowInputLatency; -} - -//---------------------------------------------------------------- -// setOutputChannelCount() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputChannelCount(int count) -{ - m_outputBuffer.channelCount = count; - return paNoError; -} - -//---------------------------------------------------------------- -// getOutputChannelCount() -//---------------------------------------------------------------- -const int PortAudioWrap::getOutputChannelCount() -{ - return m_outputBuffer.channelCount; -} - -//---------------------------------------------------------------- -// getDeviceName() -//---------------------------------------------------------------- -const char *PortAudioWrap::getDeviceName(PaDeviceIndex dev) -{ - const PaDeviceInfo *info; - info = Pa_GetDeviceInfo(dev); - return info->name; -} - -//---------------------------------------------------------------- -// setOutputSampleFormat() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputSampleFormat(PaSampleFormat format) -{ - m_outputBuffer.sampleFormat = format; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputLatency() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputLatency(PaTime latency) -{ - m_outputBuffer.suggestedLatency = latency; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputHostApiStreamInfo() -//---------------------------------------------------------------- -void PortAudioWrap::setOutputHostApiStreamInfo(void *info) -{ - m_outputBuffer.hostApiSpecificStreamInfo = info; -} - -//---------------------------------------------------------------- -// getOutputDefaultLowLatency() -//---------------------------------------------------------------- -PaTime PortAudioWrap::getOutputDefaultLowLatency() -{ - return Pa_GetDeviceInfo(m_outputBuffer.device)->defaultLowOutputLatency; -} - -//---------------------------------------------------------------- -// setFramesPerBuffer() -//---------------------------------------------------------------- -PaError PortAudioWrap::setFramesPerBuffer(unsigned long size) -{ - m_framesPerBuffer = size; - return paNoError; -} - -//---------------------------------------------------------------- -// setSampleRate() -//---------------------------------------------------------------- -PaError PortAudioWrap::setSampleRate(unsigned long rate) -{ - m_samplerate = rate; - return paNoError; -} - -//---------------------------------------------------------------- -// setStreamFlags() -//---------------------------------------------------------------- -PaError PortAudioWrap::setStreamFlags(PaStreamFlags flags) -{ - m_statusFlags = flags; - return paNoError; -} - -//---------------------------------------------------------------- -// setInputDevice() -//---------------------------------------------------------------- -PaError PortAudioWrap::setInputDevice(PaDeviceIndex index) -{ - m_inputBuffer.device = index; - return paNoError; -} - -//---------------------------------------------------------------- -// setOutputDevice() -//---------------------------------------------------------------- -PaError PortAudioWrap::setOutputDevice(PaDeviceIndex index) -{ - m_outputBuffer.device = index; - return paNoError; -} - -//---------------------------------------------------------------- -// setCallback() -//---------------------------------------------------------------- -PaError PortAudioWrap::setCallback(PaStreamCallback *callback) -{ - m_pStreamCallback = callback; - return paNoError; -} - diff --git a/freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.h b/freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.h deleted file mode 100644 index 3d216c0d..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_pa_wrapper.h +++ /dev/null @@ -1,115 +0,0 @@ -//========================================================================== -// Name: fdmdv2_pa_wrapper.h -// Purpose: Defines a wrapper class around PortAudio -// Created: August 12, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include -#include "fdmdv2_defines.h" -#include "codec2_fdmdv.h" -#include "codec2.h" -#include "portaudio.h" - -#define PA_SAMPLE_TYPE paInt16 //paFloat32 -#define FRAMES_PER_BUFFER (64) - -typedef float SAMPLE; - -class PortAudioWrap -{ - public: - PortAudioWrap(); - ~PortAudioWrap(); - -// float m_av_mag[FDMDV_NSPEC]; - - private: - PaStream *m_pStream; - void *m_pUserData; - PaStreamCallback *m_pStreamCallback; - PaStreamFinishedCallback *m_pStreamFinishedCallback; - const PaStreamCallbackTimeInfo *m_pTimeInfo; - struct FDMDV *m_pFDMDV_state; - PaStreamParameters m_inputBuffer; - PaStreamParameters m_outputBuffer; - int m_samplerate; - unsigned long m_framesPerBuffer; - PaStreamCallbackFlags m_statusFlags; - bool m_newdata; - - public: - - void averageData(float mag_dB[]); - - int getDeviceCount() { return Pa_GetDeviceCount(); } - PaDeviceIndex getDefaultInputDevice(); - PaDeviceIndex getDefaultOutputDevice(); - PaStreamParameters *getDeviceInfo(PaDeviceIndex idx); - - PaError setFramesPerBuffer(unsigned long size); - PaError setSampleRate(unsigned long size); - - PaError setStreamFlags(PaStreamFlags flags); - PaError setCallback(PaStreamCallback *m_pStreamCallback); - PaError setStreamCallback(PaStream *stream, PaStreamCallback* callback) { m_pStreamCallback = callback; return 0;} - PaError setStreamFinishedCallback(PaStream *stream, PaStreamFinishedCallback* m_pStreamFinishedCallback); - - void setInputBuffer(const PaStreamParameters& inputBuffer) {this->m_inputBuffer = inputBuffer;} - PaError setInputDevice(PaDeviceIndex dev); - PaError setInputChannelCount(int count); - int getInputChannelCount(); - PaError setInputSampleFormat(PaSampleFormat format); - PaError setInputSampleRate(PaSampleFormat format); - PaError setInputLatency(PaTime latency); - void setInputHostApiStreamInfo(void *info = NULL); - PaTime getInputDefaultLowLatency(); - const char *getDeviceName(PaDeviceIndex dev); - - PaError setOutputDevice(PaDeviceIndex dev); - PaError setOutputChannelCount(int count); - const int getOutputChannelCount(); - PaError setOutputSampleFormat(PaSampleFormat format); - PaError setOutputLatency(PaTime latency); - void setOutputHostApiStreamInfo(void *info = NULL); - PaTime getOutputDefaultLowLatency(); - - void setFdmdvState(FDMDV* fdmdv_state) {this->m_pFDMDV_state = fdmdv_state;} - void setOutputBuffer(const PaStreamParameters& outputBuffer) {this->m_outputBuffer = outputBuffer;} - void setTimeInfo(PaStreamCallbackTimeInfo* timeInfo) {this->m_pTimeInfo = timeInfo;} - void setUserData(void* userData) {this->m_pUserData = userData;} - unsigned long getFramesPerBuffer() const {return m_framesPerBuffer;} - const PaStreamParameters& getInputBuffer() const {return m_inputBuffer;} - const PaStreamParameters& getOutputBuffer() const {return m_outputBuffer;} - const PaStreamCallbackFlags& getStatusFlags() const {return m_statusFlags;} - - FDMDV* getFdmdvState() {return m_pFDMDV_state;} - int getSamplerate() const {return m_samplerate;} - PaStream* getStream() {return m_pStream;} - void *getUserData() {return m_pUserData;} - bool getDataAvail() {return m_newdata;} - PaError streamStart(); - PaError streamClose(); - PaError streamOpen(); - void terminate(); - void stop(); - void abort(); - bool isOpen() const; - bool isStopped() const; - bool isActive() const; -// void loadData(); -}; diff --git a/freedv/tags/1.2.2/src/fdmdv2_plot.cpp b/freedv/tags/1.2.2/src/fdmdv2_plot.cpp deleted file mode 100644 index 1b85cd90..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_plot.cpp +++ /dev/null @@ -1,283 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot.cpp -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "fdmdv2_plot.h" - -BEGIN_EVENT_TABLE(PlotPanel, wxPanel) - EVT_PAINT (PlotPanel::OnPaint) - EVT_MOTION (PlotPanel::OnMouseMove) - EVT_LEFT_DOWN (PlotPanel::OnMouseLeftDown) - EVT_LEFT_UP (PlotPanel::OnMouseLeftUp) - EVT_RIGHT_DOWN (PlotPanel::OnMouseRightDown) - EVT_MOUSEWHEEL (PlotPanel::OnMouseWheelMoved) - EVT_SIZE (PlotPanel::OnSize) - EVT_SHOW (PlotPanel::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotPanel(wxFrame* parent) : wxPanel(parent) -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotPanel::PlotPanel(wxFrame* parent) : wxPanel(parent) -{ - m_pNoteBook = (wxAuiNotebook *) parent; - m_pTopFrame = (MainFrame *)m_pNoteBook->GetParent(); - m_zoomFactor = 1.0; - m_pBmp = NULL; - m_pPix = NULL; - m_firstPass = true; - m_line_color = 0; - m_newdata = false; - m_clip = false; - m_use_bitmap = true; - m_rubberBand = false; - m_mouseDown = false; - m_penShortDash = wxPen(wxColor(0xA0, 0xA0, 0xA0), 1, wxPENSTYLE_SHORT_DASH); - m_penDotDash = wxPen(wxColor(0xD0, 0xD0, 0xD0), 1, wxPENSTYLE_DOT_DASH); - m_penSolid = wxPen(wxColor(0x00, 0x00, 0x00), 1, wxPENSTYLE_SOLID); - SetBackgroundStyle(wxBG_STYLE_PAINT); - SetLabelSize(10.0); -} - -//------------------------------------------------------------------------- -// ~PlotPanel() -//------------------------------------------------------------------------- -PlotPanel::~PlotPanel() -{ - if(m_pBmp != NULL) - { - delete m_pBmp; - } -} - -//------------------------------------------------------------------------- -// GetLabelSize() -//------------------------------------------------------------------------- -double PlotPanel::GetLabelSize() -{ - return m_label_size; -} - -//------------------------------------------------------------------------- -// SetLabelSize() -//------------------------------------------------------------------------- -void PlotPanel::SetLabelSize(double size) -{ - m_label_size = size; -} - -//------------------------------------------------------------------------- -// OnShow() -//------------------------------------------------------------------------- -void PlotPanel::OnShow(wxShowEvent& event) -{ - this->Refresh(); -} - -//------------------------------------------------------------------------- -// OnErase() -//------------------------------------------------------------------------- -void PlotPanel::OnErase(wxEraseEvent& event) -{ - event.Skip(); -} - -//------------------------------------------------------------------------- -// OnSize() -//------------------------------------------------------------------------- -void PlotPanel::OnSize(wxSizeEvent& event) -{ - m_rCtrlPrev = m_rCtrl; - m_rCtrl = GetClientRect(); - if(m_use_bitmap) - { - if(!m_oImage.IsOk()) - { - m_oImage.Create(m_rCtrl.GetWidth(), m_rCtrl.GetHeight(), true); - } - else - { - m_oImage.Rescale(m_rCtrl.GetWidth(), m_rCtrl.GetHeight()); - } - m_pBmp = new wxBitmap(m_oImage, wxBITMAP_SCREEN_DEPTH); - m_firstPass = true; - } - this->Refresh(); -} - -//------------------------------------------------------------------------- -// OnMouseMove() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseMove(wxMouseEvent& event) -{ -// if(m_mouseDown) -// { -// paintNow(); -// } -} - -//------------------------------------------------------------------------- -// OnMouseLeftDown() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseLeftDown(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseRightDown() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseRightDown(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseWheelMoved() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseWheelMoved(wxMouseEvent& event) -{ -} - -//------------------------------------------------------------------------- -// OnMouseLeftUp() -//------------------------------------------------------------------------- -void PlotPanel::OnMouseLeftUp(wxMouseEvent& event) -{ - m_mouseDown = false; -} - -//------------------------------------------------------------------------- -// SetZoomFactor() -//------------------------------------------------------------------------- -double PlotPanel::SetZoomFactor(double zf) -{ - if((zf > 0) && (zf < 5.0)) - { - m_zoomFactor = zf; - } - return zf; -} - -//------------------------------------------------------------------------- -// GetZoomFactor() -//------------------------------------------------------------------------- -double PlotPanel::GetZoomFactor(double zf) -{ - return m_zoomFactor; -} - -//------------------------------------------------------------------------- -// draw() -//------------------------------------------------------------------------- -void PlotPanel::draw(wxAutoBufferedPaintDC& pDC) -{ - printf("PlotPanel::draw()"); - wxMemoryDC m_mDC; - m_mDC.SelectObject(*m_pBmp); - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - m_rGrid.Offset(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER); - - pDC.Clear(); - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - if(m_firstPass) - { - m_firstPass = false; - m_mDC.FloodFill(0, 0, VERY_LTGREY_COLOR); - - // Draw a filled rectangle with aborder - wxBrush ltGraphBkgBrush = wxBrush(DARK_BLUE_COLOR); - m_mDC.SetBrush(ltGraphBkgBrush); - m_mDC.SetPen(wxPen(BLACK_COLOR, 0)); - m_mDC.DrawRectangle(m_rPlot); - } - if(m_newdata) - { - m_newdata = false; - int t = m_rPlot.GetTop(); - int l = m_rPlot.GetLeft(); -// int r = m_rPlot.GetRight(); - int h = m_rPlot.GetHeight(); - int w = m_rPlot.GetWidth(); - pDC.Blit(l, t, w, h, &m_mDC, l, t); - } - drawGraticule(pDC); - m_mDC.SetBrush(wxNullBrush); - m_mDC.SelectObject(wxNullBitmap); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotPanel::drawGraticule(wxAutoBufferedPaintDC& pDC) -{ - int p; - char buf[15]; - wxString s; - - // Vertical gridlines - pDC.SetPen(m_penShortDash); - for(p = (PLOT_BORDER + XLEFT_OFFSET + GRID_INCREMENT); p < ((m_rGrid.GetWidth() - XLEFT_OFFSET) + GRID_INCREMENT); p += GRID_INCREMENT) - { - pDC.DrawLine(p, (m_rGrid.GetHeight() + PLOT_BORDER), p, PLOT_BORDER); - } - // Horizontal gridlines - pDC.SetPen(m_penDotDash); - for(p = (m_rGrid.GetHeight() - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT) - { - pDC.DrawLine(PLOT_BORDER + XLEFT_OFFSET, (p + PLOT_BORDER), (m_rGrid.GetWidth() + PLOT_BORDER + XLEFT_OFFSET), (p + PLOT_BORDER)); - } - // Label the X-Axis - pDC.SetPen(wxPen(GREY_COLOR, 1)); - for(p = GRID_INCREMENT; p < (m_rGrid.GetWidth() - YBOTTOM_OFFSET); p += GRID_INCREMENT) - { - sprintf(buf, "%1.1f Hz",(double)(p / 10)); - pDC.DrawText(buf, p - PLOT_BORDER + XLEFT_OFFSET, m_rGrid.GetHeight() + YBOTTOM_OFFSET/2); - } - // Label the Y-Axis - //for(p = GRID_INCREMENT; p < (h - YBOTTOM_OFFSET); p += GRID_INCREMENT) - for(p = (m_rGrid.GetHeight() - GRID_INCREMENT); p > PLOT_BORDER; p -= GRID_INCREMENT) - { - sprintf(buf, "%1.0f", (double)((m_rGrid.GetHeight() - p) * -10)); - pDC.DrawText(buf, XLEFT_TEXT_OFFSET, p); - } -} - -//------------------------------------------------------------------------- -// paintEvent() -// -// Called by the system of by wxWidgets when the panel needs -// to be redrawn. You can also trigger this call by calling -// Refresh()/Update(). -//------------------------------------------------------------------------- -void PlotPanel::OnPaint(wxPaintEvent & evt) -{ - wxAutoBufferedPaintDC pdc(this); - draw(pdc); -} - diff --git a/freedv/tags/1.2.2/src/fdmdv2_plot.h b/freedv/tags/1.2.2/src/fdmdv2_plot.h deleted file mode 100644 index 25309d3a..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_plot.h +++ /dev/null @@ -1,150 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot.h -// Purpose: Declares simple wxWidgets application with GUI -// Created: Apr. 10, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -//#include "fdmdv2_main.h" -#ifndef __FDMDV2_PLOT__ -#define __FDMDV2_PLOT__ -#include -#include -#include -#include -#include - -#define MAX_ZOOM 7 -#define MAX_BMP_X (400 * MAX_ZOOM) -#define MAX_BMP_Y (400 * MAX_ZOOM) -#define DATA_LINE_HEIGHT 10 -#define TEXT_BASELINE_OFFSET_Y -5 - - -#define wxUSE_FILEDLG 1 -#define wxUSE_LIBPNG 1 -#define wxUSE_LIBJPEG 1 -#define wxUSE_GIF 1 -#define wxUSE_PCX 1 -#define wxUSE_LIBTIFF 1 - -#define PLOT_BORDER 12 -#define XLEFT_OFFSET 40 -#define XLEFT_TEXT_OFFSET 6 -#define YBOTTOM_OFFSET 20 -#define YBOTTOM_TEXT_OFFSET 15 -#define GRID_INCREMENT 50 - -#define BLACK_COLOR wxColor(0x00, 0x00, 0x00) -#define GREY_COLOR wxColor(0x80, 0x80, 0x80) -#define DARK_GREY_COLOR wxColor(0x40, 0x40, 0x40) -#define MEDIUM_GREY_COLOR wxColor(0xC0, 0xC0, 0xC0) -#define LIGHT_GREY_COLOR wxColor(0xE0, 0xE0, 0xE0) -#define VERY_LTGREY_COLOR wxColor(0xF8, 0xF8, 0xF8) -#define WHITE_COLOR wxColor(0xFF, 0xFF, 0xFF) - -#define DARK_BLUE_COLOR wxColor(0x00, 0x00, 0x60) -#define BLUE_COLOR wxColor(0x00, 0x00, 0xFF) -#define LIGHT_BLUE_COLOR wxColor(0x80, 0x80, 0xFF) - -#define RED_COLOR wxColor(0xFF, 0x5E, 0x5E) -#define LIGHT_RED_COLOR wxColor(0xFF, 0xE0, 0xE0) -#define DARK_RED_COLOR wxColor(0xFF, 0x00, 0x00) -#define PINK_COLOR wxColor(0xFF, 0x80, 0xFF) - -#define LIGHT_GREEN_COLOR wxColor(0xE3, 0xFF, 0xE0) -#define GREEN_COLOR wxColor(0x95, 0xFF, 0x8A) -#define DARK_GREEN_COLOR wxColor(0x20, 0xFF, 0x08) -#define VERY_GREEN_COLOR wxColor(0x00, 0xFF, 0x00) - -#define YELLOW_COLOR wxColor(0xFF, 0xFF, 0x5E) -#define LIGHT_YELLOW_COLOR wxColor(0xFF, 0xFF, 0xB5) -#define DARK_YELLOW_COLOR wxColor(0xFF, 0xFF, 0x08) - -class MainFrame; - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotPanel -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotPanel : public wxPanel -{ - public: - PlotPanel(wxFrame* parent); - ~PlotPanel(); - wxPen m_penShortDash; - wxPen m_penDotDash; - wxPen m_penSolid; - wxRect m_rCtrlPrev; - wxRect m_rCtrl; - wxRect m_rGrid; - wxRect m_rPlot; - MainFrame *m_pTopFrame; - wxAuiNotebook *m_pNoteBook; - double m_label_size; - wxSize m_Bufsz; - bool m_newdata; - wxImage m_oImage; - wxBitmap *m_pBmp; - wxNativePixelData *m_pPix; - - // some useful events - void OnMouseMove(wxMouseEvent& event); - virtual void OnMouseLeftDown(wxMouseEvent& event); - void OnMouseLeftUp(wxMouseEvent& event); - virtual void OnMouseRightDown(wxMouseEvent& event); - void OnMouseWheelMoved(wxMouseEvent& event); - void OnClose(wxCloseEvent& event ){ event.Skip(); } - void OnSize( wxSizeEvent& event ); - void OnErase(wxEraseEvent& event); - void OnPaint(wxPaintEvent& event); - //void OnUpdateUI( wxUpdateUIEvent& event ){ event.Skip(); } - - void paintEvent(wxPaintEvent & evt); - virtual void draw(wxAutoBufferedPaintDC& pdc); - virtual void drawGraticule(wxAutoBufferedPaintDC& pdc); - virtual double SetZoomFactor(double zf); - virtual double GetZoomFactor(double zf); - virtual void OnShow(wxShowEvent& event); - virtual double GetLabelSize(); - virtual void SetLabelSize(double size); - - protected: - int m_x; - int m_y; - int m_left; - int m_top; - int m_prev_w; - int m_prev_h; - int m_prev_x; - int m_prev_y; - bool m_use_bitmap; - bool m_clip; - bool m_rubberBand; - bool m_mouseDown; - bool m_firstPass; - double m_zoomFactor; - int m_greyscale; - int m_line_color; - DECLARE_EVENT_TABLE() -}; -#endif //__FDMDV2_PLOT__ diff --git a/freedv/tags/1.2.2/src/fdmdv2_plot_scalar.cpp b/freedv/tags/1.2.2/src/fdmdv2_plot_scalar.cpp deleted file mode 100644 index 7a07f46f..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_plot_scalar.cpp +++ /dev/null @@ -1,281 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_scalar.cpp -// Purpose: Plots scalar amplitude against time -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_main.h" -#include "fdmdv2_plot_scalar.h" - -BEGIN_EVENT_TABLE(PlotScalar, PlotPanel) - EVT_PAINT (PlotScalar::OnPaint) - EVT_MOTION (PlotScalar::OnMouseMove) - EVT_MOUSEWHEEL (PlotScalar::OnMouseWheelMoved) - EVT_SIZE (PlotScalar::OnSize) - EVT_SHOW (PlotScalar::OnShow) -// EVT_ERASE_BACKGROUND(PlotScalar::OnErase) -END_EVENT_TABLE() - -//---------------------------------------------------------------- -// PlotScalar() -//---------------------------------------------------------------- -PlotScalar::PlotScalar(wxFrame* parent, - int channels, // number on channels to plot - float t_secs, // time covered by entire x axis in seconds - float sample_period_secs, // time between each sample in seconds - float a_min, // min ampltude of samples being plotted - float a_max, // max ampltude of samples being plotted - float graticule_t_step, // time step of x (time) axis graticule in seconds - float graticule_a_step, // step of amplitude axis graticule - const char a_fmt[], // printf format string for amplitude axis labels - int mini // true for mini-plot - don't draw graticule - ): PlotPanel(parent) -{ - int i; - - m_rCtrl = GetClientRect(); - - m_channels = channels; - m_t_secs = t_secs; - m_sample_period_secs = sample_period_secs; - m_a_min = a_min; - m_a_max = a_max; - m_graticule_t_step = graticule_t_step; - m_graticule_a_step = graticule_a_step; - assert(strlen(a_fmt) < 15); - strcpy(m_a_fmt, a_fmt); - m_mini = mini; - - // work out number of samples we will store and allocate storage - - m_samples = m_t_secs/m_sample_period_secs; - m_mem = new float[m_samples*m_channels]; - - for(i = 0; i < m_samples*m_channels; i++) - { - m_mem[i] = 0.0; - } -} - -//---------------------------------------------------------------- -// ~PlotScalar() -//---------------------------------------------------------------- -PlotScalar::~PlotScalar() -{ - delete m_mem; -} - -//---------------------------------------------------------------- -// add_new_sample() -//---------------------------------------------------------------- -void PlotScalar::add_new_sample(int channel, float sample) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-1; i++) - { - m_mem[offset+i] = m_mem[offset+i+1]; - } - m_mem[offset+m_samples-1] = sample; -} - -//---------------------------------------------------------------- -// add_new_samples() -//---------------------------------------------------------------- -void PlotScalar::add_new_short_samples(int channel, short samples[], int length, float scale_factor) -{ - int i; - int offset = channel*m_samples; - - assert(channel < m_channels); - - for(i = 0; i < m_samples-length; i++) - m_mem[offset+i] = m_mem[offset+i+length]; - for(; i < m_samples; i++) - m_mem[offset+i] = (float)*samples++/scale_factor; -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotScalar::draw(wxAutoBufferedPaintDC& dc) -{ - float index_to_px; - float a_to_py; - int i; - int x, y; - int prev_x, prev_y; - float a; - - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - if (!m_mini) - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - //printf("h %d w %d\n", m_rCtrl.GetWidth(), m_rCtrl.GetHeight()); - //printf("h %d w %d\n", m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - // black background - - dc.Clear(); - if (m_mini) - m_rPlot = wxRect(0, 0, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - else - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - index_to_px = (float)m_rGrid.GetWidth()/m_samples; - a_to_py = (float)m_rGrid.GetHeight()/(m_a_max - m_a_min); - - wxPen pen; - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - // draw all samples - - prev_x = prev_y = 0; // stop warning - - // plot each channel - - int offset; - for(offset=0; offset m_a_max) a = m_a_max; - - // invert y axis and offset by minimum - - y = m_rGrid.GetHeight() - a_to_py * a + m_a_min*a_to_py; - - // put inside plot window - - if (!m_mini) { - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - } - - if (i) - dc.DrawLine(x, y, prev_x, prev_y); - prev_x = x; prev_y = y; - } - } - - drawGraticule(dc); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotScalar::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - float t, a; - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float sec_to_px; - float a_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - sec_to_px = (float)m_rGrid.GetWidth()/m_t_secs; - a_to_py = (float)m_rGrid.GetHeight()/(m_a_max - m_a_min); - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Vertical gridlines - - dc.SetPen(m_penShortDash); - for(t=0; t<=m_t_secs; t+=m_graticule_t_step) { - x = t*sec_to_px; - if (m_mini) { - dc.DrawLine(x, m_rGrid.GetHeight(), x, 0); - } - else { - x += PLOT_BORDER + XLEFT_OFFSET; - dc.DrawLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, PLOT_BORDER); - } - if (!m_mini) { - sprintf(buf, "%2.1fs", t); - GetTextExtent(buf, &text_w, &text_h); - dc.DrawText(buf, x - text_w/2, m_rGrid.GetHeight() + PLOT_BORDER + YBOTTOM_TEXT_OFFSET); - } - } - - // Horizontal gridlines - - dc.SetPen(m_penDotDash); - for(a=m_a_min; a. -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SCALAR__ -#define __FDMDV2_PLOT_SCALAR__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotScalar -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotScalar: public PlotPanel -{ - public: - - PlotScalar(wxFrame* parent, - int channels, - float t_secs, - float sample_period_secs, - float a_min, - float a_max, - float graticule_t_step, - float graticule_a_step, - const char a_fmt[], - int mini - ); - ~PlotScalar(); - void add_new_sample(int channel, float sample); - void add_new_short_samples(int channel, short samples[], int length, float scale_factor); - - protected: - - int m_channels; - float m_t_secs; - float m_sample_period_secs; - float m_a_min; - float m_a_max; - float m_graticule_t_step; - float m_graticule_a_step; - char m_a_fmt[15]; - int m_mini; - int m_samples; - float *m_mem; - - void draw(wxAutoBufferedPaintDC& dc); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - - DECLARE_EVENT_TABLE() -}; - -#endif // __FDMDV2_PLOT_SCALAR__ - diff --git a/freedv/tags/1.2.2/src/fdmdv2_plot_scatter.cpp b/freedv/tags/1.2.2/src/fdmdv2_plot_scatter.cpp deleted file mode 100644 index 12957f44..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_plot_scatter.cpp +++ /dev/null @@ -1,289 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_scatter.cpp -// Purpose: A scatter plot derivative of fdmdv2_plot. -// Created: June 24, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_plot_scatter.h" - -BEGIN_EVENT_TABLE(PlotScatter, PlotPanel) - EVT_PAINT (PlotScatter::OnPaint) - EVT_MOTION (PlotScatter::OnMouseMove) - EVT_MOUSEWHEEL (PlotScatter::OnMouseWheelMoved) - EVT_SIZE (PlotScatter::OnSize) - EVT_SHOW (PlotScatter::OnShow) -// EVT_ERASE_BACKGROUND(PlotScatter::OnErase) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// PlotScatter -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotScatter::PlotScatter(wxFrame* parent) : PlotPanel(parent) -{ - int i; - - for(i=0; i < SCATTER_MEM_SYMS_MAX; i++) - { - m_mem[i].real = 0.0; - m_mem[i].imag = 0.0; - } - - m_filter_max_xy = m_filter_max_y = 0.1; - - // defaults so we start off with something sensible - - Nsym = 14+1; - scatterMemSyms = ((int)(SCATTER_MEM_SECS*(Nsym/DT))); - assert(scatterMemSyms <= SCATTER_MEM_SYMS_MAX); - - Ncol = 0; - memset(eye_mem, 0, sizeof(eye_mem)); - - mode = PLOT_SCATTER_MODE_SCATTER; -} - -// changing number of carriers changes number of symbols to plot -void PlotScatter::setNc(int Nc) { - Nsym = Nc+1; - assert(Nsym <= (MODEM_STATS_NC_MAX+1)); - scatterMemSyms = ((int)(SCATTER_MEM_SECS*(Nsym/DT))); - assert(scatterMemSyms <= SCATTER_MEM_SYMS_MAX); -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotScatter::draw(wxAutoBufferedPaintDC& dc) -{ - float x_scale; - float y_scale; - int i,j; - int x; - int y; - wxColour sym_to_colour[] = {wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255), - wxColor(255,255,0), - wxColor(255,255,255), - wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255), - wxColor(255,255,0), - wxColor(255,255,255), - wxColor(0,0,255), - wxColor(0,255,0), - wxColor(0,255,255), - wxColor(255,0,0), - wxColor(255,0,255) - }; - - m_rCtrl = GetClientRect(); - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - // black background - - dc.Clear(); - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - wxPen pen; - pen.SetWidth(1); // note this is ignored by DrawPoint - - if (mode == PLOT_SCATTER_MODE_SCATTER) { - - // automatically scale, first measure the maximum value - - float max_xy = 1E-12; - float real,imag; - for(i=0; i< scatterMemSyms; i++) { - real = fabs(m_mem[i].real); - imag = fabs(m_mem[i].imag); - if (real > max_xy) - max_xy = real; - if (imag > max_xy) - max_xy = imag; - } - - // smooth it out and set a lower limit to prevent divide by 0 issues - - m_filter_max_xy = BETA*m_filter_max_xy + (1 - BETA)*2.5*max_xy; - if (m_filter_max_xy < 0.001) - m_filter_max_xy = 0.001; - - // quantise to log steps to prevent scatter scaling bobbing about too - // much as scaling varies - - float quant_m_filter_max_xy = exp(floor(0.5+log(m_filter_max_xy))); - //printf("max_xy: %f m_filter_max_xy: %f quant_m_filter_max_xy: %f\n", max_xy, m_filter_max_xy, quant_m_filter_max_xy); - - x_scale = (float)m_rGrid.GetWidth()/quant_m_filter_max_xy; - y_scale = (float)m_rGrid.GetHeight()/quant_m_filter_max_xy; - - // draw all samples - - for(i = 0; i < scatterMemSyms; i++) { - x = x_scale * m_mem[i].real + m_rGrid.GetWidth()/2; - y = y_scale * m_mem[i].imag + m_rGrid.GetHeight()/2; - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - pen.SetColour(sym_to_colour[i%Nsym]); - dc.SetPen(pen); - dc.DrawPoint(x, y); - } - } - - if (mode == PLOT_SCATTER_MODE_EYE) { - - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - // automatically scale, first measure the maximum Y value - - float max_y = 1E-12; - float min_y = 1E+12; - for(i=0; i max_y) { - max_y = eye_mem[i][j]; - } - if (eye_mem[i][j] < min_y) { - min_y = eye_mem[i][j]; - } - } - } - - // smooth it out and set a lower limit to prevent divide by 0 issues - - m_filter_max_y = BETA*m_filter_max_y + (1 - BETA)*2.5*max_y; - if (m_filter_max_y < 0.001) - m_filter_max_y = 0.001; - - // quantise to log steps to prevent scatter scaling bobbing about too - // much as scaling varies - - float quant_m_filter_max_y = exp(floor(0.5+log(m_filter_max_y))); - //printf("min_y: %4.3f max_y: %4.3f quant_m_filter_max_y: %4.3f\n", min_y, max_y, quant_m_filter_max_y); - - x_scale = (float)m_rGrid.GetWidth()/Ncol; - y_scale = (float)m_rGrid.GetHeight()/quant_m_filter_max_y; - //printf("GetWidth(): %d GetHeight(): %d\n", m_rGrid.GetWidth(), m_rGrid.GetHeight()); - - // plot eye traces row by row - - int prev_x, prev_y; - prev_x = prev_y = 0; - for(i=0; i. -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SCATTER__ -#define __FDMDV2_PLOT_SCATTER__ - -#include "comp.h" -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -#define PLOT_SCATTER_MODE_SCATTER 0 -#define PLOT_SCATTER_MODE_EYE 1 -#define PLOT_SCATTER_EYE_MAX_SAMPLES_ROW 80 - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotScatter -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotScatter : public PlotPanel -{ - public: - PlotScatter(wxFrame* parent); - ~PlotScatter(){}; - void add_new_samples_scatter(COMP samples[]); - void add_new_samples_eye(float samples[], int n); - void setNc(int Nc); - void setEyeScatter(int eye_mode) {mode = eye_mode;} - - protected: - int mode; - COMP m_mem[SCATTER_MEM_SYMS_MAX]; - COMP m_new_samples[MODEM_STATS_NC_MAX+1]; - float eye_mem[SCATTER_EYE_MEM_ROWS][PLOT_SCATTER_EYE_MAX_SAMPLES_ROW]; - - void draw(wxAutoBufferedPaintDC& dc); - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - - DECLARE_EVENT_TABLE() - - private: - int Nsym; - int Ncol; - int scatterMemSyms; - float m_filter_max_xy, m_filter_max_y; -}; - -#endif //__FDMDV2_PLOT_SCATTER__ diff --git a/freedv/tags/1.2.2/src/fdmdv2_plot_spectrum.cpp b/freedv/tags/1.2.2/src/fdmdv2_plot_spectrum.cpp deleted file mode 100644 index 1f5be59b..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_plot_spectrum.cpp +++ /dev/null @@ -1,267 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.cpp -// Purpose: Implements a waterfall plot derivative of fdmdv2_plot. -// Created: June 23, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" - -#include "fdmdv2_main.h" - -extern float g_avmag[]; // average mag data passed to draw() -void fdmdv2_clickTune(float frequency); // callback to pass new click freq - -BEGIN_EVENT_TABLE(PlotSpectrum, PlotPanel) - EVT_MOTION (PlotSpectrum::OnMouseMove) - EVT_LEFT_DOWN (PlotSpectrum::OnMouseLeftDown) - EVT_LEFT_DCLICK (PlotSpectrum::OnMouseLeftDoubleClick) - EVT_LEFT_UP (PlotSpectrum::OnMouseLeftUp) - EVT_MOUSEWHEEL (PlotSpectrum::OnMouseWheelMoved) - EVT_PAINT (PlotSpectrum::OnPaint) - EVT_SHOW (PlotSpectrum::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotSpectrum -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotSpectrum::PlotSpectrum(wxFrame* parent, float *magdB, int n_magdB, - float min_mag_db, float max_mag_db, bool clickTune): PlotPanel(parent) -{ - m_greyscale = 0; - m_Bufsz = GetMaxClientSize(); - m_newdata = false; - m_firstPass = true; - m_line_color = 0; - SetLabelSize(10.0); - - m_magdB = magdB; - m_n_magdB = n_magdB; // number of points in magdB that covers 0 ... MAX_F_HZ of spectrum - m_max_mag_db = max_mag_db; - m_min_mag_db = min_mag_db; - m_rxFreq = 0.0; - m_clickTune = clickTune; -} - -//---------------------------------------------------------------- -// ~PlotSpectrum() -//---------------------------------------------------------------- -PlotSpectrum::~PlotSpectrum() -{ -} - -//---------------------------------------------------------------- -// OnSize() -//---------------------------------------------------------------- -void PlotSpectrum::OnSize(wxSizeEvent& event) { -} - -//---------------------------------------------------------------- -// OnPaint() -//---------------------------------------------------------------- -void PlotSpectrum::OnPaint(wxPaintEvent& event) -{ - wxAutoBufferedPaintDC dc(this); - draw(dc); -} - -//---------------------------------------------------------------- -// OnShow() -//---------------------------------------------------------------- -void PlotSpectrum::OnShow(wxShowEvent& event) -{ -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotSpectrum::draw(wxAutoBufferedPaintDC& dc) -{ - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. We need to work this out every time we draw - // as OnSize() may not be called before OnPaint(), for example when a new tab - // is selected - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - dc.Clear(); - - // black background - - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - - // draw spectrum - - int x, y, prev_x, prev_y, index; - float index_to_px, mag_dB_to_py, mag; - - m_newdata = false; - - wxPen pen; - pen.SetColour(DARK_GREEN_COLOR); - pen.SetWidth(1); - dc.SetPen(pen); - - index_to_px = (float)m_rGrid.GetWidth()/m_n_magdB; - mag_dB_to_py = (float)m_rGrid.GetHeight()/(m_max_mag_db - m_min_mag_db); - - prev_x = PLOT_BORDER + XLEFT_OFFSET; - prev_y = PLOT_BORDER; - for(index = 0; index < m_n_magdB; index++) - { - x = index*index_to_px; - mag = m_magdB[index]; - if (mag > m_max_mag_db) mag = m_max_mag_db; - if (mag < m_min_mag_db) mag = m_min_mag_db; - y = -(mag - m_max_mag_db) * mag_dB_to_py; - - x += PLOT_BORDER + XLEFT_OFFSET; - y += PLOT_BORDER; - - if (index) - dc.DrawLine(x, y, prev_x, prev_y); - prev_x = x; prev_y = y; - } - - // and finally draw Graticule - - drawGraticule(dc); - -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotSpectrum::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float f, mag, freq_hz_to_px, mag_dB_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - mag_dB_to_py = (float)m_rGrid.GetHeight()/(m_max_mag_db - m_min_mag_db); - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Check if small screen size means text will overlap - - int textXStep = STEP_F_HZ*freq_hz_to_px; - int textYStep = STEP_MAG_DB*mag_dB_to_py; - sprintf(buf, "%4.0fHz", (float)MAX_F_HZ - STEP_F_HZ); - GetTextExtent(buf, &text_w, &text_h); - int overlappedText = (text_w > textXStep) || (text_h > textYStep); - //printf("text_w: %d textXStep: %d text_h: %d textYStep: %d overlappedText: %d\n", text_w, textXStep, - // text_h, textYStep, overlappedText); - - // Vertical gridlines - - for(f=STEP_F_HZ; f= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0) && m_clickTune) { - float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - float clickFreq = (float)pt.x/freq_hz_to_px; - - // see PlotWaterfall::OnMouseDown() - - fdmdv2_clickTune(clickFreq); - } -} diff --git a/freedv/tags/1.2.2/src/fdmdv2_plot_spectrum.h b/freedv/tags/1.2.2/src/fdmdv2_plot_spectrum.h deleted file mode 100644 index 271eeb98..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_plot_spectrum.h +++ /dev/null @@ -1,58 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_spectrum.h -// Purpose: Defines a spectrum plot derived from fdmdv2_plot class. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_PLOT_SPECTRUM__ -#define __FDMDV2_PLOT_SPECTRUM__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class Waterfall -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotSpectrum : public PlotPanel -{ - public: - PlotSpectrum(wxFrame* parent, float *magdB, int n_magdB, - float min_mag_db=MIN_MAG_DB, float max_mag_db=MAX_MAG_DB, bool clickTune=true); - ~PlotSpectrum(); - void setRxFreq(float rxFreq) { m_rxFreq = rxFreq; } - void setFreqScale(int n_magdB) { m_n_magdB = n_magdB; } - - protected: - void OnPaint(wxPaintEvent& event); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void draw(wxAutoBufferedPaintDC& dc); - void OnMouseLeftDoubleClick(wxMouseEvent& event); - - private: - float m_rxFreq; - float m_max_mag_db; - float m_min_mag_db; - float *m_magdB; - int m_n_magdB; - bool m_clickTune; - - DECLARE_EVENT_TABLE() -}; - -#endif //__FDMDV2_PLOT_SPECTRUM__ diff --git a/freedv/tags/1.2.2/src/fdmdv2_plot_waterfall.cpp b/freedv/tags/1.2.2/src/fdmdv2_plot_waterfall.cpp deleted file mode 100644 index cdbe01e0..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_plot_waterfall.cpp +++ /dev/null @@ -1,483 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.cpp -// Purpose: Implements a waterfall plot derivative of fdmdv2_plot. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include -#include "wx/wx.h" -#include "fdmdv2_main.h" - -extern float g_avmag[]; // av mag spec passed in to draw() -void fdmdv2_clickTune(float frequency); // callback to pass new click freq - -BEGIN_EVENT_TABLE(PlotWaterfall, PlotPanel) - EVT_PAINT (PlotWaterfall::OnPaint) - EVT_MOTION (PlotWaterfall::OnMouseMove) - EVT_LEFT_DCLICK (PlotWaterfall::OnMouseLeftDoubleClick) - EVT_RIGHT_DOWN (PlotWaterfall::OnMouseRightDown) - EVT_LEFT_UP (PlotWaterfall::OnMouseLeftUp) - EVT_MOUSEWHEEL (PlotWaterfall::OnMouseWheelMoved) - EVT_SIZE (PlotWaterfall::OnSize) - EVT_SHOW (PlotWaterfall::OnShow) -END_EVENT_TABLE() - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class WaterfallPlot -// -// @class WaterfallPlot -// @author David Witten -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -PlotWaterfall::PlotWaterfall(wxFrame* parent, bool graticule, int colour): PlotPanel(parent) -{ - - for(int i = 0; i < 255; i++) - { - m_heatmap_lut[i] = heatmap((float)i, 0.0, 255.0); - } - m_graticule = graticule; - m_colour = colour; - m_Bufsz = GetMaxClientSize(); - m_newdata = false; - m_firstPass = true; - m_line_color = 0; - m_modem_stats_max_f_hz = MODEM_STATS_MAX_F_HZ; - - SetLabelSize(10.0); - - m_pBmp = NULL; - m_max_mag = MAX_MAG_DB; - m_min_mag = MIN_MAG_DB; -} - -// When the window size gets set we can work outthe size of the window -// we plot in and allocate a bit map of the correct size -void PlotWaterfall::OnSize(wxSizeEvent& event) -{ - // resize bit map - - delete m_pBmp; - - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - // we want a bit map the size of m_rGrid - - m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24); - - m_dT = DT; -} - -//---------------------------------------------------------------- -// paintEvent() -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -// Called by the system of by wxWidgets when the panel needs -// to be redrawn. You can also trigger this call by calling -// Refresh()/Update(). -//---------------------------------------------------------------- -void PlotWaterfall::OnPaint(wxPaintEvent & evt) -{ - wxAutoBufferedPaintDC dc(this); - draw(dc); -} - -//---------------------------------------------------------------- -// OnShow() -//---------------------------------------------------------------- -void PlotWaterfall::OnShow(wxShowEvent& event) -{ -} - -//---------------------------------------------------------------- -// ~PlotWaterfall() -//---------------------------------------------------------------- -PlotWaterfall::~PlotWaterfall() -{ -} - -//---------------------------------------------------------------- -// heatmap() -// map val to a rgb colour -// from http://eddiema.ca/2011/01/21/c-sharp-heatmaps/ -//---------------------------------------------------------------- -unsigned PlotWaterfall::heatmap(float val, float min, float max) -{ - unsigned r = 0; - unsigned g = 0; - unsigned b = 0; - - val = (val - min) / (max - min); - if(val <= 0.2) - { - b = (unsigned)((val / 0.2) * 255); - } - else if(val > 0.2 && val <= 0.7) - { - b = (unsigned)((1.0 - ((val - 0.2) / 0.5)) * 255); - } - if(val >= 0.2 && val <= 0.6) - { - g = (unsigned)(((val - 0.2) / 0.4) * 255); - } - else if(val > 0.6 && val <= 0.9) - { - g = (unsigned)((1.0 - ((val - 0.6) / 0.3)) * 255); - } - if(val >= 0.5) - { - r = (unsigned)(((val - 0.5) / 0.5) * 255); - } - //printf("%f %x %x %x\n", val, r, g, b); - return (b << 16) + (g << 8) + r; -} - -bool PlotWaterfall::checkDT(void) -{ - // Check dY is > 1 pixel before proceeding. For small screens - // and large WATERFALL_SECS_Y we might have less than one - // block per pixel. In this case increase m_dT and perform draw - // less often - - float px_per_sec = (float)m_rGrid.GetHeight() / WATERFALL_SECS_Y; - float dy = m_dT * px_per_sec; - - if (dy < 1.0) { - m_dT += DT; - return false; - } - else - return true; -} - -//---------------------------------------------------------------- -// draw() -//---------------------------------------------------------------- -void PlotWaterfall::draw(wxAutoBufferedPaintDC& dc) -{ - - m_rCtrl = GetClientRect(); - - // m_rGrid is coords of inner window we actually plot to. We deflate it a bit - // to leave room for axis labels. - - m_rGrid = m_rCtrl; - m_rGrid = m_rGrid.Deflate(PLOT_BORDER + (XLEFT_OFFSET/2), (PLOT_BORDER + (YBOTTOM_OFFSET/2))); - - if (m_pBmp == NULL) - { - // we want a bit map the size of m_rGrid - m_pBmp = new wxBitmap(m_rGrid.GetWidth(), m_rGrid.GetHeight(), 24); - } - - dc.Clear(); - - if(m_newdata) - { - m_newdata = false; - plotPixelData(); - dc.DrawBitmap(*m_pBmp, PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER); - m_dT = DT; - } - else - { - - // no data to plot so just erase to black. Blue looks nicer - // but is same colour as low amplitude signal - - // Bug on Linux: When Stop is pressed this code doesn't erase - // the lower 25% of the Waterfall Window - - m_rPlot = wxRect(PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight()); - wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 0)); - dc.DrawRectangle(m_rPlot); - } - drawGraticule(dc); -} - -//------------------------------------------------------------------------- -// drawGraticule() -//------------------------------------------------------------------------- -void PlotWaterfall::drawGraticule(wxAutoBufferedPaintDC& dc) -{ - int x, y, text_w, text_h; - char buf[15]; - wxString s; - float f, time, freq_hz_to_px, time_s_to_py; - - wxBrush ltGraphBkgBrush; - ltGraphBkgBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - ltGraphBkgBrush.SetColour(*wxBLACK); - dc.SetBrush(ltGraphBkgBrush); - dc.SetPen(wxPen(BLACK_COLOR, 1)); - - freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - time_s_to_py = (float)m_rGrid.GetHeight()/WATERFALL_SECS_Y; - - // upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER) - // lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(), - // PLOT_BORDER + m_rGrid.GetHeight()) - - // Check if small screen size means text will overlap - - int textXStep = STEP_F_HZ*freq_hz_to_px; - int textYStep = WATERFALL_SECS_STEP*time_s_to_py; - sprintf(buf, "%4.0fHz", (float)MAX_F_HZ - STEP_F_HZ); - GetTextExtent(buf, &text_w, &text_h); - int overlappedText = (text_w > textXStep) || (text_h > textYStep); - - // Major Vertical gridlines and legend - //dc.SetPen(m_penShortDash); - for(f=STEP_F_HZ; f max_mag) - { - max_mag = g_avmag[i]; - } - } - - m_max_mag = BETA*m_max_mag + (1 - BETA)*max_mag; - m_min_mag = max_mag - 20.0; - //printf("max_mag: %f m_max_mag: %f\n", max_mag, m_max_mag); - //intensity_per_dB = (float)256 /(MAX_MAG_DB - MIN_MAG_DB); - intensity_per_dB = (float)256 /(m_max_mag - m_min_mag); - spec_index_per_px = ((float)(MAX_F_HZ)/(float)m_modem_stats_max_f_hz)*(float)MODEM_STATS_NSPEC / (float) m_rGrid.GetWidth(); - - /* - printf("h %d w %d px_per_sec %d dy %d dy_blocks %d spec_index_per_px: %f\n", - m_rGrid.GetHeight(), m_rGrid.GetWidth(), px_per_sec, - dy, dy_blocks, spec_index_per_px); - */ - - // Shift previous bit map up one row of blocks ---------------------------- - wxNativePixelData data(*m_pBmp); - wxNativePixelData::Iterator bitMapStart(data); - wxNativePixelData::Iterator p = bitMapStart; - - for(b = 0; b < dy_blocks - 1; b++) - { - wxNativePixelData::Iterator psrc = bitMapStart; - wxNativePixelData::Iterator pdest = bitMapStart; - pdest.OffsetY(data, dy * b); - psrc.OffsetY(data, dy * (b+1)); - - // copy one line of blocks - - for(py = 0; py < dy; py++) - { - wxNativePixelData::Iterator pdestRowStart = pdest; - wxNativePixelData::Iterator psrcRowStart = psrc; - - for(px = 0; px < m_rGrid.GetWidth(); px++) - { - pdest.Red() = psrc.Red(); - pdest.Green() = psrc.Green(); - pdest.Blue() = psrc.Blue(); - pdest++; - psrc++; - } - pdest = pdestRowStart; - pdest.OffsetY(data, 1); - psrc = psrcRowStart; - psrc.OffsetY(data, 1); - } - } - - // Draw last line of blocks using latest amplitude data ------------------ - p = bitMapStart; - p.OffsetY(data, dy *(dy_blocks - 1)); - for(py = 0; py < dy; py++) - { - wxNativePixelData::Iterator rowStart = p; - - for(px = 0; px < m_rGrid.GetWidth(); px++) - { - index = px * spec_index_per_px; - assert(index < MODEM_STATS_NSPEC); - - intensity = intensity_per_dB * (g_avmag[index] - m_min_mag); - if(intensity > 255) intensity = 255; - if (intensity < 0) intensity = 0; - //printf("%d %f %d \n", index, g_avmag[index], intensity); - - switch (m_colour) { - case 0: - p.Red() = m_heatmap_lut[intensity] & 0xff; - p.Green() = (m_heatmap_lut[intensity] >> 8) & 0xff; - p.Blue() = (m_heatmap_lut[intensity] >> 16) & 0xff; - break; - case 1: - p.Red() = intensity; - p.Green() = intensity; - p.Blue() = intensity; - break; - case 2: - p.Red() = intensity; - p.Green() = intensity; - if (intensity < 127) - p.Blue() = intensity*2; - else - p.Blue() = 255; - - break; - } - ++p; - } - p = rowStart; - p.OffsetY(data, 1); - } - -} - -//------------------------------------------------------------------------- -// OnMouseLeftDown() -//------------------------------------------------------------------------- -void PlotWaterfall::OnMouseLeftDoubleClick(wxMouseEvent& event) -{ - m_mouseDown = true; - wxClientDC dc(this); - - wxPoint pt(event.GetLogicalPosition(dc)); - - // map x coord to edges of actual plot - pt.x -= PLOT_BORDER + XLEFT_OFFSET; - pt.y -= PLOT_BORDER; - - // valid click if inside of plot - if ((pt.x >= 0) && (pt.x <= m_rGrid.GetWidth()) && (pt.y >=0)) - { - float freq_hz_to_px = (float)m_rGrid.GetWidth()/(MAX_F_HZ-MIN_F_HZ); - float clickFreq = (float)pt.x/freq_hz_to_px; - - // communicate back to other threads - fdmdv2_clickTune(clickFreq); - } -} - -//------------------------------------------------------------------------- -// OnMouseRightDown() -//------------------------------------------------------------------------- -void PlotWaterfall::OnMouseRightDown(wxMouseEvent& event) -{ - m_colour++; - if (m_colour == 3) - m_colour = 0; -} - diff --git a/freedv/tags/1.2.2/src/fdmdv2_plot_waterfall.h b/freedv/tags/1.2.2/src/fdmdv2_plot_waterfall.h deleted file mode 100644 index f4896c6b..00000000 --- a/freedv/tags/1.2.2/src/fdmdv2_plot_waterfall.h +++ /dev/null @@ -1,73 +0,0 @@ -//========================================================================== -// Name: fdmdv2_plot_waterfall.h -// Purpose: Defines a waterfall plot derivative of fdmdv2_plot. -// Created: June 22, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#ifndef __FDMDV2_PLOT_WATERFALL__ -#define __FDMDV2_PLOT_WATERFALL__ - -#include "fdmdv2_plot.h" -#include "fdmdv2_defines.h" - -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -// Class PlotWaterfall -// -// @class $(Name) -// @author $(User) -// @date $(Date) -// @file $(CurrentFileName).$(CurrentFileExt) -// @brief -// -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= -class PlotWaterfall : public PlotPanel -{ - public: - PlotWaterfall(wxFrame* parent, bool graticule, int colour); - ~PlotWaterfall(); - bool checkDT(void); - void setGreyscale(bool greyscale) { m_greyscale = greyscale; } - void setRxFreq(float rxFreq) { m_rxFreq = rxFreq; } - void setFs(int fs) { m_modem_stats_max_f_hz = fs/2; } - - protected: - unsigned m_heatmap_lut[256]; - - unsigned heatmap(float val, float min, float max); - - void OnPaint(wxPaintEvent & evt); - void OnSize(wxSizeEvent& event); - void OnShow(wxShowEvent& event); - void drawGraticule(wxAutoBufferedPaintDC& dc); - void draw(wxAutoBufferedPaintDC& dc); - void plotPixelData(); - void OnMouseLeftDoubleClick(wxMouseEvent& event); - void OnMouseRightDown(wxMouseEvent& event); - - private: - float m_dT; - float m_rxFreq; - bool m_graticule; - float m_min_mag; - float m_max_mag; - int m_colour; - int m_modem_stats_max_f_hz; - - DECLARE_EVENT_TABLE() -}; - -#endif //__FDMDV2_PLOT_WATERFALL__ diff --git a/freedv/tags/1.2.2/src/freedv.icns b/freedv/tags/1.2.2/src/freedv.icns deleted file mode 100644 index 5190e7951ffd9152576d6569f0c7ca64927649ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92136 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

-_`&2*Z5cR;kk!g) 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 bcU%5HHl?3v;lGWz00000NkvXXu0mjfpGTzJ diff --git a/freedv/tags/1.2.2/src/hamlib.cpp b/freedv/tags/1.2.2/src/hamlib.cpp deleted file mode 100644 index ff80b24a..00000000 --- a/freedv/tags/1.2.2/src/hamlib.cpp +++ /dev/null @@ -1,160 +0,0 @@ -//========================================================================== -// Name: hamlib.cpp -// -// Purpose: Hamlib integration for FreeDV -// Created: May 2013 -// Authors: Joel Stanley -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include - -#include -#include - -using namespace std; - -typedef std::vector riglist_t; - -static bool rig_cmp(const struct rig_caps *rig1, const struct rig_caps *rig2); -static int build_list(const struct rig_caps *rig, rig_ptr_t); - -Hamlib::Hamlib() : m_rig(NULL) { - /* Stop hamlib from spewing info to stderr. */ - rig_set_debug(RIG_DEBUG_NONE); - - /* Create sorted list of rigs. */ - rig_load_all_backends(); - rig_list_foreach(build_list, &m_rigList); - sort(m_rigList.begin(), m_rigList.end(), rig_cmp); - - /* Reset debug output. */ - rig_set_debug(RIG_DEBUG_VERBOSE); - - m_rig = NULL; -} - -Hamlib::~Hamlib() { - if(m_rig) - close(); -} - -static int build_list(const struct rig_caps *rig, rig_ptr_t rigList) { - ((riglist_t *)rigList)->push_back(rig); - return 1; -} - -static bool rig_cmp(const struct rig_caps *rig1, const struct rig_caps *rig2) { - /* Compare manufacturer. */ - int r = strcasecmp(rig1->mfg_name, rig2->mfg_name); - if (r != 0) - return r < 0; - - /* Compare model. */ - r = strcasecmp(rig1->model_name, rig2->model_name); - if (r != 0) - return r < 0; - - /* Compare rig ID. */ - return rig1->rig_model < rig2->rig_model; -} - -void Hamlib::populateComboBox(wxComboBox *cb) { - - riglist_t::const_iterator rig = m_rigList.begin(); - for (; rig !=m_rigList.end(); rig++) { - char name[128]; - snprintf(name, 128, "%s %s", (*rig)->mfg_name, (*rig)->model_name); - cb->Append(name); - } -} - -bool Hamlib::connect(unsigned int rig_index, const char *serial_port, const int serial_rate) { - /* Look up model from index. */ - if (rig_index >= m_rigList.size()) { - return false; - } - fprintf(stderr, "rig: %s %s (%d)\n", m_rigList[rig_index]->mfg_name, - m_rigList[rig_index]->model_name, m_rigList[rig_index]->rig_model); - - if(m_rig) { - printf("Closing old hamlib instance!\n"); - close(); - } - - /* Initialise, configure and open. */ - - m_rig = rig_init(m_rigList[rig_index]->rig_model); - - if (!m_rig) - return false; - - /* TODO we may also need civaddr for Icom */ - - strncpy(m_rig->state.rigport.pathname, serial_port, FILPATHLEN - 1); - if (serial_rate) { - m_rig->state.rigport.parm.serial.rate = serial_rate; - } - fprintf(stderr, "hamlib: setting serial rate: %d\n", m_rig->state.rigport.parm.serial.rate); - - if (rig_open(m_rig) == RIG_OK) { - return true; - } - - return false; -} - -int Hamlib::get_serial_rate(void) { - return m_rig->state.rigport.parm.serial.rate; -} - -int Hamlib::get_data_bits(void) { - return m_rig->state.rigport.parm.serial.data_bits; -} - -int Hamlib::get_stop_bits(void) { - return m_rig->state.rigport.parm.serial.stop_bits; -} - -bool Hamlib::ptt(bool press, wxString &hamlibError) { - fprintf(stderr,"Hamlib::ptt: %d\n", press); - hamlibError = ""; - - if(!m_rig) - return false; - - /* TODO(Joel): make ON_DATA and ON configurable. */ - - ptt_t on = press ? RIG_PTT_ON : RIG_PTT_OFF; - - /* TODO(Joel): what should the VFO option be? */ - - int retcode = rig_set_ptt(m_rig, RIG_VFO_CURR, on); - fprintf(stderr,"Hamlib::ptt: rig_set_ptt returned: %d\n", retcode); - if (retcode != RIG_OK ) { - fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(retcode)); - hamlibError = rigerror(retcode); - } - - return retcode == RIG_OK; -} - -void Hamlib::close(void) { - if(m_rig) { - rig_close(m_rig); - rig_cleanup(m_rig); - m_rig = NULL; - } -} diff --git a/freedv/tags/1.2.2/src/hamlib.h b/freedv/tags/1.2.2/src/hamlib.h deleted file mode 100644 index 150ed86a..00000000 --- a/freedv/tags/1.2.2/src/hamlib.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef HAMLIB_H -#define HAMLIB_H - -extern "C" { -#include -} -#include -#include - -class Hamlib { - - public: - Hamlib(); - ~Hamlib(); - void populateComboBox(wxComboBox *cb); - bool connect(unsigned int rig_index, const char *serial_port); - bool ptt(bool press, wxString &hamlibError); - void close(void); - int get_serial_rate(void); - int get_data_bits(void); - int get_stop_bits(void); - - typedef std::vector riglist_t; - - private: - RIG *m_rig; - /* Sorted list of rigs. */ - riglist_t m_rigList; -}; - -#endif /*HAMLIB_H*/ diff --git a/freedv/tags/1.2.2/src/info.plist b/freedv/tags/1.2.2/src/info.plist deleted file mode 100644 index 8f0d4c34..00000000 --- a/freedv/tags/1.2.2/src/info.plist +++ /dev/null @@ -1,104 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - CFBundleIconFile - freedv - NSPrincipalClass - NSApplication - - - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - NSPrincipalClass - NSApplication - - - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - freedv - CFBundleIconFile - - CFBundleIdentifier - org.freedv.freedv - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FreeDV - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSMinimumSystemVersion - 10.5 - NSHumanReadableCopyright - Copyright © 2012 FreeDV. All rights reserved. - - NSPrincipalClass - NSApplication - - \ No newline at end of file diff --git a/freedv/tags/1.2.2/src/serialport.cpp b/freedv/tags/1.2.2/src/serialport.cpp deleted file mode 100644 index 59dd0c93..00000000 --- a/freedv/tags/1.2.2/src/serialport.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include -#include -#include -#include "serialport.h" - -Serialport::Serialport() { - com_handle = COM_HANDLE_INVALID; -} - -Serialport::~Serialport() { - if (isopen()) { - closeport(); - } -} - -// returns true if comm port opened OK, false if there was a problem - -bool Serialport::openport(const char name[], bool useRTS, bool RTSPos, bool useDTR, bool DTRPos) -{ - if (com_handle != COM_HANDLE_INVALID) { - closeport(); - } - - m_useRTS = useRTS; - m_RTSPos = RTSPos; - m_useDTR = useDTR; - m_DTRPos = DTRPos; - -#ifdef _WIN32 - { - COMMCONFIG CC; - DWORD CCsize=sizeof(CC); - COMMTIMEOUTS timeouts; - DCB dcb; - - if(GetDefaultCommConfigA(name, &CC, &CCsize)) { - CC.dcb.fOutxCtsFlow = FALSE; - CC.dcb.fOutxDsrFlow = FALSE; - CC.dcb.fDtrControl = DTR_CONTROL_DISABLE; - CC.dcb.fDsrSensitivity = FALSE; - CC.dcb.fRtsControl = RTS_CONTROL_DISABLE; - SetDefaultCommConfigA(name, &CC, CCsize); - } - - if((com_handle=CreateFileA(name - ,GENERIC_READ|GENERIC_WRITE /* Access */ - ,0 /* Share mode */ - ,NULL /* Security attributes */ - ,OPEN_EXISTING /* Create access */ - ,FILE_ATTRIBUTE_NORMAL /* File attributes */ - ,NULL /* Template */ - ))==INVALID_HANDLE_VALUE) - return false; - - if(GetCommTimeouts(com_handle, &timeouts)) { - timeouts.ReadIntervalTimeout=MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier=0; - timeouts.ReadTotalTimeoutConstant=0; // No-wait read timeout - timeouts.WriteTotalTimeoutMultiplier=0; - timeouts.WriteTotalTimeoutConstant=5000; // 5 seconds - SetCommTimeouts(com_handle,&timeouts); - } - - /* Force N-8-1 mode: */ - if(GetCommState(com_handle, &dcb)==TRUE) { - dcb.ByteSize = 8; - dcb.Parity = NOPARITY; - dcb.StopBits = ONESTOPBIT; - dcb.DCBlength = sizeof(DCB); - dcb.fBinary = TRUE; - dcb.fOutxCtsFlow = FALSE; - dcb.fOutxDsrFlow = FALSE; - dcb.fDtrControl = DTR_CONTROL_DISABLE; - dcb.fDsrSensitivity = FALSE; - dcb.fTXContinueOnXoff= TRUE; - dcb.fOutX = FALSE; - dcb.fInX = FALSE; - dcb.fRtsControl = RTS_CONTROL_DISABLE; - dcb.fAbortOnError = FALSE; - SetCommState(com_handle, &dcb); - } - } -#else - { - struct termios t; - - if((com_handle=open(name, O_NONBLOCK|O_RDWR))== COM_HANDLE_INVALID) - return false; - - if(tcgetattr(com_handle, &t)==-1) { - close(com_handle); - com_handle = COM_HANDLE_INVALID; - return false; - } - - t.c_iflag = ( - IGNBRK /* ignore BREAK condition */ - | IGNPAR /* ignore (discard) parity errors */ - ); - t.c_oflag = 0; /* No output processing */ - t.c_cflag = ( - CS8 /* 8 bits */ - | CREAD /* enable receiver */ - - /* - Fun snippet from the FreeBSD manpage: - - If CREAD is set, the receiver is enabled. Otherwise, no character is - received. Not all hardware supports this bit. In fact, this flag is - pretty silly and if it were not part of the termios specification it - would be omitted. - */ - | CLOCAL /* ignore modem status lines */ - ); - - t.c_lflag = 0; /* No local modes */ - if(tcsetattr(com_handle, TCSANOW, &t)==-1) { - close(com_handle); - com_handle = COM_HANDLE_INVALID; - return false; - } - - } -#endif - return true; -} - - -// fixme: this takes about one second to close under Linux - -void Serialport::closeport() -{ -#ifdef _WIN32 - CloseHandle(com_handle); -#else - close(com_handle); -#endif - com_handle = COM_HANDLE_INVALID; -} - -//---------------------------------------------------------------- -// (raise|lower)(RTS|DTR)() -// -// Raises/lowers the specified signal -//---------------------------------------------------------------- - -void Serialport::raiseDTR(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, SETDTR); -#else - { // For C89 happiness - int flags = TIOCM_DTR; - ioctl(com_handle, TIOCMBIS, &flags); - } -#endif -} - -void Serialport::raiseRTS(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, SETRTS); -#else - { // For C89 happiness - int flags = TIOCM_RTS; - ioctl(com_handle, TIOCMBIS, &flags); - } -#endif -} - -void Serialport::lowerDTR(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, CLRDTR); -#else - { // For C89 happiness - int flags = TIOCM_DTR; - ioctl(com_handle, TIOCMBIC, &flags); - } -#endif -} - -void Serialport::lowerRTS(void) -{ - if(com_handle == COM_HANDLE_INVALID) - return; -#ifdef _WIN32 - EscapeCommFunction(com_handle, CLRRTS); -#else - { // For C89 happiness - int flags = TIOCM_RTS; - ioctl(com_handle, TIOCMBIC, &flags); - } -#endif -} - -void Serialport::ptt(bool tx) { - - /* Truth table: - - g_tx RTSPos RTS - ------------------- - 0 1 0 - 1 1 1 - 0 0 1 - 1 0 0 - - exclusive NOR - */ - - if (com_handle != COM_HANDLE_INVALID) { - if (m_useRTS) { - //fprintf(stderr, "g_tx: %d m_boolRTSPos: %d serialLine: %d\n", g_tx, wxGetApp().m_boolRTSPos, g_tx == wxGetApp().m_boolRTSPos); - if (tx == m_RTSPos) - raiseRTS(); - else - lowerRTS(); - } - if (m_useDTR) { - //fprintf(stderr, "g_tx: %d m_boolDTRPos: %d serialLine: %d\n", g_tx, wxGetApp().m_boolDTRPos, g_tx == wxGetApp().m_boolDTRPos); - if (tx == m_DTRPos) - raiseDTR(); - else - lowerDTR(); - } - - } -} diff --git a/freedv/tags/1.2.2/src/serialport.h b/freedv/tags/1.2.2/src/serialport.h deleted file mode 100644 index e5db10b4..00000000 --- a/freedv/tags/1.2.2/src/serialport.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef SERIALPORT_H -#define SERIALPORT_H - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -// Serial ports called com port for historic reasons, especially on Windows machines - -#ifdef _WIN32 -#define COM_HANDLE_INVALID INVALID_HANDLE_VALUE -typedef HANDLE com_handle_t; -#else -#define COM_HANDLE_INVALID -1 -typedef int com_handle_t; -#endif - -class Serialport { - - public: - Serialport(); - ~Serialport(); - bool openport(const char port[], bool useRTS, bool RTSPos, bool useDTR, bool DTRPos); - bool isopen() {return (com_handle != COM_HANDLE_INVALID);} - void closeport(); - void ptt(bool tx); - - private: - com_handle_t com_handle; - bool m_useRTS, m_RTSPos, m_useDTR, m_DTRPos; - - void raiseDTR(void); - void lowerDTR(void); - void raiseRTS(void); - void lowerRTS(void); -}; - -#endif /* SERIALPORT_H */ diff --git a/freedv/tags/1.2.2/src/sox/band.h b/freedv/tags/1.2.2/src/sox/band.h deleted file mode 100644 index 5398ff45..00000000 --- a/freedv/tags/1.2.2/src/sox/band.h +++ /dev/null @@ -1,47 +0,0 @@ -/* libSoX Bandpass effect file. July 5, 1991 - * Copyright 1991 Lance Norskog And Sundry Contributors - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Lance Norskog And Sundry Contributors are not responsible for - * the consequences of using this software. - * - * Algorithm: 2nd order recursive filter. - * Formula stolen from MUSIC56K, a toolkit of 56000 assembler stuff. - * Quote: - * This is a 2nd order recursive band pass filter of the form. - * y(n)= a * x(n) - b * y(n-1) - c * y(n-2) - * where : - * x(n) = "IN" - * "OUT" = y(n) - * c = EXP(-2*pi*cBW/S_RATE) - * b = -4*c/(1+c)*COS(2*pi*cCF/S_RATE) - * if cSCL=2 (i.e. noise input) - * a = SQT(((1+c)*(1+c)-b*b)*(1-c)/(1+c)) - * else - * a = SQT(1-b*b/(4*c))*(1-c) - * endif - * note : cCF is the center frequency in Hertz - * cBW is the band width in Hertz - * cSCL is a scale factor, use 1 for pitched sounds - * use 2 for noise. - * - * - * July 1, 1999 - Jan Paul Schmidt - * - * This looks like the resonator band pass in SPKit. It's a - * second order all-pole (IIR) band-pass filter described - * at the pages 186 - 189 in - * Dodge, Charles & Jerse, Thomas A. 1985: - * Computer Music -- Synthesis, Composition and Performance. - * New York: Schirmer Books. - * Reference from the SPKit manual. - */ - - p->a2 = exp(-2 * M_PI * bw_Hz / effp->in_signal.rate); - p->a1 = -4 * p->a2 / (1 + p->a2) * cos(2 * M_PI * p->fc / effp->in_signal.rate); - p->b0 = sqrt(1 - p->a1 * p->a1 / (4 * p->a2)) * (1 - p->a2); - if (p->filter_type == filter_BPF_SPK_N) { - mult = sqrt(((1+p->a2) * (1+p->a2) - p->a1*p->a1) * (1-p->a2) / (1+p->a2)) / p->b0; - p->b0 *= mult; - } diff --git a/freedv/tags/1.2.2/src/sox/biquad.c b/freedv/tags/1.2.2/src/sox/biquad.c deleted file mode 100644 index c57f1902..00000000 --- a/freedv/tags/1.2.2/src/sox/biquad.c +++ /dev/null @@ -1,178 +0,0 @@ -/* libSoX Biquad filter common functions (c) 2006-7 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "biquad.h" -#include - -typedef biquad_t priv_t; - -static char const * const width_str[] = { - "band-width(Hz)", - "band-width(kHz)", - "band-width(Hz, no warp)", /* deprecated */ - "band-width(octaves)", - "Q", - "slope", -}; -static char const all_width_types[] = "hkboqs"; - - -int lsx_biquad_getopts(sox_effect_t * effp, int argc, char **argv, - int min_args, int max_args, int fc_pos, int width_pos, int gain_pos, - char const * allowed_width_types, filter_t filter_type) -{ - priv_t * p = (priv_t *)effp->priv; - char width_type = *allowed_width_types; - char dummy, * dummy_p; /* To check for extraneous chars. */ - --argc, ++argv; - - p->filter_type = filter_type; - if (argc < min_args || argc > max_args || - (argc > fc_pos && ((p->fc = lsx_parse_frequency(argv[fc_pos], &dummy_p)) <= 0 || *dummy_p)) || - (argc > width_pos && ((unsigned)(sscanf(argv[width_pos], "%lf%c %c", &p->width, &width_type, &dummy)-1) > 1 || p->width <= 0)) || - (argc > gain_pos && sscanf(argv[gain_pos], "%lf %c", &p->gain, &dummy) != 1) || - !strchr(allowed_width_types, width_type) || (width_type == 's' && p->width > 1)) - return lsx_usage(effp); - p->width_type = strchr(all_width_types, width_type) - all_width_types; - if ((size_t)p->width_type >= strlen(all_width_types)) - p->width_type = 0; - if (p->width_type == width_bw_kHz) { - p->width *= 1000; - p->width_type = width_bw_Hz; - } - return SOX_SUCCESS; -} - - -static int start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - /* Simplify: */ - p->b2 /= p->a0; - p->b1 /= p->a0; - p->b0 /= p->a0; - p->a2 /= p->a0; - p->a1 /= p->a0; - - p->o2 = p->o1 = p->i2 = p->i1 = 0; - return SOX_SUCCESS; -} - - -int lsx_biquad_start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - - start(effp); - - if (effp->global_info->plot == sox_plot_octave) { - printf( - "%% GNU Octave file (may also work with MATLAB(R) )\n" - "Fs=%g;minF=10;maxF=Fs/2;\n" - "sweepF=logspace(log10(minF),log10(maxF),200);\n" - "[h,w]=freqz([%.15e %.15e %.15e],[1 %.15e %.15e],sweepF,Fs);\n" - "semilogx(w,20*log10(h))\n" - "title('SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)')\n" - "xlabel('Frequency (Hz)')\n" - "ylabel('Amplitude Response (dB)')\n" - "axis([minF maxF -35 25])\n" - "grid on\n" - "disp('Hit return to continue')\n" - "pause\n" - , effp->in_signal.rate, p->b0, p->b1, p->b2, p->a1, p->a2 - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate); - return SOX_EOF; - } - if (effp->global_info->plot == sox_plot_gnuplot) { - printf( - "# gnuplot file\n" - "set title 'SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)'\n" - "set xlabel 'Frequency (Hz)'\n" - "set ylabel 'Amplitude Response (dB)'\n" - "Fs=%g\n" - "b0=%.15e; b1=%.15e; b2=%.15e; a1=%.15e; a2=%.15e\n" - "o=2*pi/Fs\n" - "H(f)=sqrt((b0*b0+b1*b1+b2*b2+2.*(b0*b1+b1*b2)*cos(f*o)+2.*(b0*b2)*cos(2.*f*o))/(1.+a1*a1+a2*a2+2.*(a1+a1*a2)*cos(f*o)+2.*a2*cos(2.*f*o)))\n" - "set logscale x\n" - "set samples 250\n" - "set grid xtics ytics\n" - "set key off\n" - "plot [f=10:Fs/2] [-35:25] 20*log10(H(f))\n" - "pause -1 'Hit return to continue'\n" - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate, effp->in_signal.rate - , p->b0, p->b1, p->b2, p->a1, p->a2); - return SOX_EOF; - } - if (effp->global_info->plot == sox_plot_data) { - printf("# SoX effect: %s gain=%g frequency=%g %s=%g (rate=%g)\n" - "# IIR filter\n" - "# rate: %g\n" - "# name: b\n" - "# type: matrix\n" - "# rows: 3\n" - "# columns: 1\n" - "%24.16e\n%24.16e\n%24.16e\n" - "# name: a\n" - "# type: matrix\n" - "# rows: 3\n" - "# columns: 1\n" - "%24.16e\n%24.16e\n%24.16e\n" - , effp->handler.name, p->gain, p->fc, width_str[p->width_type], p->width - , effp->in_signal.rate, effp->in_signal.rate - , p->b0, p->b1, p->b2, 1. /* a0 */, p->a1, p->a2); - return SOX_EOF; - } - return SOX_SUCCESS; -} - - -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, - sox_sample_t *obuf, size_t *isamp, size_t *osamp) -{ - priv_t * p = (priv_t *)effp->priv; - size_t len = *isamp = *osamp = min(*isamp, *osamp); - while (len--) { - double o0 = *ibuf*p->b0 + p->i1*p->b1 + p->i2*p->b2 - p->o1*p->a1 - p->o2*p->a2; - p->i2 = p->i1, p->i1 = *ibuf++; - p->o2 = p->o1, p->o1 = o0; - *obuf++ = SOX_ROUND_CLIP_COUNT(o0, effp->clips); - } - return SOX_SUCCESS; -} - -static int create(sox_effect_t * effp, int argc, char * * argv) -{ - priv_t * p = (priv_t *)effp->priv; - double * d = &p->b0; - char c; - - --argc, ++argv; - if (argc == 6) - for (; argc && sscanf(*argv, "%lf%c", d, &c) == 1; --argc, ++argv, ++d); - return argc? lsx_usage(effp) : SOX_SUCCESS; -} - -sox_effect_handler_t const * lsx_biquad_effect_fn(void) -{ - static sox_effect_handler_t handler = { - "biquad", "b0 b1 b2 a0 a1 a2", 0, - create, lsx_biquad_start, lsx_biquad_flow, NULL, NULL, NULL, sizeof(priv_t) - }; - return &handler; -} diff --git a/freedv/tags/1.2.2/src/sox/biquad.h b/freedv/tags/1.2.2/src/sox/biquad.h deleted file mode 100644 index 8786ac83..00000000 --- a/freedv/tags/1.2.2/src/sox/biquad.h +++ /dev/null @@ -1,78 +0,0 @@ -/* libSoX Biquad filter common definitions (c) 2006-7 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef biquad_included -#define biquad_included - -#define LSX_EFF_ALIAS -#include "sox_i.h" - -typedef enum { - filter_LPF, - filter_HPF, - filter_BPF_CSG, - filter_BPF, - filter_notch, - filter_APF, - filter_peakingEQ, - filter_lowShelf, - filter_highShelf, - filter_LPF_1, - filter_HPF_1, - filter_BPF_SPK, - filter_BPF_SPK_N, - filter_AP1, - filter_AP2, - filter_deemph, - filter_riaa -} filter_t; - -typedef enum { - width_bw_Hz, - width_bw_kHz, - /* The old, non-RBJ, non-freq-warped band-pass/reject response; - * leaving here for now just in case anybody misses it: */ - width_bw_old, - width_bw_oct, - width_Q, - width_slope -} width_t; - -/* Private data for the biquad filter effects */ -typedef struct { - double gain; /* For EQ filters */ - double fc; /* Centre/corner/cutoff frequency */ - double width; /* Filter width; interpreted as per width_type */ - width_t width_type; - - filter_t filter_type; - - double b0, b1, b2; /* Filter coefficients */ - double a0, a1, a2; /* Filter coefficients */ - - sox_sample_t i1, i2; /* Filter memory */ - double o1, o2; /* Filter memory */ -} biquad_t; - -int lsx_biquad_getopts(sox_effect_t * effp, int n, char **argv, - int min_args, int max_args, int fc_pos, int width_pos, int gain_pos, - char const * allowed_width_types, filter_t filter_type); -int lsx_biquad_start(sox_effect_t * effp); -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf, - size_t *isamp, size_t *osamp); - -#endif diff --git a/freedv/tags/1.2.2/src/sox/biquads.c b/freedv/tags/1.2.2/src/sox/biquads.c deleted file mode 100644 index 19793a6d..00000000 --- a/freedv/tags/1.2.2/src/sox/biquads.c +++ /dev/null @@ -1,400 +0,0 @@ -/* libSoX Biquad filter effects (c) 2006-8 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * - * 2-pole filters designed by Robert Bristow-Johnson - * see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt - * - * 1-pole filters based on code (c) 2000 Chris Bagwell - * Algorithms: Recursive single pole low/high pass filter - * Reference: The Scientist and Engineer's Guide to Digital Signal Processing - * - * low-pass: output[N] = input[N] * A + output[N-1] * B - * X = exp(-2.0 * pi * Fc) - * A = 1 - X - * B = X - * Fc = cutoff freq / sample rate - * - * Mimics an RC low-pass filter: - * - * ---/\/\/\/\-----------> - * | - * --- C - * --- - * | - * | - * V - * - * high-pass: output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1] - * X = exp(-2.0 * pi * Fc) - * A0 = (1 + X) / 2 - * A1 = -(1 + X) / 2 - * B1 = X - * Fc = cutoff freq / sample rate - * - * Mimics an RC high-pass filter: - * - * || C - * ----||---------> - * || | - * < - * > R - * < - * | - * V - */ - - -#include "biquad.h" -#include -#include - -typedef biquad_t priv_t; - - -static int hilo1_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 1, 1, 0, 1, 2, "", - *effp->handler.name == 'l'? filter_LPF_1 : filter_HPF_1); -} - - -static int hilo2_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - if (argc > 1 && strcmp(argv[1], "-1") == 0) - return hilo1_getopts(effp, argc - 1, argv + 1); - if (argc > 1 && strcmp(argv[1], "-2") == 0) - ++argv, --argc; - p->width = sqrt(0.5); /* Default to Butterworth */ - return lsx_biquad_getopts(effp, argc, argv, 1, 2, 0, 1, 2, "qohk", - *effp->handler.name == 'l'? filter_LPF : filter_HPF); -} - - -static int bandpass_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_BPF; - if (argc > 1 && strcmp(argv[1], "-c") == 0) - ++argv, --argc, type = filter_BPF_CSG; - return lsx_biquad_getopts(effp, argc, argv, 2, 2, 0, 1, 2, "hkqob", type); -} - - -static int bandrej_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 2, 2, 0, 1, 2, "hkqob", filter_notch); -} - - -static int allpass_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_APF; - int m; - if (argc > 1 && strcmp(argv[1], "-1") == 0) - ++argv, --argc, type = filter_AP1; - else if (argc > 1 && strcmp(argv[1], "-2") == 0) - ++argv, --argc, type = filter_AP2; - m = 1 + (type == filter_APF); - return lsx_biquad_getopts(effp, argc, argv, m, m, 0, 1, 2, "hkqo", type); -} - - -static int tone_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->width = 0.5; - p->fc = *effp->handler.name == 'b'? 100 : 3000; - return lsx_biquad_getopts(effp, argc, argv, 1, 3, 1, 2, 0, "shkqo", - *effp->handler.name == 'b'? filter_lowShelf: filter_highShelf); -} - - -static int equalizer_getopts(sox_effect_t * effp, int argc, char **argv) { - return lsx_biquad_getopts(effp, argc, argv, 3, 3, 0, 1, 2, "qohk", filter_peakingEQ); -} - - -static int band_getopts(sox_effect_t * effp, int argc, char **argv) { - filter_t type = filter_BPF_SPK; - if (argc > 1 && strcmp(argv[1], "-n") == 0) - ++argv, --argc, type = filter_BPF_SPK_N; - return lsx_biquad_getopts(effp, argc, argv, 1, 2, 0, 1, 2, "hkqo", type); -} - - -static int deemph_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->fc = 5283; - p->width = 0.4845; - p->gain = -9.477; - return lsx_biquad_getopts(effp, argc, argv, 0, 0, 0, 1, 2, "s", filter_deemph); -} - - -static int riaa_getopts(sox_effect_t * effp, int argc, char **argv) { - priv_t * p = (priv_t *)effp->priv; - p->filter_type = filter_riaa; - (void)argv; - return --argc? lsx_usage(effp) : SOX_SUCCESS; -} - - -static void make_poly_from_roots( - double const * roots, size_t num_roots, double * poly) -{ - size_t i, j; - poly[0] = 1; - poly[1] = -roots[0]; - memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly)); - for (i = 1; i < num_roots; ++i) - for (j = num_roots; j > 0; --j) - poly[j] -= poly[j - 1] * roots[i]; -} - -static int start(sox_effect_t * effp) -{ - priv_t * p = (priv_t *)effp->priv; - double w0 = 2 * M_PI * p->fc / effp->in_signal.rate; - double A = exp(p->gain / 40 * log(10.)); - double alpha = 0, mult = dB_to_linear(max(p->gain, 0)); - - if (w0 > M_PI) { - lsx_fail("frequency must be less than half the sample-rate (Nyquist rate)"); - return SOX_EOF; - } - - /* Set defaults: */ - p->b0 = p->b1 = p->b2 = p->a1 = p->a2 = 0; - p->a0 = 1; - - if (p->width) switch (p->width_type) { - case width_slope: - alpha = sin(w0)/2 * sqrt((A + 1/A)*(1/p->width - 1) + 2); - break; - - case width_Q: - alpha = sin(w0)/(2*p->width); - break; - - case width_bw_oct: - alpha = sin(w0)*sinh(log(2.)/2 * p->width * w0/sin(w0)); - break; - - case width_bw_Hz: - alpha = sin(w0)/(2*p->fc/p->width); - break; - - case width_bw_kHz: assert(0); /* Shouldn't get here */ - - case width_bw_old: - alpha = tan(M_PI * p->width / effp->in_signal.rate); - break; - } - switch (p->filter_type) { - case filter_LPF: /* H(s) = 1 / (s^2 + s/Q + 1) */ - p->b0 = (1 - cos(w0))/2; - p->b1 = 1 - cos(w0); - p->b2 = (1 - cos(w0))/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_HPF: /* H(s) = s^2 / (s^2 + s/Q + 1) */ - p->b0 = (1 + cos(w0))/2; - p->b1 = -(1 + cos(w0)); - p->b2 = (1 + cos(w0))/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_BPF_CSG: /* H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q) */ - p->b0 = sin(w0)/2; - p->b1 = 0; - p->b2 = -sin(w0)/2; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_BPF: /* H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain) */ - p->b0 = alpha; - p->b1 = 0; - p->b2 = -alpha; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_notch: /* H(s) = (s^2 + 1) / (s^2 + s/Q + 1) */ - p->b0 = 1; - p->b1 = -2*cos(w0); - p->b2 = 1; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_APF: /* H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1) */ - p->b0 = 1 - alpha; - p->b1 = -2*cos(w0); - p->b2 = 1 + alpha; - p->a0 = 1 + alpha; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha; - break; - - case filter_peakingEQ: /* H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1) */ - if (A == 1) - return SOX_EFF_NULL; - p->b0 = 1 + alpha*A; - p->b1 = -2*cos(w0); - p->b2 = 1 - alpha*A; - p->a0 = 1 + alpha/A; - p->a1 = -2*cos(w0); - p->a2 = 1 - alpha/A; - break; - - case filter_lowShelf: /* H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1) */ - if (A == 1) - return SOX_EFF_NULL; - p->b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha ); - p->b1 = 2*A*( (A-1) - (A+1)*cos(w0) ); - p->b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha ); - p->a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha; - p->a1 = -2*( (A-1) + (A+1)*cos(w0) ); - p->a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha; - break; - - case filter_deemph: /* See deemph.plt for documentation */ - if (effp->in_signal.rate != 44100) { - lsx_fail("Sample rate must be 44100 (audio-CD)"); - return SOX_EOF; - } - /* Falls through... */ - - case filter_highShelf: /* H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A) */ - if (!A) - return SOX_EFF_NULL; - p->b0 = A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha ); - p->b1 = -2*A*( (A-1) + (A+1)*cos(w0) ); - p->b2 = A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha ); - p->a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha; - p->a1 = 2*( (A-1) - (A+1)*cos(w0) ); - p->a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha; - break; - - case filter_LPF_1: /* single-pole */ - p->a1 = -exp(-w0); - p->b0 = 1 + p->a1; - break; - - case filter_HPF_1: /* single-pole */ - p->a1 = -exp(-w0); - p->b0 = (1 - p->a1)/2; - p->b1 = -p->b0; - break; - - case filter_BPF_SPK: case filter_BPF_SPK_N: { - double bw_Hz; - if (!p->width) - p->width = p->fc / 2; - bw_Hz = p->width_type == width_Q? p->fc / p->width : - p->width_type == width_bw_Hz? p->width : - p->fc * (pow(2., p->width) - 1) * pow(2., -0.5 * p->width); /* bw_oct */ - #include "band.h" /* Has different licence */ - break; - } - - case filter_AP1: /* Experimental 1-pole all-pass from Tom Erbe @ UCSD */ - p->b0 = exp(-w0); - p->b1 = -1; - p->a1 = -exp(-w0); - break; - - case filter_AP2: /* Experimental 2-pole all-pass from Tom Erbe @ UCSD */ - p->b0 = 1 - sin(w0); - p->b1 = -2 * cos(w0); - p->b2 = 1 + sin(w0); - p->a0 = 1 + sin(w0); - p->a1 = -2 * cos(w0); - p->a2 = 1 - sin(w0); - break; - - case filter_riaa: /* http://www.dsprelated.com/showmessage/73300/3.php */ - if (effp->in_signal.rate == 44100) { - static const double zeros[] = {-0.2014898, 0.9233820}; - static const double poles[] = {0.7083149, 0.9924091}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 48000) { - static const double zeros[] = {-0.1766069, 0.9321590}; - static const double poles[] = {0.7396325, 0.9931330}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 88200) { - static const double zeros[] = {-0.1168735, 0.9648312}; - static const double poles[] = {0.8590646, 0.9964002}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else if (effp->in_signal.rate == 96000) { - static const double zeros[] = {-0.1141486, 0.9676817}; - static const double poles[] = {0.8699137, 0.9966946}; - make_poly_from_roots(zeros, (size_t)2, &p->b0); - make_poly_from_roots(poles, (size_t)2, &p->a0); - } - else { - lsx_fail("Sample rate must be 44.1k, 48k, 88.2k, or 96k"); - return SOX_EOF; - } - { /* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */ - double y = 2 * M_PI * 1000 / effp->in_signal.rate; - double b_re = p->b0 + p->b1 * cos(-y) + p->b2 * cos(-2 * y); - double a_re = p->a0 + p->a1 * cos(-y) + p->a2 * cos(-2 * y); - double b_im = p->b1 * sin(-y) + p->b2 * sin(-2 * y); - double a_im = p->a1 * sin(-y) + p->a2 * sin(-2 * y); - double g = 1 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im))); - p->b0 *= g; p->b1 *= g; p->b2 *= g; - } - mult = (p->b0 + p->b1 + p->b2) / (p->a0 + p->a1 + p->a2); - lsx_debug("gain=%f", linear_to_dB(mult)); - break; - } - if (effp->in_signal.mult) - *effp->in_signal.mult /= mult; - return lsx_biquad_start(effp); -} - - -#define BIQUAD_EFFECT(name,group,usage,flags) \ -sox_effect_handler_t const * lsx_##name##_effect_fn(void) { \ - static sox_effect_handler_t handler = { \ - #name, usage, flags, \ - group##_getopts, start, lsx_biquad_flow, 0, 0, 0, sizeof(biquad_t)\ - }; \ - return &handler; \ -} - -BIQUAD_EFFECT(highpass, hilo2, "[-1|-2] frequency [width[q|o|h|k](0.707q)]", 0) -BIQUAD_EFFECT(lowpass, hilo2, "[-1|-2] frequency [width[q|o|h|k]](0.707q)", 0) -BIQUAD_EFFECT(bandpass, bandpass, "[-c] frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(bandreject,bandrej, "frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(allpass, allpass, "frequency width[h|k|q|o]", 0) -BIQUAD_EFFECT(bass, tone, "gain [frequency(100) [width[s|h|k|q|o]](0.5s)]", 0) -BIQUAD_EFFECT(treble, tone, "gain [frequency(3000) [width[s|h|k|q|o]](0.5s)]", 0) -BIQUAD_EFFECT(equalizer, equalizer,"frequency width[q|o|h|k] gain", 0) -BIQUAD_EFFECT(band, band, "[-n] center [width[h|k|q|o]]", 0) -BIQUAD_EFFECT(deemph, deemph, NULL, 0) -BIQUAD_EFFECT(riaa, riaa, NULL, 0) diff --git a/freedv/tags/1.2.2/src/sox/effects.c b/freedv/tags/1.2.2/src/sox/effects.c deleted file mode 100644 index 435412fa..00000000 --- a/freedv/tags/1.2.2/src/sox/effects.c +++ /dev/null @@ -1,544 +0,0 @@ -/* SoX Effects chain (c) 2007 robs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define LSX_EFF_ALIAS -#include "sox_i.h" -#include -#include -#ifdef HAVE_STRINGS_H - #include -#endif - -#define DEBUG_EFFECTS_CHAIN 0 - -/* Default effect handler functions for do-nothing situations: */ - -static int default_function(sox_effect_t * effp UNUSED) -{ - return SOX_SUCCESS; -} - -/* Pass through samples verbatim */ -int lsx_flow_copy(sox_effect_t * effp UNUSED, const sox_sample_t * ibuf, - sox_sample_t * obuf, size_t * isamp, size_t * osamp) -{ - *isamp = *osamp = min(*isamp, *osamp); - memcpy(obuf, ibuf, *isamp * sizeof(*obuf)); - return SOX_SUCCESS; -} - -/* Inform no more samples to drain */ -static int default_drain(sox_effect_t * effp UNUSED, sox_sample_t *obuf UNUSED, size_t *osamp) -{ - *osamp = 0; - return SOX_EOF; -} - -/* Check that no parameters have been given */ -static int default_getopts(sox_effect_t * effp, int argc, char **argv UNUSED) -{ - return --argc? lsx_usage(effp) : SOX_SUCCESS; -} - -/* Partially initialise the effect structure; signal info will come later */ -sox_effect_t * sox_create_effect(sox_effect_handler_t const * eh) -{ - sox_effect_t * effp = lsx_calloc(1, sizeof(*effp)); - effp->obuf = NULL; - - effp->global_info = sox_get_effects_globals(); - effp->handler = *eh; - if (!effp->handler.getopts) effp->handler.getopts = default_getopts; - if (!effp->handler.start ) effp->handler.start = default_function; - if (!effp->handler.flow ) effp->handler.flow = lsx_flow_copy; - if (!effp->handler.drain ) effp->handler.drain = default_drain; - if (!effp->handler.stop ) effp->handler.stop = default_function; - if (!effp->handler.kill ) effp->handler.kill = default_function; - - effp->priv = lsx_calloc(1, effp->handler.priv_size); - - return effp; -} /* sox_create_effect */ - -int sox_effect_options(sox_effect_t *effp, int argc, char * const argv[]) -{ - int result; - - char * * argv2 = lsx_malloc((argc + 1) * sizeof(*argv2)); - argv2[0] = (char *)effp->handler.name; - memcpy(argv2 + 1, argv, argc * sizeof(*argv2)); - result = effp->handler.getopts(effp, argc + 1, argv2); - free(argv2); - return result; -} /* sox_effect_options */ - -/* Effects chain: */ - -sox_effects_chain_t * sox_create_effects_chain( - sox_encodinginfo_t const * in_enc, sox_encodinginfo_t const * out_enc) -{ - sox_effects_chain_t * result = lsx_calloc(1, sizeof(sox_effects_chain_t)); - result->global_info = *sox_get_effects_globals(); - result->in_enc = in_enc; - result->out_enc = out_enc; - return result; -} /* sox_create_effects_chain */ - -void sox_delete_effects_chain(sox_effects_chain_t *ecp) -{ - if (ecp && ecp->length) - sox_delete_effects(ecp); - free(ecp->effects); - free(ecp); -} /* sox_delete_effects_chain */ - -/* Effect can call in start() or flow() to set minimum input size to flow() */ -int lsx_effect_set_imin(sox_effect_t * effp, size_t imin) -{ - if (imin > sox_globals.bufsiz / effp->flows) { - lsx_fail("sox_bufsiz not big enough"); - return SOX_EOF; - } - - effp->imin = imin; - return SOX_SUCCESS; -} - -/* Effects table to be extended in steps of EFF_TABLE_STEP */ -#define EFF_TABLE_STEP 8 - -/* Add an effect to the chain. *in is the input signal for this effect. *out is - * a suggestion as to what the output signal should be, but depending on its - * given options and *in, the effect can choose to do differently. Whatever - * output rate and channels the effect does produce are written back to *in, - * ready for the next effect in the chain. - */ -int sox_add_effect(sox_effects_chain_t * chain, sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t const * out) -{ - int ret, (*start)(sox_effect_t * effp) = effp->handler.start; - unsigned f; - sox_effect_t eff0; /* Copy of effect for flow 0 before calling start */ - - effp->global_info = &chain->global_info; - effp->in_signal = *in; - effp->out_signal = *out; - effp->in_encoding = chain->in_enc; - effp->out_encoding = chain->out_enc; - if (!(effp->handler.flags & SOX_EFF_CHAN)) - effp->out_signal.channels = in->channels; - if (!(effp->handler.flags & SOX_EFF_RATE)) - effp->out_signal.rate = in->rate; - if (!(effp->handler.flags & SOX_EFF_PREC)) - effp->out_signal.precision = (effp->handler.flags & SOX_EFF_MODIFY)? - in->precision : SOX_SAMPLE_PRECISION; - if (!(effp->handler.flags & SOX_EFF_GAIN)) - effp->out_signal.mult = in->mult; - - effp->flows = - (effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->in_signal.channels; - effp->clips = 0; - effp->imin = 0; - eff0 = *effp, eff0.priv = lsx_memdup(eff0.priv, eff0.handler.priv_size); - eff0.in_signal.mult = NULL; /* Only used in channel 0 */ - ret = start(effp); - if (ret == SOX_EFF_NULL) { - lsx_report("has no effect in this configuration"); - free(eff0.priv); - free(effp->priv); - effp->priv = NULL; - return SOX_SUCCESS; - } - if (ret != SOX_SUCCESS) { - free(eff0.priv); - return SOX_EOF; - } - if (in->mult) - lsx_debug("mult=%g", *in->mult); - - if (!(effp->handler.flags & SOX_EFF_LENGTH)) { - effp->out_signal.length = in->length; - if (effp->out_signal.length != SOX_UNKNOWN_LEN) { - if (effp->handler.flags & SOX_EFF_CHAN) - effp->out_signal.length = - effp->out_signal.length / in->channels * effp->out_signal.channels; - if (effp->handler.flags & SOX_EFF_RATE) - effp->out_signal.length = - effp->out_signal.length / in->rate * effp->out_signal.rate + .5; - } - } - - *in = effp->out_signal; - - if (chain->length == chain->table_size) { - chain->table_size += EFF_TABLE_STEP; - lsx_debug_more("sox_add_effect: extending effects table, " - "new size = %lu", (unsigned long)chain->table_size); - lsx_revalloc(chain->effects, chain->table_size); - } - - chain->effects[chain->length] = - lsx_calloc(effp->flows, sizeof(chain->effects[chain->length][0])); - chain->effects[chain->length][0] = *effp; - - for (f = 1; f < effp->flows; ++f) { - chain->effects[chain->length][f] = eff0; - chain->effects[chain->length][f].flow = f; - chain->effects[chain->length][f].priv = lsx_memdup(eff0.priv, eff0.handler.priv_size); - if (start(&chain->effects[chain->length][f]) != SOX_SUCCESS) { - free(eff0.priv); - return SOX_EOF; - } - } - - ++chain->length; - free(eff0.priv); - return SOX_SUCCESS; -} - -static int flow_effect(sox_effects_chain_t * chain, size_t n) -{ - sox_effect_t * effp1 = &chain->effects[n - 1][0]; - sox_effect_t * effp = &chain->effects[n][0]; - int effstatus = SOX_SUCCESS, f = 0; - size_t i; - const sox_sample_t *ibuf; - size_t idone = effp1->oend - effp1->obeg; - size_t obeg = sox_globals.bufsiz - effp->oend; -#if DEBUG_EFFECTS_CHAIN - size_t pre_idone = idone; - size_t pre_odone = obeg; -#endif - - if (effp->flows == 1) { /* Run effect on all channels at once */ - idone -= idone % effp->in_signal.channels; - effstatus = effp->handler.flow(effp, &effp1->obuf[effp1->obeg], - &effp->obuf[effp->oend], &idone, &obeg); - if (obeg % effp->out_signal.channels != 0) { - lsx_fail("multi-channel effect flowed asymmetrically!"); - effstatus = SOX_EOF; - } - } else { /* Run effect on each channel individually */ - sox_sample_t *obuf = &effp->obuf[effp->oend]; - size_t idone_last = 0, odone_last = 0; /* Initialised to prevent warning */ - - ibuf = &effp1->obuf[effp1->obeg]; - for (i = 0; i < idone; i += effp->flows) - for (f = 0; f < (int)effp->flows; ++f) - chain->ibufc[f][i / effp->flows] = *ibuf++; - -#ifdef HAVE_OPENMP - if (sox_globals.use_threads && effp->flows > 1) - { - #pragma omp parallel for - for (f = 0; f < (int)effp->flows; ++f) { - size_t idonec = idone / effp->flows; - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.flow(&chain->effects[n][f], - chain->ibufc[f], chain->obufc[f], &idonec, &odonec); - if (!f) { - idone_last = idonec; - odone_last = odonec; - } - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - } - else /* sox_globals.use_threads */ -#endif - { - for (f = 0; f < (int)effp->flows; ++f) { - size_t idonec = idone / effp->flows; - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.flow(&chain->effects[n][f], - chain->ibufc[f], chain->obufc[f], &idonec, &odonec); - if (f && (idonec != idone_last || odonec != odone_last)) { - lsx_fail("flowed asymmetrically!"); - effstatus = SOX_EOF; - } - idone_last = idonec; - odone_last = odonec; - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - } - - for (i = 0; i < odone_last; ++i) - for (f = 0; f < (int)effp->flows; ++f) - *obuf++ = chain->obufc[f][i]; - - idone = effp->flows * idone_last; - obeg = effp->flows * odone_last; - } -#if DEBUG_EFFECTS_CHAIN - lsx_report("flow: %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR, - pre_idone, pre_odone, idone, obeg); -#endif - effp1->obeg += idone; - if (effp1->obeg == effp1->oend) - effp1->obeg = effp1->oend = 0; - else if (effp1->oend - effp1->obeg < effp->imin ) { /* Need to refill? */ - memmove(effp1->obuf, &effp1->obuf[effp1->obeg], (effp1->oend - effp1->obeg) * sizeof(*effp1->obuf)); - effp1->oend -= effp1->obeg; - effp1->obeg = 0; - } - - effp->oend += obeg; - - return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF; -} - -/* The same as flow_effect but with no input */ -static int drain_effect(sox_effects_chain_t * chain, size_t n) -{ - sox_effect_t * effp = &chain->effects[n][0]; - int effstatus = SOX_SUCCESS; - size_t i, f; - size_t obeg = sox_globals.bufsiz - effp->oend; -#if DEBUG_EFFECTS_CHAIN - size_t pre_odone = obeg; -#endif - - if (effp->flows == 1) { /* Run effect on all channels at once */ - effstatus = effp->handler.drain(effp, &effp->obuf[effp->oend], &obeg); - if (obeg % effp->out_signal.channels != 0) { - lsx_fail("multi-channel effect drained asymmetrically!"); - effstatus = SOX_EOF; - } - } else { /* Run effect on each channel individually */ - sox_sample_t *obuf = &effp->obuf[effp->oend]; - size_t odone_last = 0; /* Initialised to prevent warning */ - - for (f = 0; f < effp->flows; ++f) { - size_t odonec = obeg / effp->flows; - int eff_status_c = effp->handler.drain(&chain->effects[n][f], chain->obufc[f], &odonec); - if (f && (odonec != odone_last)) { - lsx_fail("drained asymmetrically!"); - effstatus = SOX_EOF; - } - odone_last = odonec; - - if (eff_status_c != SOX_SUCCESS) - effstatus = SOX_EOF; - } - - for (i = 0; i < odone_last; ++i) - for (f = 0; f < effp->flows; ++f) - *obuf++ = chain->obufc[f][i]; - obeg = f * odone_last; - } -#if DEBUG_EFFECTS_CHAIN - lsx_report("drain: %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR, - (size_t)0, pre_odone, (size_t)0, obeg); -#endif - if (!obeg) /* This is the only thing that drain has and flow hasn't */ - effstatus = SOX_EOF; - - effp->oend += obeg; - - return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF; -} - -/* Flow data through the effects chain until an effect or callback gives EOF */ -int sox_flow_effects(sox_effects_chain_t * chain, int (* callback)(sox_bool all_done, void * client_data), void * client_data) -{ - int flow_status = SOX_SUCCESS; - size_t e, source_e = 0; /* effect indices */ - size_t f, max_flows = 0; - sox_bool draining = sox_true; - - for (e = 0; e < chain->length; ++e) { - chain->effects[e][0].obuf = lsx_realloc(chain->effects[e][0].obuf, - sox_globals.bufsiz * sizeof(chain->effects[e][0].obuf[0])); - /* Possibly there is already a buffer, if this is a used effect; - it may still contain samples in that case. */ - /* Memory will be freed by sox_delete_effect() later. */ - max_flows = max(max_flows, chain->effects[e][0].flows); - } - if (max_flows == 1) /* don't need interleave buffers */ - max_flows = 0; - chain->ibufc = lsx_calloc(max_flows, sizeof(*chain->ibufc)); - chain->obufc = lsx_calloc(max_flows, sizeof(*chain->obufc)); - for (f = 0; f < max_flows; ++f) { - chain->ibufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->ibufc[f][0])); - chain->obufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->obufc[f][0])); - } - - e = chain->length - 1; - while (source_e < chain->length) { -#define have_imin (e > 0 && e < chain->length && chain->effects[e - 1][0].oend - chain->effects[e - 1][0].obeg >= chain->effects[e][0].imin) - size_t osize = chain->effects[e][0].oend - chain->effects[e][0].obeg; - if (e == source_e && (draining || !have_imin)) { - if (drain_effect(chain, e) == SOX_EOF) { - ++source_e; - draining = sox_false; - } - } else if (have_imin && flow_effect(chain, e) == SOX_EOF) { - flow_status = SOX_EOF; - if (e == chain->length - 1) - break; - source_e = e; - draining = sox_true; - } - if (e < chain->length && chain->effects[e][0].oend - chain->effects[e][0].obeg > osize) /* False for output */ - ++e; - else if (e == source_e) - draining = sox_true; - else if ((int)--e < (int)source_e) - e = source_e; - - if (callback && callback(source_e == chain->length, client_data) != SOX_SUCCESS) { - flow_status = SOX_EOF; /* Client has requested to stop the flow. */ - break; - } - } - - for (f = 0; f < max_flows; ++f) { - free(chain->ibufc[f]); - free(chain->obufc[f]); - } - free(chain->obufc); - free(chain->ibufc); - - return flow_status; -} - -sox_uint64_t sox_effects_clips(sox_effects_chain_t * chain) -{ - unsigned i, f; - uint64_t clips = 0; - for (i = 1; i < chain->length - 1; ++i) - for (f = 0; f < chain->effects[i][0].flows; ++f) - clips += chain->effects[i][f].clips; - return clips; -} - -sox_uint64_t sox_stop_effect(sox_effect_t *effp) -{ - unsigned f; - uint64_t clips = 0; - - for (f = 0; f < effp->flows; ++f) { - effp[f].handler.stop(&effp[f]); - clips += effp[f].clips; - } - return clips; -} - -void sox_push_effect_last(sox_effects_chain_t *chain, sox_effect_t *effp) -{ - if (chain->length == chain->table_size) { - chain->table_size += EFF_TABLE_STEP; - lsx_debug_more("sox_push_effect_last: extending effects table, " - "new size = %lu", (unsigned long)chain->table_size); - lsx_revalloc(chain->effects, chain->table_size); - } - - chain->effects[chain->length++] = effp; -} /* sox_push_effect_last */ - -sox_effect_t *sox_pop_effect_last(sox_effects_chain_t *chain) -{ - if (chain->length > 0) - { - sox_effect_t *effp; - chain->length--; - effp = chain->effects[chain->length]; - chain->effects[chain->length] = NULL; - return effp; - } - else - return NULL; -} /* sox_pop_effect_last */ - -/* Free resources related to effect. - * Note: This currently closes down the effect which might - * not be obvious from name. - */ -void sox_delete_effect(sox_effect_t *effp) -{ - uint64_t clips; - unsigned f; - - if ((clips = sox_stop_effect(effp)) != 0) - lsx_warn("%s clipped %" PRIu64 " samples; decrease volume?", - effp->handler.name, clips); - if (effp->obeg != effp->oend) - lsx_debug("output buffer still held %" PRIuPTR " samples; dropped.", - (effp->oend - effp->obeg)/effp->out_signal.channels); - /* May or may not indicate a problem; it is normal if the user aborted - processing, or if an effect like "trim" stopped early. */ - effp->handler.kill(effp); /* N.B. only one kill; not one per flow */ - for (f = 0; f < effp->flows; ++f) - free(effp[f].priv); - free(effp->obuf); - free(effp); -} - -void sox_delete_effect_last(sox_effects_chain_t *chain) -{ - if (chain->length > 0) - { - chain->length--; - sox_delete_effect(chain->effects[chain->length]); - chain->effects[chain->length] = NULL; - } -} /* sox_delete_effect_last */ - -/* Remove all effects from the chain. - * Note: This currently closes down the effect which might - * not be obvious from name. - */ -void sox_delete_effects(sox_effects_chain_t * chain) -{ - size_t e; - - for (e = 0; e < chain->length; ++e) { - sox_delete_effect(chain->effects[e]); - chain->effects[e] = NULL; - } - chain->length = 0; -} - -/*----------------------------- Effects library ------------------------------*/ - -static sox_effect_fn_t s_sox_effect_fns[] = { -#define EFFECT(f) lsx_##f##_effect_fn, -#include "effects.h" -#undef EFFECT - NULL -}; - -const sox_effect_fn_t* -sox_get_effect_fns(void) -{ - return s_sox_effect_fns; -} - -/* Find a named effect in the effects library */ -sox_effect_handler_t const * sox_find_effect(char const * name) -{ - int e; - sox_effect_fn_t const * fns = sox_get_effect_fns(); - for (e = 0; fns[e]; ++e) { - const sox_effect_handler_t *eh = fns[e] (); - if (eh && eh->name && strcasecmp(eh->name, name) == 0) - return eh; /* Found it. */ - } - return NULL; -} diff --git a/freedv/tags/1.2.2/src/sox/effects.h b/freedv/tags/1.2.2/src/sox/effects.h deleted file mode 100644 index 8d7025c8..00000000 --- a/freedv/tags/1.2.2/src/sox/effects.h +++ /dev/null @@ -1,22 +0,0 @@ -/* This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* Manually edited for FreeDV to contain just the effects we need */ - - EFFECT(bass) - EFFECT(highpass) - EFFECT(treble) - EFFECT(equalizer) - diff --git a/freedv/tags/1.2.2/src/sox/effects_i.c b/freedv/tags/1.2.2/src/sox/effects_i.c deleted file mode 100644 index e5770a94..00000000 --- a/freedv/tags/1.2.2/src/sox/effects_i.c +++ /dev/null @@ -1,379 +0,0 @@ -/* Implements a libSoX internal interface for implementing effects. - * All public functions & data are prefixed with lsx_ . - * - * Copyright (c) 2005-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define LSX_EFF_ALIAS -#include "sox_i.h" -#include -#include - -int lsx_usage(sox_effect_t * effp) -{ - if (effp->handler.usage) - lsx_fail("usage: %s", effp->handler.usage); - else - lsx_fail("this effect takes no parameters"); - return SOX_EOF; -} - -char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n) -{ - if (!*usage) { - size_t i, len; - for (len = i = 0; i < n; len += strlen(lines[i++]) + 1); - *usage = lsx_malloc(len); /* FIXME: this memory will never be freed */ - strcpy(*usage, lines[0]); - for (i = 1; i < n; ++i) { - strcat(*usage, "\n"); - strcat(*usage, lines[i]); - } - } - return *usage; -} - -static lsx_enum_item const s_lsx_wave_enum[] = { - LSX_ENUM_ITEM(SOX_WAVE_,SINE) - LSX_ENUM_ITEM(SOX_WAVE_,TRIANGLE) - {0, 0}}; - -lsx_enum_item const * lsx_get_wave_enum(void) -{ - return s_lsx_wave_enum; -} - -void lsx_generate_wave_table( - lsx_wave_t wave_type, - sox_data_t data_type, - void *table, - size_t table_size, - double min, - double max, - double phase) -{ - uint32_t t; - uint32_t phase_offset = phase / M_PI / 2 * table_size + 0.5; - - for (t = 0; t < table_size; t++) - { - uint32_t point = (t + phase_offset) % table_size; - double d; - switch (wave_type) - { - case SOX_WAVE_SINE: - d = (sin((double)point / table_size * 2 * M_PI) + 1) / 2; - break; - - case SOX_WAVE_TRIANGLE: - d = (double)point * 2 / table_size; - switch (4 * point / table_size) - { - case 0: d = d + 0.5; break; - case 1: case 2: d = 1.5 - d; break; - case 3: d = d - 1.5; break; - } - break; - - default: /* Oops! FIXME */ - d = 0.0; /* Make sure we have a value */ - break; - } - d = d * (max - min) + min; - switch (data_type) - { - case SOX_FLOAT: - { - float *fp = (float *)table; - *fp++ = (float)d; - table = fp; - continue; - } - case SOX_DOUBLE: - { - double *dp = (double *)table; - *dp++ = d; - table = dp; - continue; - } - default: break; - } - d += d < 0? -0.5 : +0.5; - switch (data_type) - { - case SOX_SHORT: - { - short *sp = table; - *sp++ = (short)d; - table = sp; - continue; - } - case SOX_INT: - { - int *ip = table; - *ip++ = (int)d; - table = ip; - continue; - } - default: break; - } - } -} - -/* - * lsx_parsesamples - * - * Parse a string for # of samples. If string ends with a 's' - * then the string is interpreted as a user calculated # of samples. - * If string contains ':' or '.' or if it ends with a 't' then its - * treated as an amount of time. This is converted into seconds and - * fraction of seconds and then use the sample rate to calculate - * # of samples. - * Returns NULL on error, pointer to next char to parse otherwise. - */ -char const * lsx_parsesamples(sox_rate_t rate, const char *str0, uint64_t *samples, int def) -{ - int i, found_samples = 0, found_time = 0; - char const * end; - char const * pos; - sox_bool found_colon, found_dot; - char * str = (char *)str0; - - for (;*str == ' '; ++str); - for (end = str; *end && strchr("0123456789:.ets", *end); ++end); - if (end == str) - return NULL; - - pos = strchr(str, ':'); - found_colon = pos && pos < end; - - pos = strchr(str, '.'); - found_dot = pos && pos < end; - - if (found_colon || found_dot || *(end-1) == 't') - found_time = 1; - else if (*(end-1) == 's') - found_samples = 1; - - if (found_time || (def == 't' && !found_samples)) { - for (*samples = 0, i = 0; *str != '.' && i < 3; ++i) { - char * last_str = str; - long part = strtol(str, &str, 10); - if (!i && str == last_str) - return NULL; - *samples += rate * part; - if (i < 2) { - if (*str != ':') - break; - ++str; - *samples *= 60; - } - } - if (*str == '.') { - char * last_str = str; - double part = strtod(str, &str); - if (str == last_str) - return NULL; - *samples += rate * part + .5; - } - return *str == 't'? str + 1 : str; - } - { - char * last_str = str; - double part = strtod(str, &str); - if (str == last_str) - return NULL; - *samples = part + .5; - return *str == 's'? str + 1 : str; - } -} - -#if 0 - -#include - -#define TEST(st, samp, len) \ - str = st; \ - next = lsx_parsesamples(10000, str, &samples, 't'); \ - assert(samples == samp && next == str + len); - -int main(int argc, char * * argv) -{ - char const * str, * next; - uint64_t samples; - - TEST("0" , 0, 1) - TEST("1" , 10000, 1) - - TEST("0s" , 0, 2) - TEST("0s,", 0, 2) - TEST("0s/", 0, 2) - TEST("0s@", 0, 2) - - TEST("0t" , 0, 2) - TEST("0t,", 0, 2) - TEST("0t/", 0, 2) - TEST("0t@", 0, 2) - - TEST("1s" , 1, 2) - TEST("1s,", 1, 2) - TEST("1s/", 1, 2) - TEST("1s@", 1, 2) - TEST(" 01s" , 1, 4) - TEST("1e6s" , 1000000, 4) - - TEST("1t" , 10000, 2) - TEST("1t,", 10000, 2) - TEST("1t/", 10000, 2) - TEST("1t@", 10000, 2) - TEST("1.1t" , 11000, 4) - TEST("1.1t,", 11000, 4) - TEST("1.1t/", 11000, 4) - TEST("1.1t@", 11000, 4) - TEST("1e6t" , 10000, 1) - - TEST(".0", 0, 2) - TEST("0.0", 0, 3) - TEST("0:0.0", 0, 5) - TEST("0:0:0.0", 0, 7) - - TEST(".1", 1000, 2) - TEST(".10", 1000, 3) - TEST("0.1", 1000, 3) - TEST("1.1", 11000, 3) - TEST("1:1.1", 611000, 5) - TEST("1:1:1.1", 36611000, 7) - TEST("1:1", 610000, 3) - TEST("1:01", 610000, 4) - TEST("1:1:1", 36610000, 5) - TEST("1:", 600000, 2) - TEST("1::", 36000000, 3) - - TEST("0.444444", 4444, 8) - TEST("0.555555", 5556, 8) - - assert(!lsx_parsesamples(10000, "x", &samples, 't')); - return 0; -} -#endif - -/* a note is given as an int, - * 0 => 440 Hz = A - * >0 => number of half notes 'up', - * <0 => number of half notes down, - * example 12 => A of next octave, 880Hz - * - * calculated by freq = 440Hz * 2**(note/12) - */ -static double calc_note_freq(double note, int key) -{ - if (key != INT_MAX) { /* Just intonation. */ - static const int n[] = {16, 9, 6, 5, 4, 7}; /* Numerator. */ - static const int d[] = {15, 8, 5, 4, 3, 5}; /* Denominator. */ - static double j[13]; /* Just semitones */ - int i, m = floor(note); - - if (!j[1]) for (i = 1; i <= 12; ++i) - j[i] = i <= 6? log((double)n[i - 1] / d[i - 1]) / log(2.) : 1 - j[12 - i]; - note -= m; - m -= key = m - ((INT_MAX / 2 - ((INT_MAX / 2) % 12) + m - key) % 12); - return 440 * pow(2., key / 12. + j[m] + (j[m + 1] - j[m]) * note); - } - return 440 * pow(2., note / 12); -} - -int lsx_parse_note(char const * text, char * * end_ptr) -{ - int result = INT_MAX; - - if (*text >= 'A' && *text <= 'G') { - result = (int)(5/3. * (*text++ - 'A') + 9.5) % 12 - 9; - if (*text == 'b') {--result; ++text;} - else if (*text == '#') {++result; ++text;} - if (isdigit((unsigned char)*text)) - result += 12 * (*text++ - '4'); - } - *end_ptr = (char *)text; - return result; -} - -/* Read string 'text' and convert to frequency. - * 'text' can be a positive number which is the frequency in Hz. - * If 'text' starts with a '%' and a following number the corresponding - * note is calculated. - * Return -1 on error. - */ -double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key) -{ - double result; - - if (*text == '%') { - result = strtod(text + 1, end_ptr); - if (*end_ptr == text + 1) - return -1; - return calc_note_freq(result, key); - } - if (*text >= 'A' && *text <= 'G') { - int result2 = lsx_parse_note(text, end_ptr); - return result2 == INT_MAX? - 1 : calc_note_freq((double)result2, key); - } - result = strtod(text, end_ptr); - if (end_ptr) { - if (*end_ptr == text) - return -1; - if (**end_ptr == 'k') { - result *= 1000; - ++*end_ptr; - } - } - return result < 0 ? -1 : result; -} - -FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename) -{ - FILE * file; - - if (!filename || !strcmp(filename, "-")) { - if (effp->global_info->global_info->stdin_in_use_by) { - lsx_fail("stdin already in use by `%s'", effp->global_info->global_info->stdin_in_use_by); - return NULL; - } - effp->global_info->global_info->stdin_in_use_by = effp->handler.name; - file = stdin; - } - else if (!(file = fopen(filename, "r"))) { - lsx_fail("couldn't open file %s: %s", filename, strerror(errno)); - return NULL; - } - return file; -} - -int lsx_effects_init(void) -{ - #ifndef __FREEDV__ - init_fft_cache(); - #endif - return SOX_SUCCESS; -} - -int lsx_effects_quit(void) -{ - #ifndef __FREEDV__ - clear_fft_cache(); - #endif - return SOX_SUCCESS; -} diff --git a/freedv/tags/1.2.2/src/sox/formats_i.c b/freedv/tags/1.2.2/src/sox/formats_i.c deleted file mode 100644 index 17c40615..00000000 --- a/freedv/tags/1.2.2/src/sox/formats_i.c +++ /dev/null @@ -1,487 +0,0 @@ -/* Implements a libSoX internal interface for use in implementing file formats. - * All public functions & data are prefixed with lsx_ . - * - * (c) 2005-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include -#include -#include - -void lsx_fail_errno(sox_format_t * ft, int sox_errno, const char *fmt, ...) -{ - va_list args; - - ft->sox_errno = sox_errno; - - va_start(args, fmt); -#ifdef HAVE_VSNPRINTF - vsnprintf(ft->sox_errstr, sizeof(ft->sox_errstr), fmt, args); -#else - vsprintf(ft->sox_errstr, fmt, args); -#endif - va_end(args); - ft->sox_errstr[255] = '\0'; -} - -void lsx_set_signal_defaults(sox_format_t * ft) -{ - if (!ft->signal.rate ) ft->signal.rate = SOX_DEFAULT_RATE; - if (!ft->signal.precision) ft->signal.precision = SOX_DEFAULT_PRECISION; - if (!ft->signal.channels ) ft->signal.channels = SOX_DEFAULT_CHANNELS; - - if (!ft->encoding.bits_per_sample) - ft->encoding.bits_per_sample = ft->signal.precision; - if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN) - ft->encoding.encoding = SOX_ENCODING_SIGN2; -} - -#ifndef __FREEDV__ -int lsx_check_read_params(sox_format_t * ft, unsigned channels, - sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, - uint64_t num_samples, sox_bool check_length) -{ - ft->signal.length = ft->signal.length == SOX_IGNORE_LENGTH? SOX_UNSPEC : num_samples; - - if (ft->seekable) - ft->data_start = lsx_tell(ft); - - if (channels && ft->signal.channels && ft->signal.channels != channels) - lsx_warn("`%s': overriding number of channels", ft->filename); - else ft->signal.channels = channels; - - if (rate && ft->signal.rate && ft->signal.rate != rate) - lsx_warn("`%s': overriding sample rate", ft->filename); - else ft->signal.rate = rate; - - if (encoding && ft->encoding.encoding && ft->encoding.encoding != encoding) - lsx_warn("`%s': overriding encoding type", ft->filename); - else ft->encoding.encoding = encoding; - - if (bits_per_sample && ft->encoding.bits_per_sample && ft->encoding.bits_per_sample != bits_per_sample) - lsx_warn("`%s': overriding encoding size", ft->filename); - ft->encoding.bits_per_sample = bits_per_sample; - - if (check_length && ft->encoding.bits_per_sample && lsx_filelength(ft)) { - uint64_t calculated_length = div_bits(lsx_filelength(ft) - ft->data_start, ft->encoding.bits_per_sample); - if (!ft->signal.length) - ft->signal.length = calculated_length; - else if (num_samples != calculated_length) - lsx_warn("`%s': file header gives the total number of samples as %" PRIu64 " but file length indicates the number is in fact %" PRIu64, ft->filename, num_samples, calculated_length); - } - - if (sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample)) - return SOX_SUCCESS; - lsx_fail_errno(ft, EINVAL, "invalid format for this file type"); - return SOX_EOF; -} -#endif - -/* Read in a buffer of data of length len bytes. - * Returns number of bytes read. - */ -size_t lsx_readbuf(sox_format_t * ft, void *buf, size_t len) -{ - size_t ret = fread(buf, (size_t) 1, len, (FILE*)ft->fp); - if (ret != len && ferror((FILE*)ft->fp)) - lsx_fail_errno(ft, errno, "lsx_readbuf"); - ft->tell_off += ret; - return ret; -} - -/* Skip input without seeking. */ -int lsx_skipbytes(sox_format_t * ft, size_t n) -{ - unsigned char trash; - - while (n--) - if (lsx_readb(ft, &trash) == SOX_EOF) - return (SOX_EOF); - - return (SOX_SUCCESS); -} - -/* Pad output. */ -int lsx_padbytes(sox_format_t * ft, size_t n) -{ - while (n--) - if (lsx_writeb(ft, '\0') == SOX_EOF) - return (SOX_EOF); - - return (SOX_SUCCESS); -} - -/* Write a buffer of data of length bytes. - * Returns number of bytes written. - */ -size_t lsx_writebuf(sox_format_t * ft, void const * buf, size_t len) -{ - size_t ret = fwrite(buf, (size_t) 1, len, (FILE*)ft->fp); - if (ret != len) { - lsx_fail_errno(ft, errno, "error writing output file"); - clearerr((FILE*)ft->fp); /* Allows us to seek back to write header */ - } - ft->tell_off += ret; - return ret; -} - -uint64_t lsx_filelength(sox_format_t * ft) -{ - struct stat st; - int ret = fstat(fileno((FILE*)ft->fp), &st); - - return (!ret && (st.st_mode & S_IFREG))? (uint64_t)st.st_size : 0; -} - -int lsx_flush(sox_format_t * ft) -{ - return fflush((FILE*)ft->fp); -} - -off_t lsx_tell(sox_format_t * ft) -{ - return ft->seekable? (off_t)ftello((FILE*)ft->fp) : (off_t)ft->tell_off; -} - -int lsx_eof(sox_format_t * ft) -{ - return feof((FILE*)ft->fp); -} - -int lsx_error(sox_format_t * ft) -{ - return ferror((FILE*)ft->fp); -} - -void lsx_rewind(sox_format_t * ft) -{ - rewind((FILE*)ft->fp); - ft->tell_off = 0; -} - -void lsx_clearerr(sox_format_t * ft) -{ - clearerr((FILE*)ft->fp); - ft->sox_errno = 0; -} - -int lsx_unreadb(sox_format_t * ft, unsigned b) -{ - return ungetc((int)b, ft->fp); -} - -/* Implements traditional fseek() behavior. Meant to abstract out - * file operations so that they could one day also work on memory - * buffers. - * - * N.B. Can only seek forwards on non-seekable streams! - */ -int lsx_seeki(sox_format_t * ft, off_t offset, int whence) -{ - if (ft->seekable == 0) { - /* If a stream peel off chars else EPERM */ - if (whence == SEEK_CUR) { - while (offset > 0 && !feof((FILE*)ft->fp)) { - getc((FILE*)ft->fp); - offset--; - ++ft->tell_off; - } - if (offset) - lsx_fail_errno(ft,SOX_EOF, "offset past EOF"); - else - ft->sox_errno = SOX_SUCCESS; - } else - lsx_fail_errno(ft,SOX_EPERM, "file not seekable"); - } else { - if (fseeko((FILE*)ft->fp, offset, whence) == -1) - lsx_fail_errno(ft,errno, "%s", strerror(errno)); - else - ft->sox_errno = SOX_SUCCESS; - } - return ft->sox_errno; -} - -int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, off_t to_sample) -{ - double wide_sample = to_sample - (to_sample % ft->signal.channels); - double to_d = wide_sample * ft->encoding.bits_per_sample / 8; - off_t to = to_d; - return (to != to_d)? SOX_EOF : lsx_seeki(ft, (byte_offset + to), SEEK_SET); -} - -/* Read and write known datatypes in "machine format". Swap if indicated. - * They all return SOX_EOF on error and SOX_SUCCESS on success. - */ -/* Read n-char string (and possibly null-terminating). - * Stop reading and null-terminate string if either a 0 or \n is reached. - */ -int lsx_reads(sox_format_t * ft, char *c, size_t len) -{ - char *sc; - char in; - - sc = c; - do - { - if (lsx_readbuf(ft, &in, (size_t)1) != 1) - { - *sc = 0; - return (SOX_EOF); - } - if (in == 0 || in == '\n') - break; - - *sc = in; - sc++; - } while (sc - c < (ptrdiff_t)len); - *sc = 0; - return(SOX_SUCCESS); -} - -/* Write null-terminated string (without \0). */ -int lsx_writes(sox_format_t * ft, char const * c) -{ - if (lsx_writebuf(ft, c, strlen(c)) != strlen(c)) - return(SOX_EOF); - return(SOX_SUCCESS); -} - -/* return swapped 32-bit float */ -static void lsx_swapf(float * f) -{ - union { - uint32_t dw; - float f; - } u; - - u.f= *f; - u.dw= (u.dw>>24) | ((u.dw>>8)&0xff00) | ((u.dw<<8)&0xff0000) | (u.dw<<24); - *f = u.f; -} - -static void swap(void * data, size_t len) -{ - uint8_t * bytes = (uint8_t *)data; - size_t i; - - for (i = 0; i < len / 2; ++i) { - char tmp = bytes[i]; - bytes[i] = bytes[len - 1 - i]; - bytes[len - 1 - i] = tmp; - } -} - -static double lsx_swapdf(double data) -{ - swap(&data, sizeof(data)); - return data; -} - -static uint64_t lsx_swapqw(uint64_t data) -{ - swap(&data, sizeof(data)); - return data; -} - -/* Lookup table to reverse the bit order of a byte. ie MSB become LSB */ -static uint8_t const cswap[256] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, - 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, - 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, - 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, - 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, - 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, - 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, - 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, - 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, - 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, - 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, - 0x3F, 0xBF, 0x7F, 0xFF -}; - -/* Utilities to byte-swap values, use libc optimized macros if possible */ -#define TWIDDLE_BYTE(ub, type) \ - do { \ - if (ft->encoding.reverse_bits) \ - ub = cswap[ub]; \ - if (ft->encoding.reverse_nibbles) \ - ub = ((ub & 15) << 4) | (ub >> 4); \ - } while (0); - -#define TWIDDLE_WORD(uw, type) \ - if (ft->encoding.reverse_bytes) \ - uw = lsx_swap ## type(uw); - -#define TWIDDLE_FLOAT(f, type) \ - if (ft->encoding.reverse_bytes) \ - lsx_swapf(&f); - -/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte - types). */ -#define READ_FUNC(type, size, ctype, twiddle) \ - size_t lsx_read_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nread; \ - nread = lsx_readbuf(ft, buf, len * size) / size; \ - for (n = 0; n < nread; n++) \ - twiddle(buf[n], type); \ - return nread; \ - } - -/* Unpack a 3-byte value from a uint8_t * */ -#define sox_unpack3(p) (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN? \ - ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16)) : \ - ((p)[2] | ((p)[1] << 8) | ((p)[0] << 16))) - -/* This (slower) macro works for unaligned types (e.g. 3-byte types) - that need to be unpacked. */ -#define READ_FUNC_UNPACK(type, size, ctype, twiddle) \ - size_t lsx_read_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nread; \ - uint8_t *data = lsx_malloc(size * len); \ - nread = lsx_readbuf(ft, data, len * size) / size; \ - for (n = 0; n < nread; n++) \ - buf[n] = sox_unpack ## size(data + n * size); \ - free(data); \ - return n; \ - } - -READ_FUNC(b, 1, uint8_t, TWIDDLE_BYTE) -READ_FUNC(w, 2, uint16_t, TWIDDLE_WORD) -READ_FUNC_UNPACK(3, 3, sox_uint24_t, TWIDDLE_WORD) -READ_FUNC(dw, 4, uint32_t, TWIDDLE_WORD) -READ_FUNC(qw, 8, uint64_t, TWIDDLE_WORD) -READ_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT) -READ_FUNC(df, sizeof(double), double, TWIDDLE_WORD) - -#define READ1_FUNC(type, ctype) \ -int lsx_read ## type(sox_format_t * ft, ctype * datum) { \ - if (lsx_read_ ## type ## _buf(ft, datum, (size_t)1) == 1) \ - return SOX_SUCCESS; \ - if (!lsx_error(ft)) \ - lsx_fail_errno(ft, errno, premature_eof); \ - return SOX_EOF; \ -} - -static char const premature_eof[] = "premature EOF"; - -READ1_FUNC(b, uint8_t) -READ1_FUNC(w, uint16_t) -READ1_FUNC(3, sox_uint24_t) -READ1_FUNC(dw, uint32_t) -READ1_FUNC(qw, uint64_t) -READ1_FUNC(f, float) -READ1_FUNC(df, double) - -int lsx_readchars(sox_format_t * ft, char * chars, size_t len) -{ - size_t ret = lsx_readbuf(ft, chars, len); - if (ret == len) - return SOX_SUCCESS; - if (!lsx_error(ft)) - lsx_fail_errno(ft, errno, premature_eof); - return SOX_EOF; -} - -/* N.B. This macro doesn't work for unaligned types (e.g. 3-byte - types). */ -#define WRITE_FUNC(type, size, ctype, twiddle) \ - size_t lsx_write_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nwritten; \ - for (n = 0; n < len; n++) \ - twiddle(buf[n], type); \ - nwritten = lsx_writebuf(ft, buf, len * size); \ - return nwritten / size; \ - } - -/* Pack a 3-byte value to a uint8_t * */ -#define sox_pack3(p, v) do {if (ft->encoding.reverse_bytes == MACHINE_IS_BIGENDIAN)\ -{(p)[0] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[2] = (v >> 16) & 0xff;} else \ -{(p)[2] = v & 0xff; (p)[1] = (v >> 8) & 0xff; (p)[0] = (v >> 16) & 0xff;} \ -} while (0) - -/* This (slower) macro works for unaligned types (e.g. 3-byte types) - that need to be packed. */ -#define WRITE_FUNC_PACK(type, size, ctype, twiddle) \ - size_t lsx_write_ ## type ## _buf( \ - sox_format_t * ft, ctype *buf, size_t len) \ - { \ - size_t n, nwritten; \ - uint8_t *data = lsx_malloc(size * len); \ - for (n = 0; n < len; n++) \ - sox_pack ## size(data + n * size, buf[n]); \ - nwritten = lsx_writebuf(ft, data, len * size); \ - free(data); \ - return nwritten / size; \ - } - -WRITE_FUNC(b, 1, uint8_t, TWIDDLE_BYTE) -WRITE_FUNC(w, 2, uint16_t, TWIDDLE_WORD) -WRITE_FUNC_PACK(3, 3, sox_uint24_t, TWIDDLE_WORD) -WRITE_FUNC(dw, 4, uint32_t, TWIDDLE_WORD) -WRITE_FUNC(qw, 8, uint64_t, TWIDDLE_WORD) -WRITE_FUNC(f, sizeof(float), float, TWIDDLE_FLOAT) -WRITE_FUNC(df, sizeof(double), double, TWIDDLE_WORD) - -#define WRITE1U_FUNC(type, ctype) \ - int lsx_write ## type(sox_format_t * ft, unsigned d) \ - { ctype datum = (ctype)d; \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -#define WRITE1S_FUNC(type, ctype) \ - int lsx_writes ## type(sox_format_t * ft, signed d) \ - { ctype datum = (ctype)d; \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -#define WRITE1_FUNC(type, ctype) \ - int lsx_write ## type(sox_format_t * ft, ctype datum) \ - { \ - return lsx_write_ ## type ## _buf(ft, &datum, (size_t)1) == 1 ? SOX_SUCCESS : SOX_EOF; \ - } - -WRITE1U_FUNC(b, uint8_t) -WRITE1U_FUNC(w, uint16_t) -WRITE1U_FUNC(3, sox_uint24_t) -WRITE1U_FUNC(dw, uint32_t) -WRITE1_FUNC(qw, uint64_t) -WRITE1S_FUNC(b, uint8_t) -WRITE1S_FUNC(w, uint16_t) -WRITE1_FUNC(df, double) - -int lsx_writef(sox_format_t * ft, double datum) -{ - float f = datum; - return lsx_write_f_buf(ft, &f, (size_t) 1) == 1 ? SOX_SUCCESS : SOX_EOF; -} diff --git a/freedv/tags/1.2.2/src/sox/libsox.c b/freedv/tags/1.2.2/src/sox/libsox.c deleted file mode 100644 index 43620250..00000000 --- a/freedv/tags/1.2.2/src/sox/libsox.c +++ /dev/null @@ -1,225 +0,0 @@ -/* Implements the public API for libSoX general functions - * All public functions & data are prefixed with sox_ . - * - * (c) 2006-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include - -const char *sox_version(void) -{ - static char versionstr[20]; - - sprintf(versionstr, "%d.%d.%d", - (SOX_LIB_VERSION_CODE & 0xff0000) >> 16, - (SOX_LIB_VERSION_CODE & 0x00ff00) >> 8, - (SOX_LIB_VERSION_CODE & 0x0000ff)); - return(versionstr); -} - -sox_version_info_t const * sox_version_info(void) -{ -#define STRINGIZE1(x) #x -#define STRINGIZE(x) STRINGIZE1(x) - static char arch[30]; - static sox_version_info_t info = { - /* size */ - sizeof(sox_version_info_t), - /* flags */ - (sox_version_flags_t)( -#if HAVE_POPEN - sox_version_have_popen + -#endif -#if HAVE_MAGIC - sox_version_have_magic + -#endif -#if HAVE_OPENMP - sox_version_have_threads + -#endif -#ifdef HAVE_FMEMOPEN - sox_version_have_memopen + -#endif - sox_version_none), - /* version_code */ - SOX_LIB_VERSION_CODE, - /* version */ - NULL, - /* sox_version_extra */ -#ifdef PACKAGE_EXTRA - PACKAGE_EXTRA, -#else - NULL, -#endif - /* sox_time */ - __DATE__ " " __TIME__, - /* sox_distro */ -#ifdef DISTRO - DISTRO, -#else - NULL, -#endif - /* sox_compiler */ -#if defined __GNUC__ - "gcc " __VERSION__, -#elif defined _MSC_VER - "msvc " STRINGIZE(_MSC_FULL_VER), -#elif defined __SUNPRO_C - fprintf(file, "sun c " STRINGIZE(__SUNPRO_C), -#else - NULL, -#endif - /* sox_arch */ - NULL - }; - - if (!info.version) - { - info.version = sox_version(); - } - - if (!info.arch) - { - snprintf(arch, sizeof(arch), - "%" PRIuPTR "%" PRIuPTR "%" PRIuPTR "%" PRIuPTR - " %" PRIuPTR "%" PRIuPTR " %" PRIuPTR "%" PRIuPTR " %c %s", - sizeof(char), sizeof(short), sizeof(long), sizeof(off_t), - sizeof(float), sizeof(double), sizeof(int *), sizeof(int (*)(void)), - MACHINE_IS_BIGENDIAN ? 'B' : 'L', - (info.flags & sox_version_have_threads) ? "OMP" : ""); - arch[sizeof(arch) - 1] = 0; - info.arch = arch; - } - - return &info; -} - -/* Default routine to output messages; can be overridden */ -static void output_message( - unsigned level, const char *filename, const char *fmt, va_list ap) -{ - if (sox_globals.verbosity >= level) { - char base_name[128]; - sox_basename(base_name, sizeof(base_name), filename); - fprintf(stderr, "%s: ", base_name); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - } -} - -static sox_globals_t s_sox_globals = { - 2, /* unsigned verbosity */ - output_message, /* sox_output_message_handler */ - sox_false, /* sox_bool repeatable */ - 8192, /* size_t bufsiz */ - 0, /* size_t input_bufsiz */ - 0, /* int32_t ranqd1 */ - NULL, /* char const * stdin_in_use_by */ - NULL, /* char const * stdout_in_use_by */ - NULL, /* char const * subsystem */ - NULL, /* char * tmp_path */ - sox_false, /* sox_bool use_magic */ - sox_false /* sox_bool use_threads */ -}; - -sox_globals_t * sox_get_globals(void) -{ - return &s_sox_globals; -} - -/* FIXME: Not thread safe using globals */ -static sox_effects_globals_t s_sox_effects_globals = - {sox_plot_off, &s_sox_globals}; - -sox_effects_globals_t * -sox_get_effects_globals(void) -{ - return &s_sox_effects_globals; -} - -char const * sox_strerror(int sox_errno) -{ - static char const * const errors[] = { - "Invalid Audio Header", - "Unsupported data format", - "Can't allocate memory", - "Operation not permitted", - "Operation not supported", - "Invalid argument", - }; - if (sox_errno < SOX_EHDR) - return strerror(sox_errno); - sox_errno -= SOX_EHDR; - if (sox_errno < 0 || (size_t)sox_errno >= array_length(errors)) - return "Unknown error"; - return errors[sox_errno]; -} - -size_t sox_basename(char * base_buffer, size_t base_buffer_len, const char * filename) -{ - if (!base_buffer || !base_buffer_len) - { - return 0; - } - else - { - char const * slash_pos = LAST_SLASH(filename); - char const * base_name = slash_pos ? slash_pos + 1 : filename; - char const * dot_pos = strrchr(base_name, '.'); - size_t i, len; - dot_pos = dot_pos ? dot_pos : base_name + strlen(base_name); - len = dot_pos - base_name; - len = min(len, base_buffer_len - 1); - for (i = 0; i < len; i++) - { - base_buffer[i] = base_name[i]; - } - base_buffer[i] = 0; - return i; - } -} - -#define SOX_MESSAGE_FUNCTION(name,level) \ -void name(char const * fmt, ...) { \ - va_list ap; \ - va_start(ap, fmt); \ - if (sox_globals.output_message_handler) \ - (*sox_globals.output_message_handler)(level,sox_globals.subsystem,fmt,ap); \ - va_end(ap); \ -} - -SOX_MESSAGE_FUNCTION(lsx_fail_impl , 1) -SOX_MESSAGE_FUNCTION(lsx_warn_impl , 2) -SOX_MESSAGE_FUNCTION(lsx_report_impl, 3) -SOX_MESSAGE_FUNCTION(lsx_debug_impl , 4) -SOX_MESSAGE_FUNCTION(lsx_debug_more_impl , 5) -SOX_MESSAGE_FUNCTION(lsx_debug_most_impl , 6) - -#undef SOX_MESSAGE_FUNCTION - -int sox_init(void) -{ - return lsx_effects_init(); -} - -int sox_quit(void) -{ - #ifndef __FREEDV__ - sox_format_quit(); - #endif - return lsx_effects_quit(); -} diff --git a/freedv/tags/1.2.2/src/sox/sox.h b/freedv/tags/1.2.2/src/sox/sox.h deleted file mode 100644 index 05372558..00000000 --- a/freedv/tags/1.2.2/src/sox/sox.h +++ /dev/null @@ -1,2608 +0,0 @@ -/* libSoX Library Public Interface - * - * Copyright 1999-2011 Chris Bagwell and SoX Contributors. - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Chris Bagwell And SoX Contributors are not responsible for - * the consequences of using this software. - */ - -/** @file -Contains the interface exposed to clients of the libSoX library. -Symbols starting with "sox_" or "SOX_" are part of the public interface for -libSoX clients (applications that consume libSoX). Symbols starting with -"lsx_" or "LSX_" are internal use by libSoX and plugins. -LSX_ and lsx_ symbols should not be used by libSoX-based applications. -*/ - -#ifndef SOX_H -#define SOX_H /**< Client API: This macro is defined if sox.h has been included. */ - -#include -#include -#include - -#if defined(__cplusplus) -extern "C" { -#endif - -/* Suppress warnings from use of type long long. */ -#if defined __GNUC__ -#pragma GCC system_header -#endif - -/***************************************************************************** -API decoration macros: -Mostly for documentation purposes. For some compilers, decorations also affect -code generation, influence compiler warnings or activate compiler -optimizations. -*****************************************************************************/ - -/** -Plugins API: -Attribute required on all functions exported by libSoX and on all function -pointer types used by the libSoX API. -*/ -#ifdef __GNUC__ -#define LSX_API __attribute__ ((cdecl)) /* libSoX function */ -#elif _MSC_VER -#define LSX_API __cdecl /* libSoX function */ -#else -#define LSX_API /* libSoX function */ -#endif - -/** -Plugins API: -Attribute applied to a parameter or local variable to suppress warnings about -the variable being unused (especially in macro-generated code). -*/ -#ifdef __GNUC__ -#define LSX_UNUSED __attribute__ ((unused)) /* Parameter or local variable is intentionally unused. */ -#else -#define LSX_UNUSED /* Parameter or local variable is intentionally unused. */ -#endif - -/** -Plugins API: -LSX_PRINTF12: Attribute applied to a function to indicate that it requires -a printf-style format string for arg1 and that printf parameters start at -arg2. -*/ -#ifdef __GNUC__ -#define LSX_PRINTF12 __attribute__ ((format (printf, 1, 2))) /* Function has printf-style arguments. */ -#else -#define LSX_PRINTF12 /* Function has printf-style arguments. */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that it has no side effects and -depends only its input parameters and global memory. If called repeatedly, it -returns the same result each time. -*/ -#ifdef __GNUC__ -#define LSX_RETURN_PURE __attribute__ ((pure)) /* Function is pure. */ -#else -#define LSX_RETURN_PURE /* Function is pure. */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the -return value is always a pointer to a valid object (never NULL). -*/ -#ifdef _Ret_ -#define LSX_RETURN_VALID _Ret_ /* Function always returns a valid object (never NULL). */ -#else -#define LSX_RETURN_VALID /* Function always returns a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the return value is always a -pointer to a valid array (never NULL). -*/ -#ifdef _Ret_valid_ -#define LSX_RETURN_ARRAY _Ret_valid_ /* Function always returns a valid array (never NULL). */ -#else -#define LSX_RETURN_ARRAY /* Function always returns a valid array (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the return value is always a -pointer to a valid 0-terminated array (never NULL). -*/ -#ifdef _Ret_z_ -#define LSX_RETURN_VALID_Z _Ret_z_ /* Function always returns a 0-terminated array (never NULL). */ -#else -#define LSX_RETURN_VALID_Z /* Function always returns a 0-terminated array (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a function to indicate that the returned pointer may be -null. -*/ -#ifdef _Ret_opt_ -#define LSX_RETURN_OPT _Ret_opt_ /* Function may return NULL. */ -#else -#define LSX_RETURN_OPT /* Function may return NULL. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to one const element of the pointed-to type (never NULL). -*/ -#ifdef _In_ -#define LSX_PARAM_IN _In_ /* Required const pointer to a valid object (never NULL). */ -#else -#define LSX_PARAM_IN /* Required const pointer to a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to a const 0-terminated string (never NULL). -*/ -#ifdef _In_z_ -#define LSX_PARAM_IN_Z _In_z_ /* Required const pointer to 0-terminated string (never NULL). */ -#else -#define LSX_PARAM_IN_Z /* Required const pointer to 0-terminated string (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a const -pointer to a 0-terminated printf format string. -*/ -#ifdef _Printf_format_string_ -#define LSX_PARAM_IN_PRINTF _Printf_format_string_ /* Required const pointer to 0-terminated printf format string (never NULL). */ -#else -#define LSX_PARAM_IN_PRINTF /* Required const pointer to 0-terminated printf format string (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) const initialized elements of the pointed-to type, where -(len) is the name of another parameter. -@param len The parameter that contains the number of elements in the array. -*/ -#ifdef _In_count_ -#define LSX_PARAM_IN_COUNT(len) _In_count_(len) /* Required const pointer to (len) valid objects (never NULL). */ -#else -#define LSX_PARAM_IN_COUNT(len) /* Required const pointer to (len) valid objects (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) const bytes of initialized data, where (len) is the name of -another parameter. -@param len The parameter that contains the number of bytes in the array. -*/ -#ifdef _In_bytecount_ -#define LSX_PARAM_IN_BYTECOUNT(len) _In_bytecount_(len) /* Required const pointer to (len) bytes of data (never NULL). */ -#else -#define LSX_PARAM_IN_BYTECOUNT(len) /* Required const pointer to (len) bytes of data (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to one const element of the pointed-to type. -*/ -#ifdef _In_opt_ -#define LSX_PARAM_IN_OPT _In_opt_ /* Optional const pointer to a valid object (may be NULL). */ -#else -#define LSX_PARAM_IN_OPT /* Optional const pointer to a valid object (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to a const 0-terminated string. -*/ -#ifdef _In_opt_z_ -#define LSX_PARAM_IN_OPT_Z _In_opt_z_ /* Optional const pointer to 0-terminated string (may be NULL). */ -#else -#define LSX_PARAM_IN_OPT_Z /* Optional const pointer to 0-terminated string (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to one initialized element of the pointed-to type (never NULL). The -function may modify the element. -*/ -#ifdef _Inout_ -#define LSX_PARAM_INOUT _Inout_ /* Required pointer to a valid object (never NULL). */ -#else -#define LSX_PARAM_INOUT /* Required pointer to a valid object (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to (len) initialized elements of the pointed-to type (never NULL). The -function may modify the elements. -@param len The parameter that contains the number of elements in the array. -*/ -#ifdef _Inout_count_x_ -#define LSX_PARAM_INOUT_COUNT(len) _Inout_count_x_(len) /* Required pointer to (len) valid objects (never NULL). */ -#else -#define LSX_PARAM_INOUT_COUNT(len) /* Required pointer to (len) valid objects (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for one element of the pointed-to type (never -NULL). The function will initialize the element. -*/ -#ifdef _Out_ -#define LSX_PARAM_OUT _Out_ /* Required pointer to an object to be initialized (never NULL). */ -#else -#define LSX_PARAM_OUT /* Required pointer to an object to be initialized (never NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) bytes of data (never NULL), where (len) -is the name of another parameter. The function may write up to len bytes of -data to this memory. -@param len The parameter that contains the number of bytes in the array. -*/ -#ifdef _Out_bytecap_ -#define LSX_PARAM_OUT_BYTECAP(len) _Out_bytecap_(len) /* Required pointer to writable buffer with room for len bytes. */ -#else -#define LSX_PARAM_OUT_BYTECAP(len) /* Required pointer to writable buffer with room for len bytes. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) elements of the pointed-to type (never -NULL), where (len) is the name of another parameter. On return, (filled) -elements will have been initialized, where (filled) is either the dereference -of another pointer parameter (for example "*written") or the "return" -parameter (indicating that the function returns the number of elements -written). -@param len The parameter that contains the number of elements in the array. -@param filled The dereference of the parameter that receives the number of elements written to the array, or "return" if the value is returned. -*/ -#ifdef _Out_cap_post_count_ -#define LSX_PARAM_OUT_CAP_POST_COUNT(len,filled) _Out_cap_post_count_(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled) elements will have been initialized. */ -#else -#define LSX_PARAM_OUT_CAP_POST_COUNT(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled) elements will have been initialized. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer to memory sufficient for (len) elements of the pointed-to type (never -NULL), where (len) is the name of another parameter. On return, (filled+1) -elements will have been initialized, with the last element having been -initialized to 0, where (filled) is either the dereference of another pointer -parameter (for example, "*written") or the "return" parameter (indicating that -the function returns the number of elements written). -@param len The parameter that contains the number of elements in the array. -@param filled The dereference of the parameter that receives the number of elements written to the array (not counting the terminating null), or "return" if the value is returned. -*/ -#ifdef _Out_z_cap_post_count_ -#define LSX_PARAM_OUT_Z_CAP_POST_COUNT(len,filled) _Out_z_cap_post_count_(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled+1) elements will have been initialized, and the array will be 0-terminated. */ -#else -#define LSX_PARAM_OUT_Z_CAP_POST_COUNT(len,filled) /* Required pointer to buffer for (len) elements (never NULL); on return, (filled+1) elements will have been initialized, and the array will be 0-terminated. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is either NULL -or a valid pointer to memory sufficient for one element of the pointed-to -type. The function will initialize the element. -*/ -#ifdef _Out_opt_ -#define LSX_PARAM_OUT_OPT _Out_opt_ /* Optional pointer to an object to be initialized (may be NULL). */ -#else -#define LSX_PARAM_OUT_OPT /* Optional pointer to an object to be initialized (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which may be NULL when the function is -invoked. -*/ -#ifdef _Deref_pre_maybenull_ -#define LSX_PARAM_DEREF_PRE_MAYBENULL _Deref_pre_maybenull_ /* Required pointer (never NULL) to another pointer (may be NULL). */ -#else -#define LSX_PARAM_DEREF_PRE_MAYBENULL /* Required pointer (never NULL) to another pointer (may be NULL). */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which will be NULL when the function -returns. -*/ -#ifdef _Deref_post_null_ -#define LSX_PARAM_DEREF_POST_NULL _Deref_post_null_ /* Required pointer (never NULL) to another pointer, which will be NULL on exit. */ -#else -#define LSX_PARAM_DEREF_POST_NULL /* Required pointer (never NULL) to another pointer, which will be NULL on exit. */ -#endif - -/** -Plugins API: -Attribute applied to a parameter to indicate that the parameter is a valid -pointer (never NULL) to another pointer which will be non-NULL when the -function returns. -*/ -#ifdef _Deref_post_notnull_ -#define LSX_PARAM_DEREF_POST_NOTNULL _Deref_post_notnull_ /* Required pointer (never NULL) to another pointer, which will be valid (not NULL) on exit. */ -#else -#define LSX_PARAM_DEREF_POST_NOTNULL /* Required pointer (never NULL) to another pointer, which will be valid (not NULL) on exit. */ -#endif - -/** -Plugins API: -Expression that "uses" a potentially-unused variable to avoid compiler -warnings (especially in macro-generated code). -*/ -#ifdef _PREFAST_ -#define LSX_USE_VAR(x) ((void)(x=0)) /* During static analysis, initialize unused variables to 0. */ -#else -#define LSX_USE_VAR(x) ((void)(x)) /* Parameter or variable is intentionally unused. */ -#endif - -/** -Plugins API: -Compile-time assertion. Causes a compile error if the expression is false. -@param e The expression to test. If expression is false, compilation will fail. -@param f A unique identifier for the test, for example foo_must_not_be_zero. -*/ -#define lsx_static_assert(e,f) enum {lsx_static_assert_##f = 1/((e) ? 1 : 0)} - -/***************************************************************************** -Basic typedefs: -*****************************************************************************/ - -/** -Client API: -Signed twos-complement 8-bit type. Typically defined as signed char. -*/ -#if SCHAR_MAX==127 && SCHAR_MIN==(-128) -typedef signed char sox_int8_t; -#elif CHAR_MAX==127 && CHAR_MIN==(-128) -typedef char sox_int8_t; -#else -#error Unable to determine an appropriate definition for sox_int8_t. -#endif - -/** -Client API: -Unsigned 8-bit type. Typically defined as unsigned char. -*/ -#if UCHAR_MAX==0xff -typedef unsigned char sox_uint8_t; -#elif CHAR_MAX==0xff && CHAR_MIN==0 -typedef char sox_uint8_t; -#else -#error Unable to determine an appropriate definition for sox_uint8_t. -#endif - -/** -Client API: -Signed twos-complement 16-bit type. Typically defined as short. -*/ -#if SHRT_MAX==32767 && SHRT_MIN==(-32768) -typedef short sox_int16_t; -#elif INT_MAX==32767 && INT_MIN==(-32768) -typedef int sox_int16_t; -#else -#error Unable to determine an appropriate definition for sox_int16_t. -#endif - -/** -Client API: -Unsigned 16-bit type. Typically defined as unsigned short. -*/ -#if USHRT_MAX==0xffff -typedef unsigned short sox_uint16_t; -#elif UINT_MAX==0xffff -typedef unsigned int sox_uint16_t; -#else -#error Unable to determine an appropriate definition for sox_uint16_t. -#endif - -/** -Client API: -Signed twos-complement 32-bit type. Typically defined as int. -*/ -#if INT_MAX==2147483647 && INT_MIN==(-2147483647-1) -typedef int sox_int32_t; -#elif LONG_MAX==2147483647 && LONG_MIN==(-2147483647-1) -typedef long sox_int32_t; -#else -#error Unable to determine an appropriate definition for sox_int32_t. -#endif - -/** -Client API: -Unsigned 32-bit type. Typically defined as unsigned int. -*/ -#if UINT_MAX==0xffffffff -typedef unsigned int sox_uint32_t; -#elif ULONG_MAX==0xffffffff -typedef unsigned long sox_uint32_t; -#else -#error Unable to determine an appropriate definition for sox_uint32_t. -#endif - -/** -Client API: -Signed twos-complement 64-bit type. Typically defined as long or long long. -*/ -#if LONG_MAX==9223372036854775807 && LONG_MIN==(-9223372036854775807-1) -typedef long sox_int64_t; -#elif defined(_MSC_VER) -typedef __int64 sox_int64_t; -#else -typedef long long sox_int64_t; -#endif - -/** -Client API: -Unsigned 64-bit type. Typically defined as unsigned long or unsigned long long. -*/ -#if ULONG_MAX==0xffffffffffffffff -typedef unsigned long sox_uint64_t; -#elif defined(_MSC_VER) -typedef unsigned __int64 sox_uint64_t; -#else -typedef unsigned long long sox_uint64_t; -#endif - -#ifndef _DOXYGEN_ -lsx_static_assert(sizeof(sox_int8_t)==1, sox_int8_size); -lsx_static_assert(sizeof(sox_uint8_t)==1, sox_uint8_size); -lsx_static_assert(sizeof(sox_int16_t)==2, sox_int16_size); -lsx_static_assert(sizeof(sox_uint16_t)==2, sox_uint16_size); -lsx_static_assert(sizeof(sox_int32_t)==4, sox_int32_size); -lsx_static_assert(sizeof(sox_uint32_t)==4, sox_uint32_size); -lsx_static_assert(sizeof(sox_int64_t)==8, sox_int64_size); -lsx_static_assert(sizeof(sox_uint64_t)==8, sox_uint64_size); -#endif - -/** -Client API: -Alias for sox_int32_t (beware of the extra byte). -*/ -typedef sox_int32_t sox_int24_t; - -/** -Client API: -Alias for sox_uint32_t (beware of the extra byte). -*/ -typedef sox_uint32_t sox_uint24_t; - -/** -Client API: -Native SoX audio sample type (alias for sox_int32_t). -*/ -typedef sox_int32_t sox_sample_t; - -/** -Client API: -Samples per second is stored as a double. -*/ -typedef double sox_rate_t; - -/** -Client API: -File's metadata, access via sox_*_comments functions. -*/ -typedef char * * sox_comments_t; - -/***************************************************************************** -Enumerations: -*****************************************************************************/ - -/** -Client API: -Boolean type, assignment (but not necessarily binary) compatible with C++ bool. -*/ -typedef enum sox_bool { - sox_false, /**< False = 0. */ - sox_true /**< True = 1. */ -} sox_bool; - -/** -Client API: -no, yes, or default (default usually implies some kind of auto-detect logic). -*/ -typedef enum sox_option_t { - sox_option_no, /**< Option specified as no = 0. */ - sox_option_yes, /**< Option specified as yes = 1. */ - sox_option_default /**< Option unspecified = 2. */ -} sox_option_t; - -/** -Client API: -The libSoX-specific error codes. -libSoX functions may return these codes or others that map from errno codes. -*/ -enum sox_error_t { - SOX_SUCCESS = 0, /**< Function succeeded = 0 */ - SOX_EOF = -1, /**< End Of File or other error = -1 */ - SOX_EHDR = 2000, /**< Invalid Audio Header = 2000 */ - SOX_EFMT, /**< Unsupported data format = 2001 */ - SOX_ENOMEM, /**< Can't alloc memory = 2002 */ - SOX_EPERM, /**< Operation not permitted = 2003 */ - SOX_ENOTSUP, /**< Operation not supported = 2004 */ - SOX_EINVAL /**< Invalid argument = 2005 */ -}; - -/** -Client API: -Flags indicating whether optional features are present in this build of libSoX. -*/ -typedef enum sox_version_flags_t { - sox_version_none = 0, /**< No special features = 0. */ - sox_version_have_popen = 1, /**< popen = 1. */ - sox_version_have_magic = 2, /**< magic = 2. */ - sox_version_have_threads = 4, /**< threads = 4. */ - sox_version_have_memopen = 8 /**< memopen = 8. */ -} sox_version_flags_t; - -/** -Client API: -Format of sample data. -*/ -typedef enum sox_encoding_t { - SOX_ENCODING_UNKNOWN , /**< encoding has not yet been determined */ - - SOX_ENCODING_SIGN2 , /**< signed linear 2's comp: Mac */ - SOX_ENCODING_UNSIGNED , /**< unsigned linear: Sound Blaster */ - SOX_ENCODING_FLOAT , /**< floating point (binary format) */ - SOX_ENCODING_FLOAT_TEXT, /**< floating point (text format) */ - SOX_ENCODING_FLAC , /**< FLAC compression */ - SOX_ENCODING_HCOM , /**< Mac FSSD files with Huffman compression */ - SOX_ENCODING_WAVPACK , /**< WavPack with integer samples */ - SOX_ENCODING_WAVPACKF , /**< WavPack with float samples */ - SOX_ENCODING_ULAW , /**< u-law signed logs: US telephony, SPARC */ - SOX_ENCODING_ALAW , /**< A-law signed logs: non-US telephony, Psion */ - SOX_ENCODING_G721 , /**< G.721 4-bit ADPCM */ - SOX_ENCODING_G723 , /**< G.723 3 or 5 bit ADPCM */ - SOX_ENCODING_CL_ADPCM , /**< Creative Labs 8 --> 2,3,4 bit Compressed PCM */ - SOX_ENCODING_CL_ADPCM16, /**< Creative Labs 16 --> 4 bit Compressed PCM */ - SOX_ENCODING_MS_ADPCM , /**< Microsoft Compressed PCM */ - SOX_ENCODING_IMA_ADPCM , /**< IMA Compressed PCM */ - SOX_ENCODING_OKI_ADPCM , /**< Dialogic/OKI Compressed PCM */ - SOX_ENCODING_DPCM , /**< Differential PCM: Fasttracker 2 (xi) */ - SOX_ENCODING_DWVW , /**< Delta Width Variable Word */ - SOX_ENCODING_DWVWN , /**< Delta Width Variable Word N-bit */ - SOX_ENCODING_GSM , /**< GSM 6.10 33byte frame lossy compression */ - SOX_ENCODING_MP3 , /**< MP3 compression */ - SOX_ENCODING_VORBIS , /**< Vorbis compression */ - SOX_ENCODING_AMR_WB , /**< AMR-WB compression */ - SOX_ENCODING_AMR_NB , /**< AMR-NB compression */ - SOX_ENCODING_CVSD , /**< Continuously Variable Slope Delta modulation */ - SOX_ENCODING_LPC10 , /**< Linear Predictive Coding */ - - SOX_ENCODINGS /**< End of list marker */ -} sox_encoding_t; - -/** -Client API: -Flags for sox_encodings_info_t: lossless/lossy1/lossy2. -*/ -typedef enum sox_encodings_flags_t { - sox_encodings_none = 0, /**< no flags specified (implies lossless encoding) = 0. */ - sox_encodings_lossy1 = 1, /**< encode, decode: lossy once = 1. */ - sox_encodings_lossy2 = 2 /**< encode, decode, encode, decode: lossy twice = 2. */ -} sox_encodings_flags_t; - -/** -Client API: -Type of plot. -*/ -typedef enum sox_plot_t { - sox_plot_off, /**< No plot = 0. */ - sox_plot_octave, /**< Octave plot = 1. */ - sox_plot_gnuplot, /**< Gnuplot plot = 2. */ - sox_plot_data /**< Plot data = 3. */ -} sox_plot_t; - -/** -Client API: -Loop modes: upper 4 bits mask the loop blass, lower 4 bits describe -the loop behaviour, for example single shot, bidirectional etc. -*/ -enum sox_loop_flags_t { - sox_loop_none = 0, /**< single-shot = 0 */ - sox_loop_forward = 1, /**< forward loop = 1 */ - sox_loop_forward_back = 2, /**< forward/back loop = 2 */ - sox_loop_8 = 32, /**< 8 loops (??) = 32 */ - sox_loop_sustain_decay = 64 /**< AIFF style, one sustain & one decay loop = 64 */ -}; - -/** -Plugins API: -Is file a real file, a pipe, or a url? -*/ -typedef enum lsx_io_type -{ - lsx_io_file, /**< File is a real file = 0. */ - lsx_io_pipe, /**< File is a pipe (no seeking) = 1. */ - lsx_io_url /**< File is a URL (no seeking) = 2. */ -} lsx_io_type; - -/***************************************************************************** -Macros: -*****************************************************************************/ - -/** -Client API: -Compute a 32-bit integer API version from three 8-bit parts. -@param a Major version. -@param b Minor version. -@param c Revision or build number. -@returns 32-bit integer API version 0x000a0b0c. -*/ -#define SOX_LIB_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) - -/** -Client API: -The API version of the sox.h file. It is not meant to follow the version -number of SoX but it has historically. Please do not count on -SOX_LIB_VERSION_CODE staying in sync with the libSoX version. -*/ -#define SOX_LIB_VERSION_CODE SOX_LIB_VERSION(14, 4, 1) - -/** -Client API: -Returns the smallest (negative) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer; -for example, SOX_INT_MIN(8) = 0x80, SOX_INT_MIN(16) = 0x8000, etc. -@param bits Size of value for which to calculate minimum. -@returns the smallest (negative) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer. -*/ -#define SOX_INT_MIN(bits) (1 <<((bits)-1)) - -/** -Client API: -Returns the largest (positive) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer; -for example, SOX_INT_MAX(8) = 0x7F, SOX_INT_MAX(16) = 0x7FFF, etc. -@param bits Size of value for which to calculate maximum. -@returns the largest (positive) value storable in a twos-complement signed -integer with the specified number of bits, cast to an unsigned integer. -*/ -#define SOX_INT_MAX(bits) (((unsigned)-1)>>(33-(bits))) - -/** -Client API: -Returns the largest value storable in an unsigned integer with the specified -number of bits; for example, SOX_UINT_MAX(8) = 0xFF, -SOX_UINT_MAX(16) = 0xFFFF, etc. -@param bits Size of value for which to calculate maximum. -@returns the largest value storable in an unsigned integer with the specified -number of bits. -*/ -#define SOX_UINT_MAX(bits) (SOX_INT_MIN(bits)|SOX_INT_MAX(bits)) - -/** -Client API: -Returns 0x7F. -*/ -#define SOX_INT8_MAX SOX_INT_MAX(8) - -/** -Client API: -Returns 0x7FFF. -*/ -#define SOX_INT16_MAX SOX_INT_MAX(16) - -/** -Client API: -Returns 0x7FFFFF. -*/ -#define SOX_INT24_MAX SOX_INT_MAX(24) - -/** -Client API: -Returns 0x7FFFFFFF. -*/ -#define SOX_INT32_MAX SOX_INT_MAX(32) - -/** -Client API: -Bits in a sox_sample_t = 32. -*/ -#define SOX_SAMPLE_PRECISION 32 - -/** -Client API: -Max value for sox_sample_t = 0x7FFFFFFF. -*/ -#define SOX_SAMPLE_MAX (sox_sample_t)SOX_INT_MAX(32) - -/** -Client API: -Min value for sox_sample_t = 0x80000000. -*/ -#define SOX_SAMPLE_MIN (sox_sample_t)SOX_INT_MIN(32) - - -/* Conversions: Linear PCM <--> sox_sample_t - * - * I/O Input sox_sample_t Clips? Input sox_sample_t Clips? - * Format Minimum Minimum I O Maximum Maximum I O - * ------ --------- ------------ -- -- -------- ------------ -- -- - * Float -inf -1 y n inf 1 - 5e-10 y n - * Int8 -128 -128 n n 127 127.9999999 n y - * Int16 -32768 -32768 n n 32767 32767.99998 n y - * Int24 -8388608 -8388608 n n 8388607 8388607.996 n y - * Int32 -2147483648 -2147483648 n n 2147483647 2147483647 n n - * - * Conversions are as accurate as possible (with rounding). - * - * Rounding: halves toward +inf, all others to nearest integer. - * - * Clips? shows whether on not there is the possibility of a conversion - * clipping to the minimum or maximum value when inputing from or outputing - * to a given type. - * - * Unsigned integers are converted to and from signed integers by flipping - * the upper-most bit then treating them as signed integers. - */ - -/** -Client API: -Declares the temporary local variables that are required when using SOX -conversion macros. -*/ -#define SOX_SAMPLE_LOCALS sox_sample_t sox_macro_temp_sample LSX_UNUSED; \ - double sox_macro_temp_double LSX_UNUSED - -/** -Client API: -Sign bit for sox_sample_t = 0x80000000. -*/ -#define SOX_SAMPLE_NEG SOX_INT_MIN(32) - -/** -Client API: -Converts sox_sample_t to an unsigned integer of width (bits). -@param bits Width of resulting sample (1 through 32). -@param d Input sample to be converted. -@param clips Variable that is incremented if the result is too big. -@returns Unsigned integer of width (bits). -*/ -#define SOX_SAMPLE_TO_UNSIGNED(bits,d,clips) \ - (sox_uint##bits##_t)(SOX_SAMPLE_TO_SIGNED(bits,d,clips)^SOX_INT_MIN(bits)) - -/** -Client API: -Converts sox_sample_t to a signed integer of width (bits). -@param bits Width of resulting sample (1 through 32). -@param d Input sample to be converted. -@param clips Variable that is incremented if the result is too big. -@returns Signed integer of width (bits). -*/ -#define SOX_SAMPLE_TO_SIGNED(bits,d,clips) \ - (sox_int##bits##_t)(LSX_USE_VAR(sox_macro_temp_double),sox_macro_temp_sample=(d),sox_macro_temp_sample>SOX_SAMPLE_MAX-(1<<(31-bits))?++(clips),SOX_INT_MAX(bits):((sox_uint32_t)(sox_macro_temp_sample+(1<<(31-bits))))>>(32-bits)) - -/** -Client API: -Converts signed integer of width (bits) to sox_sample_t. -@param bits Width of input sample (1 through 32). -@param d Input sample to be converted. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_TO_SAMPLE(bits,d)((sox_sample_t)(d)<<(32-bits)) - -/** -Client API: -Converts unsigned integer of width (bits) to sox_sample_t. -@param bits Width of input sample (1 through 32). -@param d Input sample to be converted. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_TO_SAMPLE(bits,d)(SOX_SIGNED_TO_SAMPLE(bits,d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts unsigned 8-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_8BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(8,d) - -/** -Client API: -Converts signed 8-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_8BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(8,d) - -/** -Client API: -Converts unsigned 16-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_16BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(16,d) - -/** -Client API: -Converts signed 16-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_16BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(16,d) - -/** -Client API: -Converts unsigned 24-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_24BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(24,d) - -/** -Client API: -Converts signed 24-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_24BIT_TO_SAMPLE(d,clips) SOX_SIGNED_TO_SAMPLE(24,d) - -/** -Client API: -Converts unsigned 32-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_UNSIGNED_32BIT_TO_SAMPLE(d,clips) ((sox_sample_t)(d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts signed 32-bit integer to sox_sample_t. -@param d Input sample to be converted. -@param clips The parameter is not used. -@returns SoX native sample value. -*/ -#define SOX_SIGNED_32BIT_TO_SAMPLE(d,clips) (sox_sample_t)(d) - -/** -Client API: -Converts 32-bit float to sox_sample_t. -@param d Input sample to be converted, range [-1, 1). -@param clips Variable to increment if the input sample is too large or too small. -@returns SoX native sample value. -*/ -#define SOX_FLOAT_32BIT_TO_SAMPLE(d,clips) (sox_sample_t)(LSX_USE_VAR(sox_macro_temp_sample),sox_macro_temp_double=(d)*(SOX_SAMPLE_MAX+1.),sox_macro_temp_double=SOX_SAMPLE_MAX+1.?sox_macro_temp_double>SOX_SAMPLE_MAX+1.?++(clips),SOX_SAMPLE_MAX:SOX_SAMPLE_MAX:sox_macro_temp_double) - -/** -Client API: -Converts 64-bit float to sox_sample_t. -@param d Input sample to be converted, range [-1, 1). -@param clips Variable to increment if the input sample is too large or too small. -@returns SoX native sample value. -*/ -#define SOX_FLOAT_64BIT_TO_SAMPLE(d,clips) (sox_sample_t)(LSX_USE_VAR(sox_macro_temp_sample),sox_macro_temp_double=(d)*(SOX_SAMPLE_MAX+1.),sox_macro_temp_double<0?sox_macro_temp_double<=SOX_SAMPLE_MIN-.5?++(clips),SOX_SAMPLE_MIN:sox_macro_temp_double-.5:sox_macro_temp_double>=SOX_SAMPLE_MAX+.5?sox_macro_temp_double>SOX_SAMPLE_MAX+1.?++(clips),SOX_SAMPLE_MAX:SOX_SAMPLE_MAX:sox_macro_temp_double+.5) - -/** -Client API: -Converts SoX native sample to an unsigned 8-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_8BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(8,d,clips) - -/** -Client API: -Converts SoX native sample to an signed 8-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_8BIT(d,clips) SOX_SAMPLE_TO_SIGNED(8,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 16-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_16BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(16,d,clips) - -/** -Client API: -Converts SoX native sample to a signed 16-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_16BIT(d,clips) SOX_SAMPLE_TO_SIGNED(16,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 24-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_24BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(24,d,clips) - -/** -Client API: -Converts SoX native sample to a signed 24-bit integer. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_SIGNED_24BIT(d,clips) SOX_SAMPLE_TO_SIGNED(24,d,clips) - -/** -Client API: -Converts SoX native sample to an unsigned 32-bit integer. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_UNSIGNED_32BIT(d,clips) (sox_uint32_t)((d)^SOX_SAMPLE_NEG) - -/** -Client API: -Converts SoX native sample to a signed 32-bit integer. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_SIGNED_32BIT(d,clips) (sox_int32_t)(d) - -/** -Client API: -Converts SoX native sample to a 32-bit float. -@param d Input sample to be converted. -@param clips Variable to increment if input sample is too large. -*/ -#define SOX_SAMPLE_TO_FLOAT_32BIT(d,clips) (LSX_USE_VAR(sox_macro_temp_double),sox_macro_temp_sample=(d),sox_macro_temp_sample>SOX_SAMPLE_MAX-128?++(clips),1:(((sox_macro_temp_sample+128)&~255)*(1./(SOX_SAMPLE_MAX+1.)))) - -/** -Client API: -Converts SoX native sample to a 64-bit float. -@param d Input sample to be converted. -@param clips The parameter is not used. -*/ -#define SOX_SAMPLE_TO_FLOAT_64BIT(d,clips) ((d)*(1./(SOX_SAMPLE_MAX+1.))) - -/** -Client API: -Clips a value of a type that is larger then sox_sample_t (for example, int64) -to sox_sample_t's limits and increment a counter if clipping occurs. -@param samp Value (lvalue) to be clipped, updated as necessary. -@param clips Value (lvalue) that is incremented if clipping is needed. -*/ -#define SOX_SAMPLE_CLIP_COUNT(samp, clips) \ - do { \ - if (samp > SOX_SAMPLE_MAX) \ - { samp = SOX_SAMPLE_MAX; clips++; } \ - else if (samp < SOX_SAMPLE_MIN) \ - { samp = SOX_SAMPLE_MIN; clips++; } \ - } while (0) - -/** -Client API: -Clips a value of a type that is larger then sox_sample_t (for example, int64) -to sox_sample_t's limits and increment a counter if clipping occurs. -@param d Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_ROUND_CLIP_COUNT(d, clips) \ - ((d) < 0? (d) <= SOX_SAMPLE_MIN - 0.5? ++(clips), SOX_SAMPLE_MIN: (d) - 0.5 \ - : (d) >= SOX_SAMPLE_MAX + 0.5? ++(clips), SOX_SAMPLE_MAX: (d) + 0.5) - -/** -Client API: -Clips a value to the limits of a signed integer of the specified width -and increment a counter if clipping occurs. -@param bits Width (in bits) of target integer type. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_INTEGER_CLIP_COUNT(bits,i,clips) ( \ - (i) >(1 << ((bits)-1))- 1? ++(clips),(1 << ((bits)-1))- 1 : \ - (i) <-1 << ((bits)-1) ? ++(clips),-1 << ((bits)-1) : (i)) - -/** -Client API: -Clips a value to the limits of a 16-bit signed integer and increment a counter -if clipping occurs. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_16BIT_CLIP_COUNT(i,clips) SOX_INTEGER_CLIP_COUNT(16,i,clips) - -/** -Client API: -Clips a value to the limits of a 24-bit signed integer and increment a counter -if clipping occurs. -@param i Value (rvalue) to be clipped. -@param clips Value (lvalue) that is incremented if clipping is needed. -@returns Clipped value. -*/ -#define SOX_24BIT_CLIP_COUNT(i,clips) SOX_INTEGER_CLIP_COUNT(24,i,clips) - -#define SOX_SIZE_MAX ((size_t)(-1)) /**< Client API: Maximum value of size_t. */ - -#define SOX_UNSPEC 0 /**< Client API: Members of sox_signalinfo_t are set to SOX_UNSPEC (= 0) if the actual value is not yet known. */ -#define SOX_UNKNOWN_LEN (sox_uint64_t)(-1) /**< Client API: sox_signalinfo_t.length is set to SOX_UNKNOWN_LEN (= -1) within the effects chain if the actual length is not known. Format handlers currently use SOX_UNSPEC instead. */ -#define SOX_IGNORE_LENGTH (sox_uint64_t)(-2) /**< Client API: sox_signalinfo_t.length is set to SOX_IGNORE_LENGTH (= -2) to indicate that a format handler should ignore length information in file headers. */ - -#define SOX_DEFAULT_CHANNELS 2 /**< Client API: Default channel count is 2 (stereo). */ -#define SOX_DEFAULT_RATE 48000 /**< Client API: Default rate is 48000Hz. */ -#define SOX_DEFAULT_PRECISION 16 /**< Client API: Default precision is 16 bits per sample. */ -#define SOX_DEFAULT_ENCODING SOX_ENCODING_SIGN2 /**< Client API: Default encoding is SIGN2 (linear 2's complement PCM). */ - -#define SOX_LOOP_NONE ((unsigned char)sox_loop_none) /**< Client API: single-shot = 0 */ -#define SOX_LOOP_8 ((unsigned char)sox_loop_8) /**< Client API: 8 loops = 32 */ -#define SOX_LOOP_SUSTAIN_DECAY ((unsigned char)sox_loop_sustain_decay) /**< Client API: AIFF style, one sustain & one decay loop = 64 */ - -#define SOX_MAX_NLOOPS 8 /**< Client API: Maximum number of loops supported by sox_oob_t = 8. */ - -#define SOX_FILE_NOSTDIO 0x0001 /**< Client API: Does not use stdio routines */ -#define SOX_FILE_DEVICE 0x0002 /**< Client API: File is an audio device */ -#define SOX_FILE_PHONY 0x0004 /**< Client API: Phony file/device (for example /dev/null) */ -#define SOX_FILE_REWIND 0x0008 /**< Client API: File should be rewound to write header */ -#define SOX_FILE_BIT_REV 0x0010 /**< Client API: Is file bit-reversed? */ -#define SOX_FILE_NIB_REV 0x0020 /**< Client API: Is file nibble-reversed? */ -#define SOX_FILE_ENDIAN 0x0040 /**< Client API: Is file format endian? */ -#define SOX_FILE_ENDBIG 0x0080 /**< Client API: For endian file format, is it big endian? */ -#define SOX_FILE_MONO 0x0100 /**< Client API: Do channel restrictions allow mono? */ -#define SOX_FILE_STEREO 0x0200 /**< Client API: Do channel restrictions allow stereo? */ -#define SOX_FILE_QUAD 0x0400 /**< Client API: Do channel restrictions allow quad? */ - -#define SOX_FILE_CHANS (SOX_FILE_MONO | SOX_FILE_STEREO | SOX_FILE_QUAD) /**< Client API: No channel restrictions */ -#define SOX_FILE_LIT_END (SOX_FILE_ENDIAN | 0) /**< Client API: File is little-endian */ -#define SOX_FILE_BIG_END (SOX_FILE_ENDIAN | SOX_FILE_ENDBIG) /**< Client API: File is big-endian */ - -#define SOX_EFF_CHAN 1 /**< Client API: Effect might alter the number of channels */ -#define SOX_EFF_RATE 2 /**< Client API: Effect might alter sample rate */ -#define SOX_EFF_PREC 4 /**< Client API: Effect does its own calculation of output sample precision (otherwise a default value is taken, depending on the presence of SOX_EFF_MODIFY) */ -#define SOX_EFF_LENGTH 8 /**< Client API: Effect might alter audio length (as measured in time units, not necessarily in samples) */ -#define SOX_EFF_MCHAN 16 /**< Client API: Effect handles multiple channels internally */ -#define SOX_EFF_NULL 32 /**< Client API: Effect does nothing (can be optimized out of chain) */ -#define SOX_EFF_DEPRECATED 64 /**< Client API: Effect will soon be removed from SoX */ -#define SOX_EFF_GAIN 128 /**< Client API: Effect does not support gain -r */ -#define SOX_EFF_MODIFY 256 /**< Client API: Effect does not modify sample values (but might remove or duplicate samples or insert zeros) */ -#define SOX_EFF_ALPHA 512 /**< Client API: Effect is experimental/incomplete */ -#define SOX_EFF_INTERNAL 1024 /**< Client API: Effect present in libSoX but not valid for use by SoX command-line tools */ - -/** -Client API: -When used as the "whence" parameter of sox_seek, indicates that the specified -offset is relative to the beginning of the file. -*/ -#define SOX_SEEK_SET 0 - -/***************************************************************************** -Forward declarations: -*****************************************************************************/ - -typedef struct sox_format_t sox_format_t; -typedef struct sox_effect_t sox_effect_t; -typedef struct sox_effect_handler_t sox_effect_handler_t; -typedef struct sox_format_handler_t sox_format_handler_t; - -/***************************************************************************** -Function pointers: -*****************************************************************************/ - -/** -Client API: -Callback to write a message to an output device (console or log file), -used by sox_globals_t.output_message_handler. -*/ -typedef void (LSX_API * sox_output_message_handler_t)( - unsigned level, /* 1 = FAIL, 2 = WARN, 3 = INFO, 4 = DEBUG, 5 = DEBUG_MORE, 6 = DEBUG_MOST. */ - LSX_PARAM_IN_Z char const * filename, /* Source code __FILENAME__ from which message originates. */ - LSX_PARAM_IN_PRINTF char const * fmt, /* Message format string. */ - LSX_PARAM_IN va_list ap /* Message format parameters. */ - ); - -/** -Client API: -Callback to retrieve information about a format handler, -used by sox_format_tab_t.fn. -@returns format handler information. -*/ -typedef sox_format_handler_t const * (LSX_API * sox_format_fn_t)(void); - -/** -Client API: -Callback to get information about an effect handler, -used by the table returned from sox_get_effect_fns(void). -@returns Pointer to information about an effect handler. -*/ -typedef sox_effect_handler_t const * (LSX_API *sox_effect_fn_t)(void); - -/** -Client API: -Callback to initialize reader (decoder), used by -sox_format_handler.startread. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_startread)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to read (decode) a block of samples, -used by sox_format_handler.read. -@returns number of samples read, or 0 if unsuccessful. -*/ -typedef size_t (LSX_API * sox_format_handler_read)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(len,return) sox_sample_t *buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Callback to close reader (decoder), -used by sox_format_handler.stopread. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_stopread)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to initialize writer (encoder), -used by sox_format_handler.startwrite. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_startwrite)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to write (encode) a block of samples, -used by sox_format_handler.write. -@returns number of samples written, or 0 if unsuccessful. -*/ -typedef size_t (LSX_API * sox_format_handler_write)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_IN_COUNT(len) sox_sample_t const * buf, /**< Buffer to which samples are written. */ - size_t len /**< Capacity of buf, measured in samples. */ - ); - -/** -Client API: -Callback to close writer (decoder), -used by sox_format_handler.stopwrite. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_stopwrite)( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Callback to reposition reader, -used by sox_format_handler.seek. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_format_handler_seek)( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - sox_uint64_t offset /**< Sample offset to which reader should be positioned. */ - ); - -/** -Client API: -Callback to parse command-line arguments (called once per effect), -used by sox_effect_handler.getopts. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_getopts)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - int argc, /**< Number of arguments in argv. */ - LSX_PARAM_IN_COUNT(argc) char *argv[] /**< Array of command-line arguments. */ - ); - -/** -Client API: -Callback to initialize effect (called once per flow), -used by sox_effect_handler.start. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_start)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback to process samples, -used by sox_effect_handler.flow. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_flow)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - LSX_PARAM_IN_COUNT(*isamp) sox_sample_t const * ibuf, /**< Buffer from which to read samples. */ - LSX_PARAM_OUT_CAP_POST_COUNT(*osamp,*osamp) sox_sample_t * obuf, /**< Buffer to which samples are written. */ - LSX_PARAM_INOUT size_t *isamp, /**< On entry, contains capacity of ibuf; on exit, contains number of samples consumed. */ - LSX_PARAM_INOUT size_t *osamp /**< On entry, contains capacity of obuf; on exit, contains number of samples written. */ - ); - -/** -Client API: -Callback to finish getting output after input is complete, -used by sox_effect_handler.drain. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_drain)( - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(*osamp,*osamp) sox_sample_t *obuf, /**< Buffer to which samples are written. */ - LSX_PARAM_INOUT size_t *osamp /**< On entry, contains capacity of obuf; on exit, contains number of samples written. */ - ); - -/** -Client API: -Callback to shut down effect (called once per flow), -used by sox_effect_handler.stop. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_stop)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback to shut down effect (called once per effect), -used by sox_effect_handler.kill. -@returns SOX_SUCCESS if successful. -*/ -typedef int (LSX_API * sox_effect_handler_kill)( - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect pointer. */ - ); - -/** -Client API: -Callback called while flow is running (called once per buffer), -used by sox_flow_effects.callback. -@returns SOX_SUCCESS to continue, other value to abort flow. -*/ -typedef int (LSX_API * sox_flow_effects_callback)( - sox_bool all_done, - void * client_data - ); - -/** -Client API: -Callback for enumerating the contents of a playlist, -used by the sox_parse_playlist function. -@returns SOX_SUCCESS if successful, any other value to abort playlist enumeration. -*/ -typedef int (LSX_API * sox_playlist_callback_t)( - void * callback_data, - LSX_PARAM_IN_Z char const * filename - ); - -/***************************************************************************** -Structures: -*****************************************************************************/ - -/** -Client API: -Information about a build of libSoX, returned from the sox_version_info -function. -*/ -typedef struct sox_version_info_t { - size_t size; /**< structure size = sizeof(sox_version_info_t) */ - sox_version_flags_t flags; /**< feature flags = popen | magic | threads | memopen */ - sox_uint32_t version_code; /**< version number = 0x140400 */ - char const * version; /**< version string = sox_version(), for example, "14.4.0" */ - char const * version_extra;/**< version extra info or null = "PACKAGE_EXTRA", for example, "beta" */ - char const * time; /**< build time = "__DATE__ __TIME__", for example, "Jan 7 2010 03:31:50" */ - char const * distro; /**< distro or null = "DISTRO", for example, "Debian" */ - char const * compiler; /**< compiler info or null, for example, "msvc 160040219" */ - char const * arch; /**< arch, for example, "1248 48 44 L OMP" */ - /* new info should be added at the end for version backwards-compatibility. */ -} sox_version_info_t; - -/** -Client API: -Global parameters (for effects & formats), returned from the sox_get_globals -function. -*/ -typedef struct sox_globals_t { -/* public: */ - unsigned verbosity; /**< messages are only written if globals.verbosity >= message.level */ - sox_output_message_handler_t output_message_handler; /**< client-specified message output callback */ - sox_bool repeatable; /**< true to use pre-determined timestamps and PRNG seed */ - - /** - Default size (in bytes) used by libSoX for blocks of sample data. - Plugins should use similarly-sized buffers to get best performance. - */ - size_t bufsiz; - - /** - Default size (in bytes) used by libSoX for blocks of input sample data. - Plugins should use similarly-sized buffers to get best performance. - */ - size_t input_bufsiz; - - sox_int32_t ranqd1; /**< Can be used to re-seed libSoX's PRNG */ - - char const * stdin_in_use_by; /**< Private: tracks the name of the handler currently using stdin */ - char const * stdout_in_use_by; /**< Private: tracks the name of the handler currently using stdout */ - char const * subsystem; /**< Private: tracks the name of the handler currently writing an output message */ - char * tmp_path; /**< Private: client-configured path to use for temporary files */ - sox_bool use_magic; /**< Private: true if client has requested use of 'magic' file-type detection */ - sox_bool use_threads; /**< Private: true if client has requested parallel effects processing */ -} sox_globals_t; - -/** -Client API: -Signal parameters; members should be set to SOX_UNSPEC (= 0) if unknown. -*/ -typedef struct sox_signalinfo_t { - sox_rate_t rate; /**< samples per second, 0 if unknown */ - unsigned channels; /**< number of sound channels, 0 if unknown */ - unsigned precision; /**< bits per sample, 0 if unknown */ - sox_uint64_t length; /**< samples * chans in file, 0 if unknown, -1 if unspecified */ - double * mult; /**< Effects headroom multiplier; may be null */ -} sox_signalinfo_t; - -/** -Client API: -Basic information about an encoding. -*/ -typedef struct sox_encodings_info_t { - sox_encodings_flags_t flags; /**< lossy once (lossy1), lossy twice (lossy2), or lossless (none). */ - char const * name; /**< encoding name. */ - char const * desc; /**< encoding description. */ -} sox_encodings_info_t; - -/** -Client API: -Encoding parameters. -*/ -typedef struct sox_encodinginfo_t { - sox_encoding_t encoding; /**< format of sample numbers */ - unsigned bits_per_sample;/**< 0 if unknown or variable; uncompressed value if lossless; compressed value if lossy */ - double compression; /**< compression factor (where applicable) */ - - /** - Should bytes be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_bytes; - - /** - Should nibbles be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_nibbles; - - /** - Should bits be reversed? If this is default during sox_open_read or - sox_open_write, libSoX will set them to either no or yes according to the - machine or format default. - */ - sox_option_t reverse_bits; - - /** - If set to true, the format should reverse its default endianness. - */ - sox_bool opposite_endian; -} sox_encodinginfo_t; - -/** -Client API: -Looping parameters (out-of-band data). -*/ -typedef struct sox_loopinfo_t { - sox_uint64_t start; /**< first sample */ - sox_uint64_t length; /**< length */ - unsigned count; /**< number of repeats, 0=forever */ - unsigned char type; /**< 0=no, 1=forward, 2=forward/back (see sox_loop_* for valid values). */ -} sox_loopinfo_t; - -/** -Client API: -Instrument information. -*/ -typedef struct sox_instrinfo_t{ - signed char MIDInote; /**< for unity pitch playback */ - signed char MIDIlow; /**< MIDI pitch-bend low range */ - signed char MIDIhi; /**< MIDI pitch-bend high range */ - unsigned char loopmode; /**< 0=no, 1=forward, 2=forward/back (see sox_loop_* values) */ - unsigned nloops; /**< number of active loops (max SOX_MAX_NLOOPS). */ -} sox_instrinfo_t; - -/** -Client API: -File buffer info. Holds info so that data can be read in blocks. -*/ -typedef struct sox_fileinfo_t { - char *buf; /**< Pointer to data buffer */ - size_t size; /**< Size of buffer in bytes */ - size_t count; /**< Count read into buffer */ - size_t pos; /**< Position in buffer */ -} sox_fileinfo_t; - -/** -Client API: -Handler structure defined by each format. -*/ -struct sox_format_handler_t { - unsigned sox_lib_version_code; /**< Checked on load; must be 1st in struct*/ - char const * description; /**< short description of format */ - char const * const * names; /**< null-terminated array of filename extensions that are handled by this format */ - unsigned int flags; /**< File flags (SOX_FILE_* values). */ - sox_format_handler_startread startread; /**< called to initialize reader (decoder) */ - sox_format_handler_read read; /**< called to read (decode) a block of samples */ - sox_format_handler_stopread stopread; /**< called to close reader (decoder); may be null if no closing necessary */ - sox_format_handler_startwrite startwrite; /**< called to initialize writer (encoder) */ - sox_format_handler_write write; /**< called to write (encode) a block of samples */ - sox_format_handler_stopwrite stopwrite; /**< called to close writer (decoder); may be null if no closing necessary */ - sox_format_handler_seek seek; /**< called to reposition reader; may be null if not supported */ - - /** - Array of values indicating the encodings and precisions supported for - writing (encoding). Precisions specified with default precision first. - Encoding, precision, precision, ..., 0, repeat. End with one more 0. - Example: - unsigned const * formats = { - SOX_ENCODING_SIGN2, 16, 24, 0, // Support SIGN2 at 16 and 24 bits, default to 16 bits. - SOX_ENCODING_UNSIGNED, 8, 0, // Support UNSIGNED at 8 bits, default to 8 bits. - 0 // No more supported encodings. - }; - */ - unsigned const * write_formats; - - /** - Array of sample rates (samples per second) supported for writing (encoding). - NULL if all (or almost all) rates are supported. End with 0. - */ - sox_rate_t const * write_rates; - - /** - SoX will automatically allocate a buffer in which the handler can store data. - Specify the size of the buffer needed here. Usually this will be sizeof(your_struct). - The buffer will be allocated and zeroed before the call to startread/startwrite. - The buffer will be freed after the call to stopread/stopwrite. - The buffer will be provided via format.priv in each call to the handler. - */ - size_t priv_size; -}; - -/** -Client API: -Comments, instrument info, loop info (out-of-band data). -*/ -typedef struct sox_oob_t{ - /* Decoded: */ - sox_comments_t comments; /**< Comment strings in id=value format. */ - sox_instrinfo_t instr; /**< Instrument specification */ - sox_loopinfo_t loops[SOX_MAX_NLOOPS]; /**< Looping specification */ - - /* TBD: Non-decoded chunks, etc: */ -} sox_oob_t; - -/** -Client API: -Data passed to/from the format handler -*/ -struct sox_format_t { - char * filename; /**< File name */ - - /** - Signal specifications for reader (decoder) or writer (encoder): - sample rate, number of channels, precision, length, headroom multiplier. - Any info specified by the user is here on entry to startread or - startwrite. Info will be SOX_UNSPEC if the user provided no info. - At exit from startread, should be completely filled in, using - either data from the file's headers (if available) or whatever - the format is guessing/assuming (if header data is not available). - At exit from startwrite, should be completely filled in, using - either the data that was specified, or values chosen by the format - based on the format's defaults or capabilities. - */ - sox_signalinfo_t signal; - - /** - Encoding specifications for reader (decoder) or writer (encoder): - encoding (sample format), bits per sample, compression rate, endianness. - Should be filled in by startread. Values specified should be used - by startwrite when it is configuring the encoding parameters. - */ - sox_encodinginfo_t encoding; - - char * filetype; /**< Type of file, as determined by header inspection or libmagic. */ - sox_oob_t oob; /**< comments, instrument info, loop info (out-of-band data) */ - sox_bool seekable; /**< Can seek on this file */ - char mode; /**< Read or write mode ('r' or 'w') */ - sox_uint64_t olength; /**< Samples * chans written to file */ - sox_uint64_t clips; /**< Incremented if clipping occurs */ - int sox_errno; /**< Failure error code */ - char sox_errstr[256]; /**< Failure error text */ - void * fp; /**< File stream pointer */ - lsx_io_type io_type; /**< Stores whether this is a file, pipe or URL */ - sox_uint64_t tell_off; /**< Current offset within file */ - sox_uint64_t data_start; /**< Offset at which headers end and sound data begins (set by lsx_check_read_params) */ - sox_format_handler_t handler; /**< Format handler for this file */ - void * priv; /**< Format handler's private data area */ -}; - -/** -Client API: -Information about a loaded format handler, including the format name and a -function pointer that can be invoked to get additional information about the -format. -*/ -typedef struct sox_format_tab_t { - char *name; /**< Name of format handler */ - sox_format_fn_t fn; /**< Function to call to get format handler's information */ -} sox_format_tab_t; - -/** -Client API: -Global parameters for effects. -*/ -typedef struct sox_effects_globals_t { - sox_plot_t plot; /**< To help the user choose effect & options */ - sox_globals_t * global_info; /**< Pointer to associated SoX globals */ -} sox_effects_globals_t; - -/** -Client API: -Effect handler information. -*/ -struct sox_effect_handler_t { - char const * name; /**< Effect name */ - char const * usage; /**< Short explanation of parameters accepted by effect */ - unsigned int flags; /**< Combination of SOX_EFF_* flags */ - sox_effect_handler_getopts getopts; /**< Called to parse command-line arguments (called once per effect). */ - sox_effect_handler_start start; /**< Called to initialize effect (called once per flow). */ - sox_effect_handler_flow flow; /**< Called to process samples. */ - sox_effect_handler_drain drain; /**< Called to finish getting output after input is complete. */ - sox_effect_handler_stop stop; /**< Called to shut down effect (called once per flow). */ - sox_effect_handler_kill kill; /**< Called to shut down effect (called once per effect). */ - size_t priv_size; /**< Size of private data SoX should pre-allocate for effect */ -}; - -/** -Client API: -Effect information. -*/ -struct sox_effect_t { - sox_effects_globals_t * global_info; /**< global effect parameters */ - sox_signalinfo_t in_signal; /**< Information about the incoming data stream */ - sox_signalinfo_t out_signal; /**< Information about the outgoing data stream */ - sox_encodinginfo_t const * in_encoding; /**< Information about the incoming data encoding */ - sox_encodinginfo_t const * out_encoding; /**< Information about the outgoing data encoding */ - sox_effect_handler_t handler; /**< The handler for this effect */ - sox_sample_t * obuf; /**< output buffer */ - size_t obeg; /**< output buffer: start of valid data section */ - size_t oend; /**< output buffer: one past valid data section (oend-obeg is length of current content) */ - size_t imin; /**< minimum input buffer content required for calling this effect's flow function; set via lsx_effect_set_imin() */ - sox_uint64_t clips; /**< increment if clipping occurs */ - size_t flows; /**< 1 if MCHAN, number of chans otherwise */ - size_t flow; /**< flow number */ - void * priv; /**< Effect's private data area (each flow has a separate copy) */ -}; - -/** -Client API: -Chain of effects to be applied to a stream. -*/ -typedef struct sox_effects_chain_t { - sox_effect_t **effects; /**< Table of effects to be applied to a stream */ - unsigned table_size; /**< Number of entries in effects table */ - unsigned length; /**< Number of effects to be applied */ - sox_sample_t **ibufc; /**< Channel interleave buffer */ - sox_sample_t **obufc; /**< Channel interleave buffer */ - sox_effects_globals_t global_info; /**< Copy of global effects settings */ - sox_encodinginfo_t const * in_enc; /**< Input encoding */ - sox_encodinginfo_t const * out_enc; /**< Output encoding */ -} sox_effects_chain_t; - -/***************************************************************************** -Functions: -*****************************************************************************/ - -/** -Client API: -Returns version number string of libSoX, for example, "14.4.0". -@returns The version number string of libSoX, for example, "14.4.0". -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -sox_version(void); - -/** -Client API: -Returns information about this build of libsox. -@returns Pointer to a version information structure. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_version_info_t const * -LSX_API -sox_version_info(void); - -/** -Client API: -Returns a pointer to the structure with libSoX's global settings. -@returns a pointer to the structure with libSoX's global settings. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_globals_t * -LSX_API -sox_get_globals(void); - -/** -Client API: -Deprecated macro that returns the structure with libSoX's global settings -as an lvalue. -*/ -#define sox_globals (*sox_get_globals()) - -/** -Client API: -Returns a pointer to the list of available encodings. -End of list indicated by name == NULL. -@returns pointer to the list of available encodings. -*/ -LSX_RETURN_ARRAY LSX_RETURN_PURE -sox_encodings_info_t const * -LSX_API -sox_get_encodings_info(void); - -/** -Client API: -Deprecated macro that returns the list of available encodings. -End of list indicated by name == NULL. -*/ -#define sox_encodings_info (sox_get_encodings_info()) - -/** -Client API: -Fills in an encodinginfo with default values. -*/ -void -LSX_API -sox_init_encodinginfo( - LSX_PARAM_OUT sox_encodinginfo_t * e /**< Pointer to uninitialized encoding info structure to be initialized. */ - ); - -/** -Client API: -Given an encoding (for example, SIGN2) and the encoded bits_per_sample (for -example, 16), returns the number of useful bits per sample in the decoded data -(for example, 16), or returns 0 to indicate that the value returned by the -format handler should be used instead of a pre-determined precision. -@returns the number of useful bits per sample in the decoded data (for example -16), or returns 0 to indicate that the value returned by the format handler -should be used instead of a pre-determined precision. -*/ -LSX_RETURN_PURE -unsigned -LSX_API -sox_precision( - sox_encoding_t encoding, /**< Encoding for which to lookup precision information. */ - unsigned bits_per_sample /**< The number of encoded bits per sample. */ - ); - -/** -Client API: -Returns the number of items in the metadata block. -@returns the number of items in the metadata block. -*/ -size_t -LSX_API -sox_num_comments( - LSX_PARAM_IN_OPT sox_comments_t comments /**< Metadata block. */ - ); - -/** -Client API: -Adds an "id=value" item to the metadata block. -*/ -void -LSX_API -sox_append_comment( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NOTNULL sox_comments_t * comments, /**< Metadata block. */ - LSX_PARAM_IN_Z char const * item /**< Item to be added in "id=value" format. */ - ); - -/** -Client API: -Adds a newline-delimited list of "id=value" items to the metadata block. -*/ -void -LSX_API -sox_append_comments( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NOTNULL sox_comments_t * comments, /**< Metadata block. */ - LSX_PARAM_IN_Z char const * items /**< Newline-separated list of items to be added, for example "id1=value1\\nid2=value2". */ - ); - -/** -Client API: -Duplicates the metadata block. -@returns the copied metadata block. -*/ -LSX_RETURN_OPT -sox_comments_t -LSX_API -sox_copy_comments( - LSX_PARAM_IN_OPT sox_comments_t comments /**< Metadata block to copy. */ - ); - -/** -Client API: -Frees the metadata block. -*/ -void -LSX_API -sox_delete_comments( - LSX_PARAM_DEREF_PRE_MAYBENULL LSX_PARAM_DEREF_POST_NULL sox_comments_t * comments /**< Metadata block. */ - ); - -/** -Client API: -If "id=value" is found, return value, else return null. -@returns value, or null if value not found. -*/ -LSX_RETURN_OPT -char const * -LSX_API -sox_find_comment( - LSX_PARAM_IN_OPT sox_comments_t comments, /**< Metadata block in which to search. */ - LSX_PARAM_IN_Z char const * id /**< Id for which to search */ - ); - -/** -Client API: -Find and load format handler plugins. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_format_init(void); - -/** -Client API: -Unload format handler plugins. -*/ -void -LSX_API -sox_format_quit(void); - -/** -Client API: -Initialize effects library. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_init(void); - -/** -Client API: -Close effects library and unload format handler plugins. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_quit(void); - -/** -Client API: -Returns the table of format handler names and functions. -@returns the table of format handler names and functions. -*/ -LSX_RETURN_ARRAY LSX_RETURN_PURE -sox_format_tab_t const * -LSX_API -sox_get_format_fns(void); - -/** -Client API: -Deprecated macro that returns the table of format handler names and functions. -*/ -#define sox_format_fns (sox_get_format_fns()) - -/** -Client API: -Opens a decoding session for a file. Returned handle must be closed with sox_close(). -@returns The handle for the new session, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_read( - LSX_PARAM_IN_Z char const * path, /**< Path to file to be opened (required). */ - LSX_PARAM_IN_OPT sox_signalinfo_t const * signal, /**< Information already known about audio stream, or NULL if none. */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information already known about sample encoding, or NULL if none. */ - LSX_PARAM_IN_OPT_Z char const * filetype /**< Previously-determined file type, or NULL to auto-detect. */ - ); - -/** -Client API: -Opens a decoding session for a memory buffer. Returned handle must be closed with sox_close(). -@returns The handle for the new session, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_mem_read( - LSX_PARAM_IN_BYTECOUNT(buffer_size) void * buffer, /**< Pointer to audio data buffer (required). */ - size_t buffer_size,/**< Number of bytes to read from audio data buffer. */ - LSX_PARAM_IN_OPT sox_signalinfo_t const * signal, /**< Information already known about audio stream, or NULL if none. */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information already known about sample encoding, or NULL if none. */ - LSX_PARAM_IN_OPT_Z char const * filetype /**< Previously-determined file type, or NULL to auto-detect. */ - ); - -/** -Client API: -Returns true if the format handler for the specified file type supports the specified encoding. -@returns true if the format handler for the specified file type supports the specified encoding. -*/ -sox_bool -LSX_API -sox_format_supports_encoding( - LSX_PARAM_IN_OPT_Z char const * path, /**< Path to file to be examined (required if filetype is NULL). */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to use extension from path. */ - LSX_PARAM_IN sox_encodinginfo_t const * encoding /**< Encoding for which format handler should be queried. */ - ); - -/** -Client API: -Gets the format handler for a specified file type. -@returns The found format handler, or null if not found. -*/ -LSX_RETURN_OPT -sox_format_handler_t const * -LSX_API -sox_write_handler( - LSX_PARAM_IN_OPT_Z char const * path, /**< Path to file (required if filetype is NULL). */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Filetype for which handler is needed, or NULL to use extension from path. */ - LSX_PARAM_OUT_OPT char const * * filetype1 /**< Receives the filetype that was detected. Pass NULL if not needed. */ - ); - -/** -Client API: -Opens an encoding session for a file. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_write( - LSX_PARAM_IN_Z char const * path, /**< Path to file to be written (required). */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob, /**< Out-of-band data to add to file, or NULL if none. */ - LSX_PARAM_IN_OPT sox_bool (LSX_API * overwrite_permitted)(LSX_PARAM_IN_Z char const * filename) /**< Called if file exists to determine whether overwrite is ok. */ - ); - -/** -Client API: -Opens an encoding session for a memory buffer. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_mem_write( - LSX_PARAM_OUT_BYTECAP(buffer_size) void * buffer, /**< Pointer to audio data buffer that receives data (required). */ - LSX_PARAM_IN size_t buffer_size, /**< Maximum number of bytes to write to audio data buffer. */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob /**< Out-of-band data to add to file, or NULL if none. */ - ); - -/** -Client API: -Opens an encoding session for a memstream buffer. Returned handle must be closed with sox_close(). -@returns The new session handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_format_t * -LSX_API -sox_open_memstream_write( - LSX_PARAM_OUT char * * buffer_ptr, /**< Receives pointer to audio data buffer that receives data (required). */ - LSX_PARAM_OUT size_t * buffer_size_ptr, /**< Receives size of data written to audio data buffer (required). */ - LSX_PARAM_IN sox_signalinfo_t const * signal, /**< Information about desired audio stream (required). */ - LSX_PARAM_IN_OPT sox_encodinginfo_t const * encoding, /**< Information about desired sample encoding, or NULL to use defaults. */ - LSX_PARAM_IN_OPT_Z char const * filetype, /**< Previously-determined file type, or NULL to auto-detect. */ - LSX_PARAM_IN_OPT sox_oob_t const * oob /**< Out-of-band data to add to file, or NULL if none. */ - ); - -/** -Client API: -Reads samples from a decoding session into a sample buffer. -@returns Number of samples decoded, or 0 for EOF. -*/ -size_t -LSX_API -sox_read( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_OUT_CAP_POST_COUNT(len,return) sox_sample_t *buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Writes samples to an encoding session from a sample buffer. -@returns Number of samples encoded. -*/ -size_t -LSX_API -sox_write( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - LSX_PARAM_IN_COUNT(len) sox_sample_t const * buf, /**< Buffer from which to read samples. */ - size_t len /**< Number of samples available in buf. */ - ); - -/** -Client API: -Closes an encoding or decoding session. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_close( - LSX_PARAM_INOUT sox_format_t * ft /**< Format pointer. */ - ); - -/** -Client API: -Sets the location at which next samples will be decoded. Returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_seek( - LSX_PARAM_INOUT sox_format_t * ft, /**< Format pointer. */ - sox_uint64_t offset, /**< Sample offset at which to position reader. */ - int whence /**< Set to SOX_SEEK_SET. */ - ); - -/** -Client API: -Finds a format handler by name. -@returns Format handler data, or null if not found. -*/ -LSX_RETURN_OPT -sox_format_handler_t const * -LSX_API -sox_find_format( - LSX_PARAM_IN_Z char const * name, /**< Name of format handler to find. */ - sox_bool ignore_devices /**< Set to true to ignore device names. */ - ); - -/** -Client API: -Returns global parameters for effects -@returns global parameters for effects. -*/ -LSX_RETURN_VALID LSX_RETURN_PURE -sox_effects_globals_t * -LSX_API -sox_get_effects_globals(void); - -/** -Client API: -Deprecated macro that returns global parameters for effects. -*/ -#define sox_effects_globals (*sox_get_effects_globals()) - -/** -Client API: -Finds the effect handler with the given name. -@returns Effect pointer, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -sox_effect_handler_t const * -LSX_API -sox_find_effect( - LSX_PARAM_IN_Z char const * name /**< Name of effect to find. */ - ); - -/** -Client API: -Creates an effect using the given handler. -@returns The new effect, or null if not found. -*/ -LSX_RETURN_OPT -sox_effect_t * -LSX_API -sox_create_effect( - LSX_PARAM_IN sox_effect_handler_t const * eh /**< Handler to use for effect. */ - ); - -/** -Client API: -Applies the command-line options to the effect. -@returns the number of arguments consumed. -*/ -int -LSX_API -sox_effect_options( - LSX_PARAM_IN sox_effect_t *effp, /**< Effect pointer on which to set options. */ - int argc, /**< Number of arguments in argv. */ - LSX_PARAM_IN_COUNT(argc) char * const argv[] /**< Array of command-line options. */ - ); - -/** -Client API: -Returns an array containing the known effect handlers. -@returns An array containing the known effect handlers. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -sox_effect_fn_t const * -LSX_API -sox_get_effect_fns(void); - -/** -Client API: -Deprecated macro that returns an array containing the known effect handlers. -*/ -#define sox_effect_fns (sox_get_effect_fns()) - -/** -Client API: -Initializes an effects chain. Returned handle must be closed with sox_delete_effects_chain(). -@returns Handle, or null on failure. -*/ -LSX_RETURN_OPT -sox_effects_chain_t * -LSX_API -sox_create_effects_chain( - LSX_PARAM_IN sox_encodinginfo_t const * in_enc, /**< Input encoding. */ - LSX_PARAM_IN sox_encodinginfo_t const * out_enc /**< Output encoding. */ - ); - -/** -Client API: -Closes an effects chain. -*/ -void -LSX_API -sox_delete_effects_chain( - LSX_PARAM_INOUT sox_effects_chain_t *ecp /**< Effects chain pointer. */ - ); - -/** -Client API: -Adds an effect to the effects chain, returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_add_effect( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to which effect should be added . */ - LSX_PARAM_INOUT sox_effect_t * effp, /**< Effect to be added. */ - LSX_PARAM_INOUT sox_signalinfo_t * in, /**< Input format. */ - LSX_PARAM_IN sox_signalinfo_t const * out /**< Output format. */ - ); - -/** -Client API: -Runs the effects chain, returns SOX_SUCCESS if successful. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_flow_effects( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to run. */ - LSX_PARAM_IN_OPT sox_flow_effects_callback callback, /**< Callback for monitoring flow progress. */ - LSX_PARAM_IN_OPT void * client_data /**< Data to pass into callback. */ - ); - -/** -Client API: -Gets the number of clips that occurred while running an effects chain. -@returns the number of clips that occurred while running an effects chain. -*/ -sox_uint64_t -LSX_API -sox_effects_clips( - LSX_PARAM_IN sox_effects_chain_t * chain /**< Effects chain from which to read clip information. */ - ); - -/** -Client API: -Shuts down an effect (calls stop on each of its flows). -@returns the number of clips from all flows. -*/ -sox_uint64_t -LSX_API -sox_stop_effect( - LSX_PARAM_INOUT_COUNT(effp->flows) sox_effect_t * effp /**< Effect to stop. */ - ); - -/** -Client API: -Adds an already-initialized effect to the end of the chain. -*/ -void -LSX_API -sox_push_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t * chain, /**< Effects chain to which effect should be added. */ - LSX_PARAM_INOUT sox_effect_t * effp /**< Effect to be added. */ - ); - -/** -Client API: -Removes and returns an effect from the end of the chain. -@returns the removed effect, or null if no effects. -*/ -LSX_RETURN_OPT -sox_effect_t * -LSX_API -sox_pop_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to remove an effect. */ - ); - -/** -Client API: -Shut down and delete an effect. -*/ -void -LSX_API -sox_delete_effect( - LSX_PARAM_INOUT_COUNT(effp->flows) sox_effect_t *effp /**< Effect to be deleted. */ - ); - -/** -Client API: -Shut down and delete the last effect in the chain. -*/ -void -LSX_API -sox_delete_effect_last( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to remove the last effect. */ - ); - -/** -Client API: -Shut down and delete all effects in the chain. -*/ -void -LSX_API -sox_delete_effects( - LSX_PARAM_INOUT sox_effects_chain_t *chain /**< Effects chain from which to delete effects. */ - ); - -/** -Client API: -Gets the sample offset of the start of the trim, useful for efficiently -skipping the part that will be trimmed anyway (get trim start, seek, then -clear trim start). -@returns the sample offset of the start of the trim. -*/ -sox_uint64_t -LSX_API -sox_trim_get_start( - LSX_PARAM_IN sox_effect_t * effp /**< Trim effect. */ - ); - -/** -Client API: -Clears the start of the trim to 0. -*/ -void -LSX_API -sox_trim_clear_start( - LSX_PARAM_INOUT sox_effect_t * effp /**< Trim effect. */ - ); - -/** -Client API: -Returns true if the specified file is a known playlist file type. -@returns true if the specified file is a known playlist file type. -*/ -sox_bool -LSX_API -sox_is_playlist( - LSX_PARAM_IN_Z char const * filename /**< Name of file to examine. */ - ); - -/** -Client API: -Parses the specified playlist file. -@returns SOX_SUCCESS if successful. -*/ -int -LSX_API -sox_parse_playlist( - LSX_PARAM_IN sox_playlist_callback_t callback, /**< Callback to call for each item in the playlist. */ - void * p, /**< Data to pass to callback. */ - LSX_PARAM_IN char const * const listname /**< Filename of playlist file. */ - ); - -/** -Client API: -Converts a SoX error code into an error string. -@returns error string corresponding to the specified error code, -or a generic message if the error code is not recognized. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -sox_strerror( - int sox_errno /**< Error code to look up. */ - ); - -/** -Client API: -Gets the basename of the specified file; for example, the basename of -"/a/b/c.d" would be "c". -@returns the number of characters written to base_buffer, excluding the null, -or 0 on failure. -*/ -size_t -LSX_API -sox_basename( - LSX_PARAM_OUT_Z_CAP_POST_COUNT(base_buffer_len,return) char * base_buffer, /**< Buffer into which basename should be written. */ - size_t base_buffer_len, /**< Size of base_buffer, in bytes. */ - LSX_PARAM_IN_Z char const * filename /**< Filename from which to extract basename. */ - ); - -/***************************************************************************** -Internal API: -WARNING - The items in this section are subject to instability. They only -exist in the public header because sox (the application) currently uses them. -These may be changed or removed in future versions of libSoX. -*****************************************************************************/ - -/** -Plugins API: -Print a fatal error in libSoX. -*/ -void -LSX_API -lsx_fail_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print a warning in libSoX. -*/ -void -LSX_API -lsx_warn_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print an informational message in libSoX. -*/ -void -LSX_API -lsx_report_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Print a debug message in libSoX. -*/ -void -LSX_API -lsx_debug_impl( - LSX_PARAM_IN_PRINTF char const * fmt, /**< printf-style format string. */ - ...) - LSX_PRINTF12; - -/** -Plugins API: -Report a fatal error in libSoX; printf-style arguments must follow. -*/ -#define lsx_fail sox_get_globals()->subsystem=__FILE__,lsx_fail_impl - -/** -Plugins API: -Report a warning in libSoX; printf-style arguments must follow. -*/ -#define lsx_warn sox_get_globals()->subsystem=__FILE__,lsx_warn_impl - -/** -Plugins API: -Report an informational message in libSoX; printf-style arguments must follow. -*/ -#define lsx_report sox_get_globals()->subsystem=__FILE__,lsx_report_impl - -/** -Plugins API: -Report a debug message in libSoX; printf-style arguments must follow. -*/ -#define lsx_debug sox_get_globals()->subsystem=__FILE__,lsx_debug_impl - -/** -Plugins API: -String name and integer values for enumerated types (type metadata), for use -with LSX_ENUM_ITEM, lsx_find_enum_text, and lsx_find_enum_value. -*/ -typedef struct lsx_enum_item { - char const *text; /**< String name of enumeration. */ - unsigned value; /**< Integer value of enumeration. */ -} lsx_enum_item; - -/** -Plugins API: -Declares a static instance of an lsx_enum_item structure in format -{ "item", prefixitem }, for use in declaring lsx_enum_item[] arrays. -@param prefix The prefix to prepend to the item in the enumeration symbolic name. -@param item The user-visible text name of the item (must also be a valid C symbol name). -*/ -#define LSX_ENUM_ITEM(prefix, item) {#item, prefix##item}, - -/** -Plugins API: -Flags for use with lsx_find_enum_item. -*/ -enum -{ - lsx_find_enum_item_none = 0, /**< Default parameters (case-insensitive). */ - lsx_find_enum_item_case_sensitive = 1 /**< Enable case-sensitive search. */ -}; - -/** -Plugins API: -Looks up an enumeration by name in an array of lsx_enum_items. -@returns the corresponding item, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -lsx_enum_item const * -LSX_API -lsx_find_enum_text( - LSX_PARAM_IN_Z char const * text, /**< Name of enumeration to find. */ - LSX_PARAM_IN lsx_enum_item const * lsx_enum_items, /**< Array of items to search, with text == NULL for last item. */ - int flags /**< Search flags: 0 (case-insensitive) or lsx_find_enum_item_case_sensitive (case-sensitive). */ - ); - -/** -Plugins API: -Looks up an enumeration by value in an array of lsx_enum_items. -@returns the corresponding item, or null if not found. -*/ -LSX_RETURN_OPT LSX_RETURN_PURE -lsx_enum_item const * -LSX_API -lsx_find_enum_value( - unsigned value, /**< Enumeration value to find. */ - LSX_PARAM_IN lsx_enum_item const * lsx_enum_items /**< Array of items to search, with text == NULL for last item. */ - ); - -/** -Plugins API: -Looks up a command-line argument in a set of enumeration names, showing an -error message if the argument is not found in the set of names. -@returns The enumeration value corresponding to the matching enumeration, or -INT_MAX if the argument does not match any enumeration name. -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_enum_option( - int c, /**< Option character to which arg is associated, for example with -a, c would be 'a'. */ - LSX_PARAM_IN_Z char const * arg, /**< Argument to find in enumeration list. */ - LSX_PARAM_IN lsx_enum_item const * items /**< Array of items to search, with text == NULL for last item. */ - ); - -/** -Plugins API: -Determines whether the specified string ends with the specified suffix (case-sensitive). -@returns true if the specified string ends with the specified suffix. -*/ -LSX_RETURN_PURE -sox_bool -LSX_API -lsx_strends( - LSX_PARAM_IN_Z char const * str, /**< String to search. */ - LSX_PARAM_IN_Z char const * end /**< Suffix to search for. */ - ); - -/** -Plugins API: -Finds the file extension for a filename. -@returns the file extension, not including the '.', or null if filename does -not have an extension. -*/ -LSX_RETURN_VALID_Z LSX_RETURN_PURE -char const * -LSX_API -lsx_find_file_extension( - LSX_PARAM_IN_Z char const * pathname /**< Filename to search for extension. */ - ); - -/** -Plugins API: -Formats the specified number with up to three significant figures and adds a -metric suffix in place of the exponent, such as 1.23G. -@returns A static buffer with the formatted number, valid until the next time -this function is called (note: not thread safe). -*/ -LSX_RETURN_VALID_Z -char const * -LSX_API -lsx_sigfigs3( - double number /**< Number to be formatted. */ - ); - -/** -Plugins API: -Formats the specified number as a percentage, showing up to three significant -figures. -@returns A static buffer with the formatted number, valid until the next time -this function is called (note: not thread safe). -*/ -LSX_RETURN_VALID_Z -char const * -LSX_API -lsx_sigfigs3p( - double percentage /**< Number to be formatted. */ - ); - -/** -Plugins API: -Allocates, deallocates, or resizes; like C's realloc, except that this version -terminates the running application if unable to allocate the requested memory. -@returns New buffer, or null if buffer was freed. -*/ -LSX_RETURN_OPT -void * -LSX_API -lsx_realloc( - LSX_PARAM_IN_OPT void *ptr, /**< Pointer to be freed or resized, or null if allocating a new buffer. */ - size_t newsize /**< New size for buffer, or 0 to free the buffer. */ - ); - -/** -Plugins API: -Like strcmp, except that the characters are compared without regard to case. -@returns 0 (s1 == s2), negative (s1 < s2), or positive (s1 > s2). -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_strcasecmp( - LSX_PARAM_IN_Z char const * s1, /**< First string. */ - LSX_PARAM_IN_Z char const * s2 /**< Second string. */ - ); - - -/** -Plugins API: -Like strncmp, except that the characters are compared without regard to case. -@returns 0 (s1 == s2), negative (s1 < s2), or positive (s1 > s2). -*/ -LSX_RETURN_PURE -int -LSX_API -lsx_strncasecmp( - LSX_PARAM_IN_Z char const * s1, /**< First string. */ - LSX_PARAM_IN_Z char const * s2, /**< Second string. */ - size_t n /**< Maximum number of characters to examine. */ - ); - -/** -Plugins API: -Is option argument unsupported, required, or optional. -*/ -typedef enum lsx_option_arg_t { - lsx_option_arg_none, /**< Option does not have an argument. */ - lsx_option_arg_required, /**< Option requires an argument. */ - lsx_option_arg_optional /**< Option can optionally be followed by an argument. */ -} lsx_option_arg_t; - -/** -Plugins API: -lsx_getopt_init options. -*/ -typedef enum lsx_getopt_flags_t { - lsx_getopt_flag_none = 0, /**< no flags (no output, not long-only) */ - lsx_getopt_flag_opterr = 1, /**< if set, invalid options trigger lsx_warn output */ - lsx_getopt_flag_longonly = 2 /**< if set, recognize -option as a long option */ -} lsx_getopt_flags_t; - -/** -Plugins API: -lsx_getopt long option descriptor. -*/ -typedef struct lsx_option_t { - char const * name; /**< Name of the long option. */ - lsx_option_arg_t has_arg; /**< Whether the long option supports an argument and, if so, whether the argument is required or optional. */ - int * flag; /**< Flag to set if argument is present. */ - int val; /**< Value to put in flag if argument is present. */ -} lsx_option_t; - -/** -Plugins API: -lsx_getopt session information (initialization data and state). -*/ -typedef struct lsx_getopt_t { - int argc; /**< IN argc: Number of arguments in argv */ - char * const * argv; /**< IN argv: Array of arguments */ - char const * shortopts;/**< IN shortopts: Short option characters */ - lsx_option_t const * longopts; /**< IN longopts: Array of long option descriptors */ - lsx_getopt_flags_t flags; /**< IN flags: Flags for longonly and opterr */ - char const * curpos; /**< INOUT curpos: Maintains state between calls to lsx_getopt */ - int ind; /**< INOUT optind: Maintains the index of next element to be processed */ - int opt; /**< OUT optopt: Receives the option character that caused error */ - char const * arg; /**< OUT optarg: Receives the value of the option's argument */ - int lngind; /**< OUT lngind: Receives the index of the matched long option or -1 if not a long option */ -} lsx_getopt_t; - -/** -Plugins API: -Initializes an lsx_getopt_t structure for use with lsx_getopt. -*/ -void -LSX_API -lsx_getopt_init( - LSX_PARAM_IN int argc, /**< Number of arguments in argv */ - LSX_PARAM_IN_COUNT(argc) char * const * argv, /**< Array of arguments */ - LSX_PARAM_IN_Z char const * shortopts, /**< Short options, for example ":abc:def::ghi" (+/- not supported) */ - LSX_PARAM_IN_OPT lsx_option_t const * longopts, /**< Array of long option descriptors */ - LSX_PARAM_IN lsx_getopt_flags_t flags, /**< Flags for longonly and opterr */ - LSX_PARAM_IN int first, /**< First argv to check (usually 1) */ - LSX_PARAM_OUT lsx_getopt_t * state /**< State object to be initialized */ - ); - -/** -Plugins API: -Gets the next option. Options are parameters that start with "-" or "--". -If no more options, returns -1. If unrecognized short option, returns '?'. -If a recognized short option is missing a required argument, -return (shortopts[0]==':' ? ':' : '?'). If successfully recognized short -option, return the recognized character. If successfully recognized long -option, returns (option.flag ? 0 : option.val). -Note: lsx_getopt does not permute the non-option arguments. -@returns option character (short), val or 0 (long), or -1 (no more). -*/ -int -LSX_API -lsx_getopt( - LSX_PARAM_INOUT lsx_getopt_t * state /**< The getopt state pointer. */ - ); - -/* WARNING END */ - -#if defined(__cplusplus) -} -#endif - -#endif /* SOX_H */ diff --git a/freedv/tags/1.2.2/src/sox/sox_i.h b/freedv/tags/1.2.2/src/sox/sox_i.h deleted file mode 100644 index 9d533263..00000000 --- a/freedv/tags/1.2.2/src/sox/sox_i.h +++ /dev/null @@ -1,417 +0,0 @@ -/* libSoX Internal header - * - * This file is meant for libSoX internal use only - * - * Copyright 2001-2008 Chris Bagwell and SoX Contributors - * - * This source code is freely redistributable and may be used for - * any purpose. This copyright notice must be maintained. - * Chris Bagwell And SoX Contributors are not responsible for - * the consequences of using this software. - */ - -#ifndef SOX_I_H -#define SOX_I_H - -#include "soxomp.h" /* Note: soxomp.h includes soxconfig.h */ -#include "sox.h" - -#define __FREEDV__ - -#if defined HAVE_FMEMOPEN -#define _GNU_SOURCE -#endif - -#include -#include -#include - -#include "util.h" - -#if defined(LSX_EFF_ALIAS) -#undef lsx_debug -#undef lsx_fail -#undef lsx_report -#undef lsx_warn -#define lsx_debug sox_globals.subsystem=effp->handler.name,lsx_debug_impl -#define lsx_fail sox_globals.subsystem=effp->handler.name,lsx_fail_impl -#define lsx_report sox_globals.subsystem=effp->handler.name,lsx_report_impl -#define lsx_warn sox_globals.subsystem=effp->handler.name,lsx_warn_impl -#endif - -#define RANQD1 ranqd1(sox_globals.ranqd1) -#define DRANQD1 dranqd1(sox_globals.ranqd1) - -typedef enum {SOX_SHORT, SOX_INT, SOX_FLOAT, SOX_DOUBLE} sox_data_t; -typedef enum {SOX_WAVE_SINE, SOX_WAVE_TRIANGLE} lsx_wave_t; -lsx_enum_item const * lsx_get_wave_enum(void); - -/* Define fseeko and ftello for platforms lacking them */ -#ifndef HAVE_FSEEKO -#define fseeko fseek -#define ftello ftell -#endif - -#ifdef _FILE_OFFSET_BITS -assert_static(sizeof(off_t) == _FILE_OFFSET_BITS >> 3, OFF_T_BUILD_PROBLEM); -#endif - -FILE * lsx_tmpfile(void); - -void lsx_debug_more_impl(char const * fmt, ...) LSX_PRINTF12; -void lsx_debug_most_impl(char const * fmt, ...) LSX_PRINTF12; - -#define lsx_debug_more sox_get_globals()->subsystem=__FILE__,lsx_debug_more_impl -#define lsx_debug_most sox_get_globals()->subsystem=__FILE__,lsx_debug_most_impl - -/* Digitise one cycle of a wave and store it as - * a table of samples of a specified data-type. - */ -void lsx_generate_wave_table( - lsx_wave_t wave_type, - sox_data_t data_type, - void * table, /* Really of type indicated by data_type. */ - size_t table_size, /* Number of points on the x-axis. */ - double min, /* Minimum value on the y-axis. (e.g. -1) */ - double max, /* Maximum value on the y-axis. (e.g. +1) */ - double phase); /* Phase at 1st point; 0..2pi. (e.g. pi/2 for cosine) */ -char const * lsx_parsesamples(sox_rate_t rate, const char *str, uint64_t *samples, int def); -int lsx_parse_note(char const * text, char * * end_ptr); -double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key); -#define lsx_parse_frequency(a, b) lsx_parse_frequency_k(a, b, INT_MAX) -FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename); - -void lsx_prepare_spline3(double const * x, double const * y, int n, - double start_1d, double end_1d, double * y_2d); -double lsx_spline3(double const * x, double const * y, double const * y_2d, - int n, double x1); - -double lsx_bessel_I_0(double x); -int lsx_set_dft_length(int num_taps); -void init_fft_cache(void); -void clear_fft_cache(void); -void lsx_safe_rdft(int len, int type, double * d); -void lsx_safe_cdft(int len, int type, double * d); -void lsx_power_spectrum(int n, double const * in, double * out); -void lsx_power_spectrum_f(int n, float const * in, float * out); -void lsx_apply_hann_f(float h[], const int num_points); -void lsx_apply_hann(double h[], const int num_points); -void lsx_apply_hamming(double h[], const int num_points); -void lsx_apply_bartlett(double h[], const int num_points); -void lsx_apply_blackman(double h[], const int num_points, double alpha); -void lsx_apply_blackman_nutall(double h[], const int num_points); -double lsx_kaiser_beta(double att); -void lsx_apply_kaiser(double h[], const int num_points, double beta); -double * lsx_make_lpf(int num_taps, double Fc, double beta, double scale, sox_bool dc_norm); -int lsx_lpf_num_taps(double att, double tr_bw, int k); -double * lsx_design_lpf( - double Fp, /* End of pass-band; ~= 0.01dB point */ - double Fc, /* Start of stop-band */ - double Fn, /* Nyquist freq; e.g. 0.5, 1, PI */ - sox_bool allow_aliasing, - double att, /* Stop-band attenuation in dB */ - int * num_taps, /* (Single phase.) 0: value will be estimated */ - int k); /* Number of phases; 0 for single-phase */ -void lsx_fir_to_phase(double * * h, int * len, - int * post_len, double phase0); -#define LSX_TO_6dB .5869 -#define LSX_TO_3dB ((2/3.) * (.5 + LSX_TO_6dB)) -#define LSX_MAX_TBW0 36. -#define LSX_MAX_TBW0A (LSX_MAX_TBW0 / (1 + LSX_TO_3dB)) -#define LSX_MAX_TBW3 floor(LSX_MAX_TBW0 * LSX_TO_3dB) -#define LSX_MAX_TBW3A floor(LSX_MAX_TBW0A * LSX_TO_3dB) -void lsx_plot_fir(double * h, int num_points, sox_rate_t rate, sox_plot_t type, char const * title, double y1, double y2); - -#ifdef HAVE_BYTESWAP_H -#include -#define lsx_swapw(x) bswap_16(x) -#define lsx_swapdw(x) bswap_32(x) -#elif defined(_MSC_VER) -#define lsx_swapw(x) _byteswap_ushort(x) -#define lsx_swapdw(x) _byteswap_ulong(x) -#else -#define lsx_swapw(uw) (((uw >> 8) | (uw << 8)) & 0xffff) -#define lsx_swapdw(udw) ((udw >> 24) | ((udw >> 8) & 0xff00) | ((udw << 8) & 0xff0000) | (udw << 24)) -#endif - - - -/*------------------------ Implemented in libsoxio.c -------------------------*/ - -/* Read and write basic data types from "ft" stream. */ -size_t lsx_readbuf(sox_format_t * ft, void *buf, size_t len); -int lsx_skipbytes(sox_format_t * ft, size_t n); -int lsx_padbytes(sox_format_t * ft, size_t n); -size_t lsx_writebuf(sox_format_t * ft, void const *buf, size_t len); -int lsx_reads(sox_format_t * ft, char *c, size_t len); -int lsx_writes(sox_format_t * ft, char const * c); -void lsx_set_signal_defaults(sox_format_t * ft); -#define lsx_writechars(ft, chars, len) (lsx_writebuf(ft, chars, len) == len? SOX_SUCCESS : SOX_EOF) - -size_t lsx_read_3_buf(sox_format_t * ft, sox_uint24_t *buf, size_t len); -size_t lsx_read_b_buf(sox_format_t * ft, uint8_t *buf, size_t len); -size_t lsx_read_df_buf(sox_format_t * ft, double *buf, size_t len); -size_t lsx_read_dw_buf(sox_format_t * ft, uint32_t *buf, size_t len); -size_t lsx_read_qw_buf(sox_format_t * ft, uint64_t *buf, size_t len); -size_t lsx_read_f_buf(sox_format_t * ft, float *buf, size_t len); -size_t lsx_read_w_buf(sox_format_t * ft, uint16_t *buf, size_t len); - -size_t lsx_write_3_buf(sox_format_t * ft, sox_uint24_t *buf, size_t len); -size_t lsx_write_b_buf(sox_format_t * ft, uint8_t *buf, size_t len); -size_t lsx_write_df_buf(sox_format_t * ft, double *buf, size_t len); -size_t lsx_write_dw_buf(sox_format_t * ft, uint32_t *buf, size_t len); -size_t lsx_write_qw_buf(sox_format_t * ft, uint64_t *buf, size_t len); -size_t lsx_write_f_buf(sox_format_t * ft, float *buf, size_t len); -size_t lsx_write_w_buf(sox_format_t * ft, uint16_t *buf, size_t len); - -int lsx_read3(sox_format_t * ft, sox_uint24_t * u3); -int lsx_readb(sox_format_t * ft, uint8_t * ub); -int lsx_readchars(sox_format_t * ft, char * chars, size_t len); -int lsx_readdf(sox_format_t * ft, double * d); -int lsx_readdw(sox_format_t * ft, uint32_t * udw); -int lsx_readqw(sox_format_t * ft, uint64_t * udw); -int lsx_readf(sox_format_t * ft, float * f); -int lsx_readw(sox_format_t * ft, uint16_t * uw); - -#if 1 /* FIXME: use defines */ -UNUSED static int lsx_readsb(sox_format_t * ft, int8_t * sb) -{return lsx_readb(ft, (uint8_t *)sb);} -UNUSED static int lsx_readsw(sox_format_t * ft, int16_t * sw) -{return lsx_readw(ft, (uint16_t *)sw);} -#else -#define lsx_readsb(ft, sb) lsx_readb(ft, (uint8_t *)sb) -#define lsx_readsw(ft, sw) lsx_readb(ft, (uint16_t *)sw) -#endif - -int lsx_write3(sox_format_t * ft, unsigned u3); -int lsx_writeb(sox_format_t * ft, unsigned ub); -int lsx_writedf(sox_format_t * ft, double d); -int lsx_writedw(sox_format_t * ft, unsigned udw); -int lsx_writeqw(sox_format_t * ft, uint64_t uqw); -int lsx_writef(sox_format_t * ft, double f); -int lsx_writew(sox_format_t * ft, unsigned uw); - -int lsx_writesb(sox_format_t * ft, signed); -int lsx_writesw(sox_format_t * ft, signed); - -int lsx_eof(sox_format_t * ft); -int lsx_error(sox_format_t * ft); -int lsx_flush(sox_format_t * ft); -int lsx_seeki(sox_format_t * ft, off_t offset, int whence); -int lsx_unreadb(sox_format_t * ft, unsigned ub); -uint64_t lsx_filelength(sox_format_t * ft); -off_t lsx_tell(sox_format_t * ft); -void lsx_clearerr(sox_format_t * ft); -void lsx_rewind(sox_format_t * ft); - -int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, off_t to_sample); - -void lsx_fail_errno(sox_format_t *, int, const char *, ...) -#ifdef __GNUC__ -__attribute__ ((format (printf, 3, 4))); -#else -; -#endif - -typedef struct sox_formats_globals { /* Global parameters (for formats) */ - sox_globals_t * global_info; -} sox_formats_globals; - - - -/*------------------------------ File Handlers -------------------------------*/ - -int lsx_check_read_params(sox_format_t * ft, unsigned channels, - sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, - uint64_t num_samples, sox_bool check_length); -#define LSX_FORMAT_HANDLER(name) \ -sox_format_handler_t const * lsx_##name##_format_fn(void); \ -sox_format_handler_t const * lsx_##name##_format_fn(void) -#define div_bits(size, bits) ((uint64_t)(size) * 8 / bits) - -/* Raw I/O */ -int lsx_rawstartread(sox_format_t * ft); -size_t lsx_rawread(sox_format_t * ft, sox_sample_t *buf, size_t nsamp); -int lsx_rawstopread(sox_format_t * ft); -int lsx_rawstartwrite(sox_format_t * ft); -size_t lsx_rawwrite(sox_format_t * ft, const sox_sample_t *buf, size_t nsamp); -int lsx_rawseek(sox_format_t * ft, uint64_t offset); -int lsx_rawstart(sox_format_t * ft, sox_bool default_rate, sox_bool default_channels, sox_bool default_length, sox_encoding_t encoding, unsigned bits_per_sample); -#define lsx_rawstartread(ft) lsx_rawstart(ft, sox_false, sox_false, sox_false, SOX_ENCODING_UNKNOWN, 0) -#define lsx_rawstartwrite lsx_rawstartread -#define lsx_rawstopread NULL -#define lsx_rawstopwrite NULL - -extern sox_format_handler_t const * lsx_sndfile_format_fn(void); - -char * lsx_cat_comments(sox_comments_t comments); - -/*--------------------------------- Effects ----------------------------------*/ - -int lsx_flow_copy(sox_effect_t * effp, const sox_sample_t * ibuf, - sox_sample_t * obuf, size_t * isamp, size_t * osamp); -int lsx_usage(sox_effect_t * effp); -char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n); -#define EFFECT(f) extern sox_effect_handler_t const * lsx_##f##_effect_fn(void); -#include "effects.h" -#undef EFFECT - -#define NUMERIC_PARAMETER(name, min, max) { \ - char * end_ptr; \ - double d; \ - if (argc == 0) break; \ - d = strtod(*argv, &end_ptr); \ - if (end_ptr != *argv) { \ - if (d < min || d > max || *end_ptr != '\0') {\ - lsx_fail("parameter `%s' must be between %g and %g", #name, (double)min, (double)max); \ - return lsx_usage(effp); \ - } \ - p->name = d; \ - --argc, ++argv; \ - } \ -} - -#define TEXTUAL_PARAMETER(name, enum_table) { \ - lsx_enum_item const * e; \ - if (argc == 0) break; \ - e = lsx_find_enum_text(*argv, enum_table, 0); \ - if (e != NULL) { \ - p->name = e->value; \ - --argc, ++argv; \ - } \ -} - -#define GETOPT_NUMERIC(state, ch, name, min, max) case ch:{ \ - char * end_ptr; \ - double d = strtod(state.arg, &end_ptr); \ - if (end_ptr == state.arg || d < min || d > max || *end_ptr != '\0') {\ - lsx_fail("parameter `%s' must be between %g and %g", #name, (double)min, (double)max); \ - return lsx_usage(effp); \ - } \ - p->name = d; \ - break; \ -} - -int lsx_effect_set_imin(sox_effect_t * effp, size_t imin); - -int lsx_effects_init(void); -int lsx_effects_quit(void); - -/*--------------------------------- Dynamic Library ----------------------------------*/ - -#if defined(HAVE_WIN32_LTDL_H) - #include "win32-ltdl.h" - #define HAVE_LIBLTDL 1 - typedef lt_dlhandle lsx_dlhandle; -#elif defined(HAVE_LIBLTDL) - #include - typedef lt_dlhandle lsx_dlhandle; -#else - struct lsx_dlhandle_tag; - typedef struct lsx_dlhandle_tag *lsx_dlhandle; -#endif - -typedef void (*lsx_dlptr)(void); - -typedef struct lsx_dlfunction_info -{ - const char* name; - lsx_dlptr static_func; - lsx_dlptr stub_func; -} lsx_dlfunction_info; - -int lsx_open_dllibrary( - int show_error_on_failure, - const char* library_description, - const char * const library_names[], - const lsx_dlfunction_info func_infos[], - lsx_dlptr selected_funcs[], - lsx_dlhandle* pdl); - -void lsx_close_dllibrary( - lsx_dlhandle dl); - -#define LSX_DLENTRIES_APPLY__(entries, f, x) entries(f, x) - -#define LSX_DLENTRY_TO_PTR__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - func_return (*func_ptr) func_args; - -#define LSX_DLENTRIES_TO_FUNCTIONS__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - func_return func_name func_args; - -/* LSX_DLENTRIES_TO_PTRS: Given an ENTRIES macro and the name of the dlhandle - variable, declares the corresponding function pointer variables and the - dlhandle variable. */ -#define LSX_DLENTRIES_TO_PTRS(entries, dlhandle) \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRY_TO_PTR__, 0) \ - lsx_dlhandle dlhandle - -/* LSX_DLENTRIES_TO_FUNCTIONS: Given an ENTRIES macro, declares the corresponding - functions. */ -#define LSX_DLENTRIES_TO_FUNCTIONS(entries) \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRIES_TO_FUNCTIONS__, 0) - -#define LSX_DLLIBRARY_OPEN1__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - { #func_name, (lsx_dlptr)(static_func), (lsx_dlptr)(stub_func) }, - -#define LSX_DLLIBRARY_OPEN2__(ptr_container, func_return, func_name, func_args, static_func, stub_func, func_ptr) \ - (ptr_container)->func_ptr = (func_return (*)func_args)lsx_dlfunction_open_library_funcs[lsx_dlfunction_open_library_index++]; - -/* LSX_DLLIBRARY_OPEN: Input an ENTRIES macro, the library's description, - a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }), - the name of the dlhandle variable, the name of the structure that contains - the function pointer and dlhandle variables, and the name of the variable in - which the result of the lsx_open_dllibrary call should be stored. This will - call lsx_open_dllibrary and copy the resulting function pointers into the - structure members. If the library cannot be opened, show a failure message. */ -#define LSX_DLLIBRARY_OPEN(ptr_container, dlhandle, entries, library_description, library_names, return_var) \ - LSX_DLLIBRARY_TRYOPEN(1, ptr_container, dlhandle, entries, library_description, library_names, return_var) - -/* LSX_DLLIBRARY_TRYOPEN: Input an ENTRIES macro, the library's description, - a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }), - the name of the dlhandle variable, the name of the structure that contains - the function pointer and dlhandle variables, and the name of the variable in - which the result of the lsx_open_dllibrary call should be stored. This will - call lsx_open_dllibrary and copy the resulting function pointers into the - structure members. If the library cannot be opened, show a report or a failure - message, depending on whether error_on_failure is non-zero. */ -#define LSX_DLLIBRARY_TRYOPEN(error_on_failure, ptr_container, dlhandle, entries, library_description, library_names, return_var) \ - do { \ - lsx_dlfunction_info lsx_dlfunction_open_library_infos[] = { \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN1__, 0) \ - {NULL,NULL,NULL} }; \ - int lsx_dlfunction_open_library_index = 0; \ - lsx_dlptr lsx_dlfunction_open_library_funcs[sizeof(lsx_dlfunction_open_library_infos)/sizeof(lsx_dlfunction_open_library_infos[0])]; \ - (return_var) = lsx_open_dllibrary((error_on_failure), (library_description), (library_names), lsx_dlfunction_open_library_infos, lsx_dlfunction_open_library_funcs, &(ptr_container)->dlhandle); \ - LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN2__, ptr_container) \ - } while(0) - -#define LSX_DLLIBRARY_CLOSE(ptr_container, dlhandle) \ - lsx_close_dllibrary((ptr_container)->dlhandle) - - /* LSX_DLENTRY_STATIC: For use in creating an ENTRIES macro. func is - expected to be available at link time. If not present, link will fail. */ -#define LSX_DLENTRY_STATIC(f,x, ret, func, args) f(x, ret, func, args, func, NULL, func) - - /* LSX_DLENTRY_DYNAMIC: For use in creating an ENTRIES macro. func need - not be available at link time (and if present, the link time version will - not be used). func will be loaded via dlsym. If this function is not - found in the shared library, the shared library will not be used. */ -#define LSX_DLENTRY_DYNAMIC(f,x, ret, func, args) f(x, ret, func, args, NULL, NULL, func) - - /* LSX_DLENTRY_STUB: For use in creating an ENTRIES macro. func need not - be available at link time (and if present, the link time version will not - be used). If using DL_LAME, the func may be loaded via dlopen/dlsym, but - if not found, the shared library will still be used if all of the - non-stub functions are found. If the function is not found via dlsym (or - if we are not loading any shared libraries), the stub will be used. This - assumes that the name of the stub function is the name of the function + - "_stub". */ -#define LSX_DLENTRY_STUB(f,x, ret, func, args) f(x, ret, func, args, NULL, func##_stub, func) - - /* LSX_DLFUNC_IS_STUB: returns true if the named function is a do-nothing - stub. Assumes that the name of the stub function is the name of the - function + "_stub". */ -#define LSX_DLFUNC_IS_STUB(ptr_container, func) ((ptr_container)->func == func##_stub) - -#endif diff --git a/freedv/tags/1.2.2/src/sox/soxomp.h b/freedv/tags/1.2.2/src/sox/soxomp.h deleted file mode 100644 index 6fce07d9..00000000 --- a/freedv/tags/1.2.2/src/sox/soxomp.h +++ /dev/null @@ -1,38 +0,0 @@ -#include "soxconfig.h" - -#ifdef HAVE_OPENMP - #include -#else - -typedef int omp_lock_t; -typedef int omp_nest_lock_t; - -#define omp_set_num_threads(int) (void)0 -#define omp_get_num_threads() 1 -#define omp_get_max_threads() 1 -#define omp_get_thread_num() 0 -#define omp_get_num_procs() 1 -#define omp_in_parallel() 1 - -#define omp_set_dynamic(int) (void)0 -#define omp_get_dynamic() 0 - -#define omp_set_nested(int) (void)0 -#define omp_get_nested() 0 - -#define omp_init_lock(omp_lock_t) (void)0 -#define omp_destroy_lock(omp_lock_t) (void)0 -#define omp_set_lock(omp_lock_t) (void)0 -#define omp_unset_lock(omp_lock_t) (void)0 -#define omp_test_lock(omp_lock_t) 0 - -#define omp_init_nest_lock(omp_nest_lock_t) (void)0 -#define omp_destroy_nest_lock(omp_nest_lock_t) (void)0 -#define omp_set_nest_lock(omp_nest_lock_t) (void)0 -#define omp_unset_nest_lock(omp_nest_lock_t) (void)0 -#define omp_test_nest_lock(omp_nest_lock_t) 0 - -#define omp_get_wtime() 0 -#define omp_get_wtick() 0 - -#endif diff --git a/freedv/tags/1.2.2/src/sox/util.h b/freedv/tags/1.2.2/src/sox/util.h deleted file mode 100644 index 89bbe752..00000000 --- a/freedv/tags/1.2.2/src/sox/util.h +++ /dev/null @@ -1,231 +0,0 @@ -/* General purpose, i.e. non SoX specific, utility functions and macros. - * - * (c) 2006-8 Chris Bagwell and SoX contributors - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "soxconfig.h" - -#ifdef HAVE_SYS_TYPES_H -#include /* For off_t not found in stdio.h */ -#endif - -#ifdef HAVE_SYS_STAT_H -#include /* Needs to be included before we redefine off_t. */ -#endif - -#include "xmalloc.h" - -/*---------------------------- Portability stuff -----------------------------*/ - -#if defined(HAVE_INTTYPES_H) - #include -#elif defined(HAVE_STDINT_H) - #include -#else - typedef sox_int8_t int8_t; - typedef sox_uint8_t uint8_t; - typedef sox_int16_t int16_t; - typedef sox_uint16_t uint16_t; - typedef sox_int32_t int32_t; - typedef sox_uint32_t uint32_t; - typedef sox_int64_t int64_t; - typedef sox_uint64_t uint64_t; -#endif - -/* Define the format specifier to use for int64_t values. - * Example: printf("You may have already won $ %" PRId64 " !!!", n64); */ -#ifndef PRId64 /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %lld. */ -#define PRId64 "I64d" -#elif LONG_MAX==9223372036854775807 -#define PRId64 "ld" -#else -#define PRId64 "lld" -#endif -#endif /* PRId64 */ - -/* Define the format specifier to use for uint64_t values. */ -#ifndef PRIu64 /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %llu. */ -#define PRIu64 "I64u" -#elif ULONG_MAX==0xffffffffffffffff -#define PRIu64 "lu" -#else -#define PRIu64 "llu" -#endif -#endif /* PRIu64 */ - -/* Define the format specifier to use for size_t values. - * Example: printf("Sizeof(x) = %" PRIuPTR " bytes", sizeof(x)); */ -#ifndef PRIuPTR /* Maybe already defined this. */ -#if defined(_MSC_VER) || defined(__MINGW32__) /* Older versions of msvcrt.dll don't recognize %zu. */ -#define PRIuPTR "Iu" -#else -#define PRIuPTR "zu" -#endif -#endif /* PRIuPTR */ - -#ifdef __GNUC__ -#define NORET __attribute__((noreturn)) -#define UNUSED __attribute__ ((unused)) -#else -#define NORET -#define UNUSED -#endif - -#ifdef _MSC_VER - -#define __STDC__ 1 -#define O_BINARY _O_BINARY -#define O_CREAT _O_CREAT -#define O_RDWR _O_RDWR -#define O_TRUNC _O_TRUNC -#define S_IFMT _S_IFMT -#define S_IFREG _S_IFREG -#define S_IREAD _S_IREAD -#define S_IWRITE _S_IWRITE -#define close _close -#define dup _dup -#define fdopen _fdopen -#define fileno _fileno - -#ifdef _fstati64 -#define fstat _fstati64 -#else -#define fstat _fstat -#endif - -#define ftime _ftime -#define inline __inline -#define isatty _isatty -#define kbhit _kbhit -#define mktemp _mktemp -#define off_t _off_t -#define open _open -#define pclose _pclose -#define popen _popen -#define setmode _setmode -#define snprintf _snprintf - -#ifdef _stati64 -#define stat _stati64 -#else -#define stat _stat -#endif - -#define strdup _strdup -#define timeb _timeb -#define unlink _unlink - -#if defined(HAVE__FSEEKI64) && !defined(HAVE_FSEEKO) -#undef off_t -#define fseeko _fseeki64 -#define ftello _ftelli64 -#define off_t __int64 -#define HAVE_FSEEKO 1 -#endif - -#elif defined(__MINGW32__) - -#if !defined(HAVE_FSEEKO) -#undef off_t -#define fseeko fseeko64 -#define fstat _fstati64 -#define ftello ftello64 -#define off_t off64_t -#define stat _stati64 -#define HAVE_FSEEKO 1 -#endif - -#endif - -#if defined(DOS) || defined(WIN32) || defined(__NT__) || defined(__DJGPP__) || defined(__OS2__) - #define LAST_SLASH(path) max(strrchr(path, '/'), strrchr(path, '\\')) - #define IS_ABSOLUTE(path) ((path)[0] == '/' || (path)[0] == '\\' || (path)[1] == ':') - #define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) - #define POPEN_MODE "rb" -#else - #define LAST_SLASH(path) strrchr(path, '/') - #define IS_ABSOLUTE(path) ((path)[0] == '/') - #define SET_BINARY_MODE(file) -#endif - -#ifdef WORDS_BIGENDIAN - #define MACHINE_IS_BIGENDIAN 1 - #define MACHINE_IS_LITTLEENDIAN 0 -#else - #define MACHINE_IS_BIGENDIAN 0 - #define MACHINE_IS_LITTLEENDIAN 1 -#endif - -/*--------------------------- Language extensions ----------------------------*/ - -/* Compile-time ("static") assertion */ -/* e.g. assert_static(sizeof(int) >= 4, int_type_too_small) */ -#define assert_static(e,f) enum {assert_static__##f = 1/(e)} -#define array_length(a) (sizeof(a)/sizeof(a[0])) -#define field_offset(type, field) ((size_t)&(((type *)0)->field)) -#define unless(x) if (!(x)) - -/*------------------------------- Maths stuff --------------------------------*/ - -#include - -#ifdef min -#undef min -#endif -#define min(a, b) ((a) <= (b) ? (a) : (b)) - -#ifdef max -#undef max -#endif -#define max(a, b) ((a) >= (b) ? (a) : (b)) - -#define range_limit(x, lower, upper) (min(max(x, lower), upper)) - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#endif -#ifndef M_LN10 -#define M_LN10 2.30258509299404568402 /* natural log of 10 */ -#endif -#ifndef M_SQRT2 -#define M_SQRT2 sqrt(2.) -#endif - -#define sqr(a) ((a) * (a)) -#define sign(x) ((x) < 0? -1 : 1) - -/* Numerical Recipes in C, p. 284 */ -#define ranqd1(x) ((x) = 1664525L * (x) + 1013904223L) /* int32_t x */ -#define dranqd1(x) (ranqd1(x) * (1. / (65536. * 32768.))) /* [-1,1) */ - -#define dB_to_linear(x) exp((x) * M_LN10 * 0.05) -#define linear_to_dB(x) (log10(x) * 20) - -extern int lsx_strcasecmp(const char *s1, const char *st); -extern int lsx_strncasecmp(char const *s1, char const *s2, size_t n); - -#ifndef HAVE_STRCASECMP -#define strcasecmp(s1, s2) lsx_strcasecmp((s1), (s2)) -#define strncasecmp(s1, s2, n) lsx_strncasecmp((s1), (s2), (n)) -#endif diff --git a/freedv/tags/1.2.2/src/sox/xmalloc.c b/freedv/tags/1.2.2/src/sox/xmalloc.c deleted file mode 100644 index 9bf15969..00000000 --- a/freedv/tags/1.2.2/src/sox/xmalloc.c +++ /dev/null @@ -1,43 +0,0 @@ -/* SoX Memory allocation functions - * - * Copyright (c) 2005-2006 Reuben Thomas. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "sox_i.h" -#include - -/* Resize an allocated memory area; abort if not possible. - * - * For malloc, `If the size of the space requested is zero, the behavior is - * implementation defined: either a null pointer is returned, or the - * behavior is as if the size were some nonzero value, except that the - * returned pointer shall not be used to access an object' - */ -void *lsx_realloc(void *ptr, size_t newsize) -{ - if (ptr && newsize == 0) { - free(ptr); - return NULL; - } - - if ((ptr = realloc(ptr, newsize)) == NULL) { - lsx_fail("out of memory"); - exit(2); - } - - return ptr; -} diff --git a/freedv/tags/1.2.2/src/sox/xmalloc.h b/freedv/tags/1.2.2/src/sox/xmalloc.h deleted file mode 100644 index 9ee77f63..00000000 --- a/freedv/tags/1.2.2/src/sox/xmalloc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* libSoX Memory allocation functions - * - * Copyright (c) 2005-2006 Reuben Thomas. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - * - * This library 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LSX_MALLOC_H -#define LSX_MALLOC_H - -#include -#include - -#define lsx_malloc(size) lsx_realloc(NULL, (size)) -#define lsx_calloc(n,s) (((n)*(s))? memset(lsx_malloc((n)*(s)),0,(n)*(s)) : NULL) -#define lsx_Calloc(v,n) v = lsx_calloc(n,sizeof(*(v))) -#define lsx_strdup(p) ((p)? strcpy((char *)lsx_malloc(strlen(p) + 1), p) : NULL) -#define lsx_memdup(p,s) ((p)? memcpy(lsx_malloc(s), p, s) : NULL) -#define lsx_valloc(v,n) v = lsx_malloc((n)*sizeof(*(v))) -#define lsx_revalloc(v,n) v = lsx_realloc(v, (n)*sizeof(*(v))) - -#endif diff --git a/freedv/tags/1.2.2/src/sox_biquad.c b/freedv/tags/1.2.2/src/sox_biquad.c deleted file mode 100644 index 548f4249..00000000 --- a/freedv/tags/1.2.2/src/sox_biquad.c +++ /dev/null @@ -1,134 +0,0 @@ -//========================================================================== -// Name: sox_biquad.h -// Purpose: Interface into Sox Biquad filters -// Created: Dec 1, 2012 -// Authors: David Rowe -// -// To test: -/* - $ gcc sox_biquad.c sox/effects_i.c sox/effects.c sox/formats_i.c \ - sox/biquad.c sox/biquads.c sox/xmalloc.c sox/libsox.c \ - -o sox_biquad -DSOX_BIQUAD_UNITTEST -D__FREEDV__ \ - -Wall -lm -lsndfile -g - $ ./sox_biquad -*/ -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== - -#include -#include -#include -#include "sox/sox.h" - -#include "sox_biquad.h" - - -#define N_MAX 1024 - -int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, - sox_sample_t *obuf, size_t *isamp, size_t *osamp); - -void sox_biquad_start(void) -{ - int r = sox_init(); - assert(r == SOX_SUCCESS); -} - -void sox_biquad_finish(void) -{ - sox_quit(); -} - -/* - Effect must be implemented by biquads.c in sox, arguments are just - like sox command line, for example: - - char *argv[10]; - argv[0] = "highpass"; argv[1]="1000"; argc=1; -*/ - -void *sox_biquad_create(int argc, const char *argv[]) -{ - int ret; - sox_effect_t *e; - int (*start)(sox_effect_t *); /* function pointer to effect start func */ - - e = sox_create_effect(sox_find_effect(argv[0])); assert(e != NULL); - ret = sox_effect_options(e, argc, (char * const*)&argv[1]); - assert(ret == SOX_SUCCESS); - - start = e->handler.start; - e->in_signal.rate = 8000; /* locked at FS=8000 Hz */ - ret = start(e); assert(ret == SOX_SUCCESS); - - return (void *)e; -} - -void sox_biquad_destroy(void *sbq) { - sox_effect_t *e = (sox_effect_t *)sbq; - free(e); -} - -void sox_biquad_filter(void *sbq, short out[], short in[], int n) -{ - sox_effect_t *e = (sox_effect_t *)sbq; - sox_sample_t ibuf[N_MAX]; - sox_sample_t obuf[N_MAX]; - size_t isamp, osamp; - unsigned int clips; - SOX_SAMPLE_LOCALS; - int i; - - assert(n <= N_MAX); - - clips = 0; - for(i=0; i. -// -//========================================================================== - -#ifndef __SOX_BIQUAD__ -#define __SOX_BIQUAD__ - -#ifdef __cplusplus -extern "C" { - -#endif - -void sox_biquad_start(void); -void sox_biquad_finish(void); -void *sox_biquad_create(int argc, const char *argv[]); -void sox_biquad_destroy(void *sbq); -void sox_biquad_filter(void *sbq, short out[], short in[], int n); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/freedv/tags/1.2.2/src/topFrame.cpp b/freedv/tags/1.2.2/src/topFrame.cpp deleted file mode 100644 index c1044b33..00000000 --- a/freedv/tags/1.2.2/src/topFrame.cpp +++ /dev/null @@ -1,592 +0,0 @@ -//========================================================================== -// Name: topFrame.cpp -// -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License -// along with this program; if not, see . -// -//========================================================================== -#include "topFrame.h" - -extern int g_playFileToMicInEventId; -extern int g_recFileFromRadioEventId; -extern int g_playFileFromRadioEventId; - -//========================================================================= -// Code that lays out the main application window -//========================================================================= -TopFrame::TopFrame(wxString plugInName, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) -{ - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); - //===================================================== - // Menubar Setup - m_menubarMain = new wxMenuBar(wxMB_DOCKABLE); - file = new wxMenu(); - - wxMenuItem* m_menuItemOnTop; - m_menuItemOnTop = new wxMenuItem(file, wxID_ANY, wxString(_("On Top")) , _("Always Top Window"), wxITEM_NORMAL); - file->Append(m_menuItemOnTop); - - wxMenuItem* m_menuItemExit; - m_menuItemExit = new wxMenuItem(file, ID_EXIT, wxString(_("E&xit")) , _("Exit Program"), wxITEM_NORMAL); - file->Append(m_menuItemExit); - - m_menubarMain->Append(file, _("&File")); - - tools = new wxMenu(); - wxMenuItem* m_menuItemAudio; - m_menuItemAudio = new wxMenuItem(tools, wxID_ANY, wxString(_("&Audio Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemAudio); - - wxMenuItem* m_menuItemRigCtrlCfg; - m_menuItemRigCtrlCfg = new wxMenuItem(tools, wxID_ANY, wxString(_("&PTT Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemRigCtrlCfg); - - wxMenuItem* m_menuItemOptions; - m_menuItemOptions = new wxMenuItem(tools, wxID_ANY, wxString(_("Options")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemOptions); - - wxMenuItem* m_menuItemFilter; - m_menuItemFilter = new wxMenuItem(tools, wxID_ANY, wxString(_("&Filter")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemFilter); - - wxMenuItem* m_menuItemPlugIn; - if (!wxIsEmpty(plugInName)) { - m_menuItemPlugIn = new wxMenuItem(tools, wxID_ANY, plugInName + wxString(_(" Config")) , wxEmptyString, wxITEM_NORMAL); - tools->Append(m_menuItemPlugIn); - } - - wxMenuItem* m_menuItemPlayFileToMicIn; - m_menuItemPlayFileToMicIn = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Play File - Mic In")) , wxEmptyString, wxITEM_NORMAL); - g_playFileToMicInEventId = m_menuItemPlayFileToMicIn->GetId(); - tools->Append(m_menuItemPlayFileToMicIn); - - wxMenuItem* m_menuItemRecFileFromRadio; - m_menuItemRecFileFromRadio = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Record File - From Radio")) , wxEmptyString, wxITEM_NORMAL); - g_recFileFromRadioEventId = m_menuItemRecFileFromRadio->GetId(); - tools->Append(m_menuItemRecFileFromRadio); - - wxMenuItem* m_menuItemPlayFileFromRadio; - m_menuItemPlayFileFromRadio = new wxMenuItem(tools, wxID_ANY, wxString(_("Start/Stop Play File - From Radio")) , wxEmptyString, wxITEM_NORMAL); - g_playFileFromRadioEventId = m_menuItemPlayFileFromRadio->GetId(); - tools->Append(m_menuItemPlayFileFromRadio); - m_menubarMain->Append(tools, _("&Tools")); - - help = new wxMenu(); - wxMenuItem* m_menuItemHelpUpdates; - m_menuItemHelpUpdates = new wxMenuItem(help, wxID_ANY, wxString(_("Check for Updates")) , wxEmptyString, wxITEM_NORMAL); - help->Append(m_menuItemHelpUpdates); - m_menuItemHelpUpdates->Enable(false); - - wxMenuItem* m_menuItemAbout; - m_menuItemAbout = new wxMenuItem(help, ID_ABOUT, wxString(_("&About")) , _("About this program"), wxITEM_NORMAL); - help->Append(m_menuItemAbout); - - m_menubarMain->Append(help, _("&Help")); - - this->SetMenuBar(m_menubarMain); - - wxBoxSizer* bSizer1; - bSizer1 = new wxBoxSizer(wxHORIZONTAL); - - //===================================================== - // Left side - //===================================================== - wxBoxSizer* leftSizer; - leftSizer = new wxBoxSizer(wxVERTICAL); - - wxStaticBoxSizer* snrSizer; - snrSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("SNR")), wxVERTICAL); - - //------------------------------ - // S/N ratio Guage (vert. bargraph) - //------------------------------ - m_gaugeSNR = new wxGauge(this, wxID_ANY, 25, wxDefaultPosition, wxSize(15,135), wxGA_SMOOTH|wxGA_VERTICAL); - m_gaugeSNR->SetToolTip(_("Displays signal to noise ratio in dB.")); - snrSizer->Add(m_gaugeSNR, 1, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); - - //------------------------------ - // Box for S/N ratio (Numeric) - //------------------------------ - m_textSNR = new wxStaticText(this, wxID_ANY, wxT(" 0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - snrSizer->Add(m_textSNR, 0, wxALIGN_CENTER_HORIZONTAL, 1); - - //------------------------------ - // S/N ratio slow Checkbox - //------------------------------ - m_ckboxSNR = new wxCheckBox(this, wxID_ANY, _("Slow"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - m_ckboxSNR->SetToolTip(_("Smooth but slow SNR estimation")); - snrSizer->Add(m_ckboxSNR, 0, wxALIGN_CENTER_HORIZONTAL, 5); - - leftSizer->Add(snrSizer, 2, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 1); - - //------------------------------ - // Sync Indicator box - //------------------------------ - wxStaticBoxSizer* sbSizer3_33; - sbSizer3_33 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Sync")), wxVERTICAL); - - m_rbSync = new wxRadioButton( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - m_rbSync->SetForegroundColour( wxColour( 255, 0, 0 ) ); - sbSizer3_33->Add(m_rbSync, 0, wxALIGN_CENTER|wxALL, 1); - leftSizer->Add(sbSizer3_33,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // BER Frames box - //------------------------------ - - wxStaticBoxSizer* sbSizer_ber; - sbSizer_ber = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Bit Error Rate")), wxVERTICAL); - - m_BtnBerReset = new wxButton(this, wxID_ANY, _("Reset"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_ber->Add(m_BtnBerReset, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - m_textBits = new wxStaticText(this, wxID_ANY, wxT("Bits: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textBits, 0, wxALIGN_LEFT, 1); - m_textErrors = new wxStaticText(this, wxID_ANY, wxT("Errs: 0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textErrors, 0, wxALIGN_LEFT, 1); - m_textBER = new wxStaticText(this, wxID_ANY, wxT("BER: 0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - sbSizer_ber->Add(m_textBER, 0, wxALIGN_LEFT, 1); - - leftSizer->Add(sbSizer_ber,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - //------------------------------ - // Signal Level(vert. bargraph) - //------------------------------ - wxStaticBoxSizer* levelSizer; - levelSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Level")), wxVERTICAL); - - m_textLevel = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(60,-1), wxALIGN_CENTRE); - m_textLevel->SetForegroundColour(wxColour(255,0,0)); - levelSizer->Add(m_textLevel, 0, wxALIGN_LEFT, 1); - - m_gaugeLevel = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(15,135), wxGA_SMOOTH|wxGA_VERTICAL); - m_gaugeLevel->SetToolTip(_("Peak of From Radio in Rx, or peak of From Mic in Tx mode. If Red you should reduce your levels")); - levelSizer->Add(m_gaugeLevel, 1, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); - - leftSizer->Add(levelSizer, 2, wxALIGN_CENTER|wxALL|wxEXPAND, 1); - - bSizer1->Add(leftSizer, 0, wxALL|wxEXPAND, 5); - - //===================================================== - // Center Section - //===================================================== - wxBoxSizer* centerSizer; - centerSizer = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* upperSizer; - upperSizer = new wxBoxSizer(wxVERTICAL); - - //===================================================== - // Tabbed Notebook control containing display graphs - //===================================================== - //m_auiNbookCtrl = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_NB_BOTTOM|wxAUI_NB_DEFAULT_STYLE); - //long style = wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_CLOSE_ON_ACTIVE_TAB | wxAUI_NB_MIDDLE_CLICK_CLOSE; - long nb_style = wxAUI_NB_BOTTOM | wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS; - m_auiNbookCtrl = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, nb_style); - // This line sets the fontsize for the tabs on the notebook control - m_auiNbookCtrl->SetFont(wxFont(8, 70, 90, 90, false, wxEmptyString)); - - upperSizer->Add(m_auiNbookCtrl, 1, wxALIGN_TOP|wxEXPAND, 1); - centerSizer->Add(upperSizer, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALIGN_TOP|wxEXPAND, 0); - - // lower middle used for user ID - - wxBoxSizer* lowerSizer; - lowerSizer = new wxBoxSizer(wxHORIZONTAL); - - m_BtnCallSignReset = new wxButton(this, wxID_ANY, _("Clear"), wxDefaultPosition, wxDefaultSize, 0); - lowerSizer->Add(m_BtnCallSignReset, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - wxBoxSizer* bSizer15; - bSizer15 = new wxBoxSizer(wxVERTICAL); - m_txtCtrlCallSign = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_txtCtrlCallSign->SetToolTip(_("Call Sign of transmitting station will appear here")); - bSizer15->Add(m_txtCtrlCallSign, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5); - lowerSizer->Add(bSizer15, 1, wxEXPAND, 5); - -#ifdef __EXPERIMENTAL_UDP__ - wxStaticBoxSizer* sbSizer_Checksum = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Checksums")), wxHORIZONTAL); - - wxStaticText *goodLabel = new wxStaticText(this, wxID_ANY, wxT("Good: "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - sbSizer_Checksum->Add(goodLabel, 0, 0, 2); - m_txtChecksumGood = new wxStaticText(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(30,-1), wxALIGN_CENTRE); - sbSizer_Checksum->Add(m_txtChecksumGood, 0, 0, 2); - - wxStaticText *badLabel = new wxStaticText(this, wxID_ANY, wxT("Bad: "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - sbSizer_Checksum->Add(badLabel, 0, 0, 1); - m_txtChecksumBad = new wxStaticText(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(30,-1), wxALIGN_CENTRE); - sbSizer_Checksum->Add(m_txtChecksumBad, 0, 0, 1); - - lowerSizer->Add(sbSizer_Checksum, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - //===================================================== - // These are the buttons that autosend the userid (?) - //===================================================== - - // DR 4 Dec - taken off for screen for Beta release to avoid questions on their use until - // we implement this feature - #ifdef UNIMPLEMENTED - wxBoxSizer* bSizer141; - bSizer141 = new wxBoxSizer(wxHORIZONTAL); - - // TxID - //--------- - m_togTxID = new wxToggleButton(this, wxID_ANY, _("TxID"), wxDefaultPosition, wxDefaultSize, 0); - m_togTxID->SetToolTip(_("Send Tx ID information")); - bSizer141->Add(m_togTxID, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); - - // RxID - //--------- - m_togRxID = new wxToggleButton(this, wxID_ANY, _("RxID"), wxDefaultPosition, wxDefaultSize, 0); - m_togRxID->SetToolTip(_("Enable reception of ID information")); - bSizer141->Add(m_togRxID, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_LEFT|wxALL|wxFIXED_MINSIZE, 5); - - lowerSizer->Add(bSizer141, 0, wxALIGN_RIGHT, 5); -#endif - - centerSizer->Add(lowerSizer, 0, wxALIGN_BOTTOM|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 2); - bSizer1->Add(centerSizer, 4, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 1); - - //===================================================== - // Right side - //===================================================== - wxBoxSizer* rightSizer; - rightSizer = new wxBoxSizer(wxVERTICAL); - - //===================================================== - // Squelch Slider Control - //===================================================== - wxStaticBoxSizer* sbSizer3; - sbSizer3 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Squelch")), wxVERTICAL); - - m_sliderSQ = new wxSlider(this, wxID_ANY, 0, 0, 40, wxDefaultPosition, wxSize(-1,80), wxSL_AUTOTICKS|wxSL_INVERSE|wxSL_VERTICAL); - m_sliderSQ->SetToolTip(_("Set Squelch level in dB.")); - - sbSizer3->Add(m_sliderSQ, 1, wxALIGN_CENTER_HORIZONTAL, 0); - - //------------------------------ - // Squelch Level static text box - //------------------------------ - m_textSQ = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - - sbSizer3->Add(m_textSQ, 0, wxALIGN_CENTER_HORIZONTAL, 0); - - //------------------------------ - // Squelch Toggle Checkbox - //------------------------------ - m_ckboxSQ = new wxCheckBox(this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - - sbSizer3->Add(m_ckboxSQ, 0, wxALIGN_CENTER_HORIZONTAL, 0); - rightSizer->Add(sbSizer3, 2, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 0); - - //rightSizer->Add(sbSizer3_33,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - /* new --- */ - - //------------------------------ - // Mode box - //------------------------------ - wxStaticBoxSizer* sbSizer_mode; - sbSizer_mode = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Mode")), wxVERTICAL); - -#ifdef DISABLED_FEATURE - m_rb1400old = new wxRadioButton( this, wxID_ANY, wxT("1400 V0.91"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb1400old, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1400 = new wxRadioButton( this, wxID_ANY, wxT("1400"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1400, 0, wxALIGN_LEFT|wxALL, 1); - m_rb700 = new wxRadioButton( this, wxID_ANY, wxT("700"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700, 0, wxALIGN_LEFT|wxALL, 1); -#endif - m_rb700b = new wxRadioButton( this, wxID_ANY, wxT("700B"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700b, 0, wxALIGN_LEFT|wxALL, 1); - m_rb700c = new wxRadioButton( this, wxID_ANY, wxT("700C"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - sbSizer_mode->Add(m_rb700c, 0, wxALIGN_LEFT|wxALL, 1); - m_rb800xa = new wxRadioButton( this, wxID_ANY, wxT("800XA"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb800xa, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1600 = new wxRadioButton( this, wxID_ANY, wxT("1600"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1600, 0, wxALIGN_LEFT|wxALL, 1); - m_rb1600->SetValue(true); - - m_rbPlugIn = NULL; - if (!wxIsEmpty(plugInName)) { - // Optional plug in - - m_rbPlugIn = new wxRadioButton( this, wxID_ANY, plugInName, wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rbPlugIn, 0, wxALIGN_LEFT|wxALL, 1); - } - -#ifdef DISABLED_FEATURE - m_rb1600Wide = new wxRadioButton( this, wxID_ANY, wxT("1600 Wide"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb1600Wide, 0, wxALIGN_LEFT|wxALL, 1); - m_rb2000 = new wxRadioButton( this, wxID_ANY, wxT("2000"), wxDefaultPosition, wxDefaultSize, 0); - sbSizer_mode->Add(m_rb2000, 0, wxALIGN_LEFT|wxALL, 1); -#endif - - rightSizer->Add(sbSizer_mode,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - - #ifdef MOVED_TO_OPTIONS_DIALOG - /* new --- */ - - //------------------------------ - // Test Frames box - //------------------------------ - - wxStaticBoxSizer* sbSizer_testFrames; - sbSizer_testFrames = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Test Frames")), wxVERTICAL); - - m_ckboxTestFrame = new wxCheckBox(this, wxID_ANY, _("Enable"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); - sbSizer_testFrames->Add(m_ckboxTestFrame, 0, wxALIGN_LEFT, 0); - - rightSizer->Add(sbSizer_testFrames,0, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 3); - #endif - - //===================================================== - // Control Toggles box - //===================================================== - wxStaticBoxSizer* sbSizer5; - sbSizer5 = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Control")), wxVERTICAL); - wxBoxSizer* bSizer1511; - bSizer1511 = new wxBoxSizer(wxVERTICAL); - - //------------------------------- - // Stop/Stop signal processing (rx and tx) - //------------------------------- - m_togBtnOnOff = new wxToggleButton(this, wxID_ANY, _("Start"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnOnOff->SetToolTip(_("Begin/End receiving data.")); - bSizer1511->Add(m_togBtnOnOff, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer1511, 0, wxEXPAND, 1); - -#ifdef UNIMPLEMENTED - //------------------------------ - // Toggle Loopback button for RX - //------------------------------ - wxBoxSizer* bSizer15113; - bSizer15113 = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* bSizer15111; - bSizer15111 = new wxBoxSizer(wxVERTICAL); - wxSize wxSz = wxSize(44, 30); - m_togBtnLoopRx = new wxToggleButton(this, wxID_ANY, _("Loop\nRX"), wxDefaultPosition, wxSz, 0); - m_togBtnLoopRx->SetFont(wxFont(6, 70, 90, 90, false, wxEmptyString)); - m_togBtnLoopRx->SetToolTip(_("Loopback Receive audio data.")); - - bSizer15111->Add(m_togBtnLoopRx, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - //sbSizer5->Add(bSizer15111, 0, wxEXPAND, 1); - bSizer15113->Add(bSizer15111, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - //------------------------------ - // Toggle Loopback button for Tx - //------------------------------ - wxBoxSizer* bSizer15112; - bSizer15112 = new wxBoxSizer(wxVERTICAL); - m_togBtnLoopTx = new wxToggleButton(this, wxID_ANY, _("Loop\nTX"), wxDefaultPosition, wxSz, 0); - m_togBtnLoopTx->SetFont(wxFont(6, 70, 90, 90, false, wxEmptyString)); - m_togBtnLoopTx->SetToolTip(_("Loopback Transmit audio data.")); - - bSizer15112->Add(m_togBtnLoopTx, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - bSizer15113->Add(bSizer15112, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 0); - - sbSizer5->Add(bSizer15113, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); -#endif - - //------------------------------ - // Split Frequency Mode Toggle - //------------------------------ - wxBoxSizer* bSizer151; - bSizer151 = new wxBoxSizer(wxVERTICAL); - - m_togBtnSplit = new wxToggleButton(this, wxID_ANY, _("Split"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnSplit->SetToolTip(_("Toggle split frequency mode.")); - - bSizer151->Add(m_togBtnSplit, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 1); - wxBoxSizer* bSizer13; - bSizer13 = new wxBoxSizer(wxVERTICAL); - - //------------------------------ - // Analog Passthrough Toggle - //------------------------------ - m_togBtnAnalog = new wxToggleButton(this, wxID_ANY, _("Analog"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnAnalog->SetToolTip(_("Toggle analog/digital operation.")); - bSizer13->Add(m_togBtnAnalog, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer13, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - //------------------------------ - // Voice Keyer Toggle - //------------------------------ - m_togBtnVoiceKeyer = new wxToggleButton(this, wxID_ANY, _("Voice Keyer"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnVoiceKeyer->SetToolTip(_("Toggle Voice Keyer")); - wxBoxSizer* bSizer13a = new wxBoxSizer(wxVERTICAL); - bSizer13a->Add(m_togBtnVoiceKeyer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer13a, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - - // not implemented on fdmdv2 -#ifdef ALC - //------------------------------ - // Toggle for ALC - //------------------------------ - wxBoxSizer* bSizer14; - bSizer14 = new wxBoxSizer(wxVERTICAL); - m_togBtnALC = new wxToggleButton(this, wxID_ANY, _("ALC"), wxDefaultPosition, wxDefaultSize, 0); - m_togBtnALC->SetToolTip(_("Toggle automatic level control mode.")); - - bSizer14->Add(m_togBtnALC, 0, wxALL, 1); - sbSizer5->Add(bSizer14, 0, wxALIGN_CENTER|wxALIGN_CENTER_HORIZONTAL|wxALL, 1); -#endif - - //------------------------------ - // PTT button: Toggle Transmit/Receive mode - //------------------------------ - wxBoxSizer* bSizer11; - bSizer11 = new wxBoxSizer(wxVERTICAL); - m_btnTogPTT = new wxToggleButton(this, wxID_ANY, _("PTT"), wxDefaultPosition, wxDefaultSize, 0); - m_btnTogPTT->SetToolTip(_("Push to Talk - Switch between Receive and Transmit - you can also use the space bar ")); - bSizer11->Add(m_btnTogPTT, 1, wxALIGN_CENTER|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 1); - sbSizer5->Add(bSizer11, 2, wxEXPAND, 1); - rightSizer->Add(sbSizer5, 2, wxALIGN_CENTER|wxALL|wxEXPAND, 3); - bSizer1->Add(rightSizer, 0, wxALL|wxEXPAND, 3); - this->SetSizer(bSizer1); - this->Layout(); - m_statusBar1 = this->CreateStatusBar(3, wxST_SIZEGRIP, wxID_ANY); - - //===================================================== - // End of layout - //===================================================== - - //------------------- - // Connect Events - //------------------- - this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TopFrame::topFrame_OnClose)); - this->Connect(wxEVT_PAINT, wxPaintEventHandler(TopFrame::topFrame_OnPaint)); - this->Connect(wxEVT_SIZE, wxSizeEventHandler(TopFrame::topFrame_OnSize)); - this->Connect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::topFrame_OnUpdateUI)); - - this->Connect(m_menuItemExit->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnExit)); - this->Connect(m_menuItemOnTop->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnTop)); - - this->Connect(m_menuItemAudio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsAudio)); - this->Connect(m_menuItemAudio->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsAudioUI)); - this->Connect(m_menuItemFilter->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsFilter)); - this->Connect(m_menuItemFilter->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsFilterUI)); - this->Connect(m_menuItemRigCtrlCfg->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsComCfg)); - this->Connect(m_menuItemRigCtrlCfg->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsComCfgUI)); - this->Connect(m_menuItemOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsOptions)); - this->Connect(m_menuItemOptions->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsOptionsUI)); - - if (!wxIsEmpty(plugInName)) { - this->Connect(m_menuItemPlugIn->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsPlugInCfg)); - this->Connect(m_menuItemPlugIn->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsPlugInCfgUI)); - } - - this->Connect(m_menuItemPlayFileToMicIn->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); - this->Connect(m_menuItemRecFileFromRadio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); - this->Connect(m_menuItemPlayFileFromRadio->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileFromRadio)); - - this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); - this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - this->Connect(m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpAbout)); - //m_togRxID->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnRxID), NULL, this); - //m_togTxID->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnTxID), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnSliderScrollBottom), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScrollChanged), NULL, this); - m_sliderSQ->Connect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnSliderScrollTop), NULL, this); - m_ckboxSQ->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSQClick), NULL, this); - - m_ckboxSNR->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSNRClick), NULL, this); - - m_togBtnOnOff->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnOnOff), NULL, this); - m_togBtnSplit->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnSplitClick), NULL, this); - m_togBtnAnalog->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnAnalogClick), NULL, this); - m_togBtnVoiceKeyer->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnVoiceKeyerClick), NULL, this); -#ifdef ALC - m_togBtnALC->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnALCClick), NULL, this); -#endif - m_btnTogPTT->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnPTT), NULL, this); - - m_BtnCallSignReset->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnCallSignReset), NULL, this); - m_BtnBerReset->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnBerReset), NULL, this); -} - -TopFrame::~TopFrame() -{ - //------------------- - // Disconnect Events - //------------------- - this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TopFrame::topFrame_OnClose)); - this->Disconnect(wxEVT_PAINT, wxPaintEventHandler(TopFrame::topFrame_OnPaint)); - this->Disconnect(wxEVT_SIZE, wxSizeEventHandler(TopFrame::topFrame_OnSize)); - this->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::topFrame_OnUpdateUI)); - this->Disconnect(ID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnExit)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsAudio)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsAudioUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsFilter)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsFilterUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsComCfg)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsComCfgUI)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsOptions)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnToolsOptionsUI)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnToolsPlugInCfg)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileToMicIn)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnRecFileFromRadio)); - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnPlayFileFromRadio)); - - this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpCheckUpdates)); - this->Disconnect(wxID_ANY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); - this->Disconnect(ID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TopFrame::OnHelpAbout)); - //m_togRxID->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnRxID), NULL, this); - //m_togTxID->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnTxID), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_LINEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_PAGEUP, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScroll), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_BOTTOM, wxScrollEventHandler(TopFrame::OnSliderScrollBottom), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_CHANGED, wxScrollEventHandler(TopFrame::OnCmdSliderScrollChanged), NULL, this); - m_sliderSQ->Disconnect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnSliderScrollTop), NULL, this); - m_ckboxSQ->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSQClick), NULL, this); - - m_togBtnOnOff->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnOnOff), NULL, this); - m_togBtnSplit->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnSplitClick), NULL, this); - m_togBtnAnalog->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnAnalogClick), NULL, this); - m_togBtnVoiceKeyer->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnVoiceKeyerClick), NULL, this); -#ifdef ALC - m_togBtnALC->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnALCClick), NULL, this); -#endif - m_btnTogPTT->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnPTT), NULL, this); - -} - diff --git a/freedv/tags/1.2.2/src/topFrame.h b/freedv/tags/1.2.2/src/topFrame.h deleted file mode 100644 index e4ed5830..00000000 --- a/freedv/tags/1.2.2/src/topFrame.h +++ /dev/null @@ -1,193 +0,0 @@ -//========================================================================== -// Name: topFrame.h -// -// Purpose: Implements simple wxWidgets application with GUI. -// Created: Apr. 9, 2012 -// Authors: David Rowe, David Witten -// -// License: -// -// 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 . -// -//========================================================================== -#ifndef __TOPFRAME_H__ -#define __TOPFRAME_H__ - -#include "version.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/////////////////////////////////////////////////////////////////////////// - -#define ID_OPEN 1000 -#define ID_SAVE 1001 -#define ID_CLOSE 1002 -#define ID_EXIT 1003 -#define ID_COPY 1004 -#define ID_CUT 1005 -#define ID_PASTE 1006 -#define ID_OPTIONS 1007 -#define ID_ABOUT 1008 - -/////////////////////////////////////////////////////////////////////////////// -/// Class TopFrame -/////////////////////////////////////////////////////////////////////////////// -class TopFrame : public wxFrame -{ - private: - - protected: - wxMenuBar* m_menubarMain; - wxMenu* file; - wxMenu* edit; - wxMenu* tools; - wxMenu* help; - wxGauge* m_gaugeSNR; - wxStaticText* m_textSNR; - wxCheckBox* m_ckboxSNR; - wxGauge* m_gaugeLevel; - wxStaticText* m_textLevel; - - wxButton* m_BtnCallSignReset; - wxTextCtrl* m_txtCtrlCallSign; - wxStaticText* m_txtChecksumGood; - wxStaticText* m_txtChecksumBad; - - wxSlider* m_sliderSQ; - wxCheckBox* m_ckboxSQ; - wxStaticText* m_textSQ; - wxStatusBar* m_statusBar1; - - wxButton* m_BtnBerReset; - wxStaticText *m_textBits; - wxStaticText *m_textErrors; - wxStaticText *m_textBER; - - wxRadioButton *m_rbSync; - wxRadioButton *m_rb1400old; - wxRadioButton *m_rb1400; - wxRadioButton *m_rb700; - wxRadioButton *m_rb700b; - wxRadioButton *m_rb700c; - wxRadioButton *m_rb800xa; - wxRadioButton *m_rb1600; - wxRadioButton *m_rb2000; - wxRadioButton *m_rb1600Wide; - wxRadioButton *m_rbPlugIn; - - // Virtual event handlers, overide them in your derived class - virtual void topFrame_OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void topFrame_OnPaint( wxPaintEvent& event ) { event.Skip(); } - virtual void topFrame_OnSize( wxSizeEvent& event ) { event.Skip(); } - virtual void topFrame_OnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnExit( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTop( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsAudio( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsAudioUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsFilter( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsFilterUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsOptions( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnToolsPlugInCfg( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsPlugInCfgUI( wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnToolsUDP( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsOptionsUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnToolsComCfg( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToolsComCfgUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnPlayFileToMicIn( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRecFileFromRadio( wxCommandEvent& event ) { event.Skip(); } - virtual void OnPlayFileFromRadio( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnHelpCheckUpdates( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpCheckUpdatesUI( wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnHelpAbout( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnRxID( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnTxID( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCmdSliderScroll( wxScrollEvent& event ) { event.Skip(); } - virtual void OnSliderScrollBottom( wxScrollEvent& event ) { event.Skip(); } - virtual void OnCmdSliderScrollChanged( wxScrollEvent& event ) { event.Skip(); } - virtual void OnSliderScrollTop( wxScrollEvent& event ) { event.Skip(); } - virtual void OnCheckSQClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCheckSNRClick( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnTogBtnLoopRx( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnLoopTx( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnOnOff( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnSplitClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnAnalogClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnVoiceKeyerClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnALCClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnTogBtnPTT( wxCommandEvent& event ) { event.Skip(); } - - virtual void OnTogBtnSplitClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnAnalogClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnALCClickUI(wxUpdateUIEvent& event) { event.Skip(); } - virtual void OnTogBtnRxIDUI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnTxIDUI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnPTT_UI(wxUpdateUIEvent& event ) { event.Skip(); } - virtual void OnTogBtnOnOffUI(wxUpdateUIEvent& event ) { event.Skip(); } - - virtual void OnCallSignReset( wxCommandEvent& event ) { event.Skip(); } - virtual void OnBerReset( wxCommandEvent& event ) { event.Skip(); } - - public: - wxToggleButton* m_togRxID; - wxToggleButton* m_togTxID; - wxToggleButton* m_togBtnOnOff; - wxToggleButton* m_togBtnSplit; - wxToggleButton* m_togBtnAnalog; - wxToggleButton* m_togBtnVoiceKeyer; - wxToggleButton* m_togBtnALC; - wxToggleButton* m_btnTogPTT; - wxToggleButton* m_togBtnLoopRx; - wxToggleButton* m_togBtnLoopTx; - wxAuiNotebook* m_auiNbookCtrl; - - TopFrame( wxString plugInName, wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeDV ") + _(FREEDV_VERSION), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(561,300 ), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL ); - - ~TopFrame(); -}; - -#endif //__TOPFRAME_H__ -- 2.25.1