diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..0e24a440 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "opus-tools"] + path = opus-tools + url = https://github.com/xiph/opus-tools.git +[submodule "rtaudio"] + path = rtaudio + url = https://github.com/thestk/rtaudio.git diff --git a/audiohandler.cpp b/audiohandler.cpp index 4ae2f75c..1b3e0016 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -1,964 +1,161 @@ /* - This class handles both RX and TX audio, each is created as a seperate instance of the class - but as the setup/handling if output (RX) and input (TX) devices is so similar I have combined them. + This class handles both RX and TX audio, each is created as a seperate instance of the class + but as the setup/handling if output (RX) and input (TX) devices is so similar I have combined them. */ #include "audiohandler.h" + #include "logcategories.h" - -#define MULAW_BIAS 33 -#define MULAW_MAX 0x1fff - -static unsigned char ulaw_encode[8193] = - { 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, - 0xf9, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, - 0xf3, 0xf2, 0xf2, 0xf1, 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, - 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, - 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, - 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, - 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, - 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, - 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, - 0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, - 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, - 0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, - 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, - 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, - 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, - 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, - 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, - 0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, - 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, - 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, - 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, - 0xce, 0xce, 0xce, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, - 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, - 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, - 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, - 0xcb, 0xcb, 0xcb, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, - 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, - 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, - 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, - 0xc8, 0xc8, 0xc8, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, - 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, - 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, - 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 -}; - -static const qint16 ulaw_decode[256] = { - -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, - -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, - -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, - -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0 -}; +#include "ulaw.h" audioHandler::audioHandler(QObject* parent) : - QIODevice(parent), - isInitialized(false), - audioOutput(Q_NULLPTR), - audioInput(Q_NULLPTR), - isUlaw(false), - latency(0), - isInput(0), + isInitialized(false), + isUlaw(false), + audioLatency(0), + isInput(0), chunkAvailable(false) { + Q_UNUSED(parent) } audioHandler::~audioHandler() { - stop(); - if (audioOutput != Q_NULLPTR) { - audioOutput->stop(); - delete audioOutput; - qDebug(logAudio()) << "Audio output stopped"; - } - if (audioInput != Q_NULLPTR) { - audioInput->stop(); - delete audioInput; - qDebug(logAudio()) << "Audio input stopped"; - } + //stop(); - if (resampler != NULL) { + if (resampler != Q_NULLPTR) { speex_resampler_destroy(resampler); - qDebug(logAudio()) << "Resampler closed"; } + if (audio.isStreamRunning()) + { + audio.stopStream(); + audio.closeStream(); + } + if (ringBuf != Q_NULLPTR) + delete ringBuf; } -bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool ulaw, const bool isinput, QAudioDeviceInfo port, quint8 resampleQuality) +bool audioHandler::init(const quint8 bits, const quint8 radioChan, const quint16 samplerate, const quint16 latency, const bool ulaw, const bool isinput, int port, quint8 resampleQuality) { - if (isInitialized) { - return false; - } - /* Always use 16 bit 48K samples internally*/ - format.setSampleSize(16); - format.setChannelCount(channels); - format.setSampleRate(INTERNAL_SAMPLE_RATE); - format.setCodec("audio/pcm"); - format.setByteOrder(QAudioFormat::LittleEndian); - format.setSampleType(QAudioFormat::SignedInt); + if (isInitialized) { + return false; + } - this->latency = latency; + this->audioLatency = latency; this->isUlaw = ulaw; - this->isInput = isinput; - this->radioSampleBits = bits; - this->radioSampleRate = samplerate; - this->radioChannels = channels; + this->isInput = isinput; + this->radioSampleBits = bits; + this->radioSampleRate = samplerate; + this->radioChannels = radioChan; // chunk size is always relative to Internal Sample Rate. - this->chunkSize = (INTERNAL_SAMPLE_RATE / 25) * radioChannels; - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "chunkSize: " << this->chunkSize; - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "bufferLength (latency): " << this->latency; + ringBuf = new wilt::Ring(100); // Should be customizable. + tempBuf.sent = 0; - int resample_error=0; + if (port > 0) { + aParams.deviceId = port; + } + else if (isInput) { + aParams.deviceId = audio.getDefaultInputDevice(); + } + else { + aParams.deviceId = audio.getDefaultOutputDevice(); + } + aParams.firstChannel = 0; + + try { + info = audio.getDeviceInfo(aParams.deviceId); + } + catch (RtAudioError& e) { + qInfo(logAudio()) << "Device error:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); + return false; + } + + if (info.probed) + { + // Per channel chunk size. + aParams.nChannels = 2; // Internally this is always 2 channels for TX and RX. + this->chunkSize = (info.preferredSampleRate / 50); + + qInfo(logAudio()) << (isInput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed"; + if (info.nativeFormats == 0) + { + qInfo(logAudio()) << " No natively supported data formats!"; + return false; + } + else { + qDebug(logAudio()) << " Supported formats:" << + (info.nativeFormats & RTAUDIO_SINT8 ? "8-bit int," : "") << + (info.nativeFormats & RTAUDIO_SINT16 ? "16-bit int," : "") << + (info.nativeFormats & RTAUDIO_SINT24 ? "24-bit int," : "") << + (info.nativeFormats & RTAUDIO_SINT32 ? "32-bit int," : "") << + (info.nativeFormats & RTAUDIO_FLOAT32 ? "32-bit float," : "") << + (info.nativeFormats & RTAUDIO_FLOAT64 ? "64-bit float," : ""); + + qInfo(logAudio()) << " Preferred sample rate:" << info.preferredSampleRate; + if (isInput) { + devChannels = info.inputChannels; + } + else { + devChannels = info.outputChannels; + } + qInfo(logAudio()) << " Channels:" << devChannels; + if (devChannels > 2) { + devChannels = 2; + } + aParams.nChannels = devChannels; + } + + qInfo(logAudio()) << " chunkSize: " << chunkSize; + } + else + { + qCritical(logAudio()) << (isInput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!"; + return false; + } + + int resample_error = 0; if (isInput) { - resampler = wf_resampler_init(radioChannels, INTERNAL_SAMPLE_RATE, samplerate, resampleQuality, &resample_error); - - isInitialized = setDevice(port); - - if (!isInitialized) { - qInfo(logAudio()) << "Input device " << port.deviceName() << " not found, using default"; - isInitialized = setDevice(QAudioDeviceInfo::defaultInputDevice()); + resampler = wf_resampler_init(devChannels, info.preferredSampleRate, samplerate, resampleQuality, &resample_error); + try { + audio.openStream(NULL, &aParams, RTAUDIO_SINT16, info.preferredSampleRate, &this->chunkSize, &staticWrite, this); + audio.startStream(); + } + catch (RtAudioError& e) { + qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); + return false; } } else { - resampler = wf_resampler_init(radioChannels, samplerate, INTERNAL_SAMPLE_RATE, resampleQuality, &resample_error); - - isInitialized = setDevice(port); - - if (!isInitialized) { - qInfo(logAudio()) << "Output device " << deviceInfo.deviceName() << " not found, using default"; - isInitialized = setDevice(QAudioDeviceInfo::defaultOutputDevice()); + resampler = wf_resampler_init(devChannels, samplerate, info.preferredSampleRate, resampleQuality, &resample_error); + try { + audio.openStream(&aParams, NULL, RTAUDIO_SINT16, info.preferredSampleRate, &this->chunkSize, &staticRead, this); + audio.startStream(); + } + catch (RtAudioError& e) { + qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); + return false; } } + qInfo(logAudio()) << (isInput ? "Input" : "Output") << "device successfully opened"; + + qInfo(logAudio()) << (isInput ? "Input" : "Output") << "detected latency:" <setVolume((qreal)(volume / 255.0)); - } - else if (audioInput != Q_NULLPTR) { - audioInput->setVolume((qreal)(volume / 255.0)); - } -} - -bool audioHandler::setDevice(QAudioDeviceInfo deviceInfo) -{ - bool ret = true; - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "setDevice() running :" << deviceInfo.deviceName(); - if (!deviceInfo.isFormatSupported(format)) { - if (deviceInfo.isNull()) - { - qInfo(logAudio()) << "No audio device was found. You probably need to install libqt5multimedia-plugins."; - ret = false; - } - else { - /* - qInfo(logAudio()) << "Audio Devices found: "; - const auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); - for (const QAudioDeviceInfo& deviceInfo : deviceInfos) - { - qInfo(logAudio()) << "Device name: " << deviceInfo.deviceName(); - qInfo(logAudio()) << "is null (probably not good):" << deviceInfo.isNull(); - qInfo(logAudio()) << "channel count:" << deviceInfo.supportedChannelCounts(); - qInfo(logAudio()) << "byte order:" << deviceInfo.supportedByteOrders(); - qInfo(logAudio()) << "supported codecs:" << deviceInfo.supportedCodecs(); - qInfo(logAudio()) << "sample rates:" << deviceInfo.supportedSampleRates(); - qInfo(logAudio()) << "sample sizes:" << deviceInfo.supportedSampleSizes(); - qInfo(logAudio()) << "sample types:" << deviceInfo.supportedSampleTypes(); - } - qInfo(logAudio()) << "----- done with audio info -----"; - */ - ret=false; - } - - qInfo(logAudio()) << "Format not supported, choosing nearest supported format - which may not work!"; - deviceInfo.nearestFormat(format); - } - this->deviceInfo = deviceInfo; - this->reinit(); - return ret; -} - -void audioHandler::reinit() -{ - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "reinit() running"; - if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) { - this->stop(); - } - - if (this->isInput) - { - // (Re)initialize audio input - if (audioInput != Q_NULLPTR) - delete audioInput; - audioInput = new QAudioInput(deviceInfo, format, this); - - connect(audioInput, SIGNAL(notify()), SLOT(notified())); - connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); - - } - else { - // (Re)initialize audio output - if (audioOutput != Q_NULLPTR) - delete audioOutput; - audioOutput = Q_NULLPTR; - audioOutput = new QAudioOutput(deviceInfo, format, this); - - // This seems to only be needed on Linux but is critical in aligning buffer sizes. -//#ifdef Q_OS_MAC - audioOutput->setBufferSize(chunkSize*8); -//#else -// audioOutput->setBufferSize(chunkSize*4); -//#endif - - connect(audioOutput, SIGNAL(notify()), SLOT(notified())); - connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); - } - - this->start(); - this->flush(); -} - -void audioHandler::start() -{ - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "start() running"; - - if ((audioOutput == Q_NULLPTR || audioOutput->state() != QAudio::StoppedState) && - (audioInput == Q_NULLPTR || audioInput->state() != QAudio::StoppedState) ) { - return; - } - - if (isInput) { - //this->open(QIODevice::WriteOnly | QIODevice::Unbuffered); - this->open(QIODevice::WriteOnly); - audioInput->start(this); - } - else { - //this->open(QIODevice::ReadOnly | QIODevice::Unbuffered); - this->open(QIODevice::ReadOnly); - audioOutput->start(this); - } + this->volume = (qreal)volume/255.0; + qInfo(logAudio()) << (isInput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } -void audioHandler::flush() -{ - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "flush() running"; - this->stop(); - if (isInput) { - audioInput->reset(); - } - else { - audioOutput->reset(); - } - - QMutexLocker locker(&mutex); - audioBuffer.clear(); - this->start(); -} - -void audioHandler::stop() -{ - if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) { - // Stop audio output - audioOutput->stop(); - this->close(); - } - - if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) { - // Stop audio output - audioInput->stop(); - this->close(); - } -} /// /// This function processes the incoming audio FROM the radio and pushes it into the playback buffer *data @@ -966,341 +163,309 @@ void audioHandler::stop() /// /// /// -qint64 audioHandler::readData(char* data, qint64 maxlen) +int audioHandler::readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status) { - // Calculate output length, always full samples + Q_UNUSED(inputBuffer); + Q_UNUSED(streamTime); + // Calculate output length, always full samples int sentlen = 0; + quint8* buffer = (quint8*)outputBuffer; + if (status == RTAUDIO_OUTPUT_UNDERFLOW) + qDebug(logAudio()) << "Underflow detected"; - //qInfo(logAudio()) << "Looking for: " << maxlen << " bytes"; + int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels - // We must lock the mutex for the entire time that the buffer may be modified. - // Get next packet from buffer. - if (!inputBuffer.isEmpty()) + if (ringBuf->size()>0) { - // Output buffer is ALWAYS 16 bit. - QMutexLocker locker(&mutex); - auto packet = inputBuffer.begin(); - - while (packet != inputBuffer.end() && sentlen < maxlen) + //qDebug(logAudio()) << "Read: nFrames" << nFrames << "nBytes" << nBytes; + while (sentlen < nBytes) { - int timediff = packet->time.msecsTo(QTime::currentTime()); - - if (timediff > (int)latency * 2) { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Packet " << hex << packet->seq << - " arrived too late (increase output latency!) " << - dec << packet->time.msecsTo(QTime::currentTime()) << "ms"; - while (packet !=inputBuffer.end() && timediff > (int)latency) { - timediff = packet->time.msecsTo(QTime::currentTime()); - lastSeq=packet->seq; - packet = inputBuffer.erase(packet); // returns next packet - } - if (packet == inputBuffer.end()) { - break; - } - } - - // If we got here then packet time must be within latency threshold - - if (packet->seq == lastSeq+1 || packet->seq <= lastSeq) + audioPacket packet; + if (!ringBuf->try_read(packet)) { - int send = qMin((int)maxlen-sentlen, packet->dataout.length() - packet->sent); - lastSeq = packet->seq; - //qInfo(logAudio()) << "Packet " << hex << packet->seq << " arrived on time " << Qt::dec << packet->time.msecsTo(QTime::currentTime()) << "ms"; - - memcpy(data + sentlen, packet->dataout.constData() + packet->sent, send); + qDebug() << "No more data available but buffer is not full! sentlen:" << sentlen << " nBytes:" << nBytes ; + break; + } + currentLatency = packet.time.msecsTo(QTime::currentTime()); + // This shouldn't be required but if we did output a partial packet + // This will add the remaining packet data to the output buffer. + if (tempBuf.sent != tempBuf.data.length()) + { + int send = qMin((int)nBytes - sentlen, tempBuf.data.length() - tempBuf.sent); + memcpy(buffer + sentlen, tempBuf.data.constData() + tempBuf.sent, send); + tempBuf.sent = tempBuf.sent + send; sentlen = sentlen + send; - - if (send == packet->dataout.length() - packet->sent) + if (tempBuf.sent != tempBuf.data.length()) { - //qInfo(logAudio()) << "Get next packet"; - packet = inputBuffer.erase(packet); // returns next packet - } - else - { - // Store sent amount (could be zero if audioOutput buffer full) then break. - packet->sent = send; - break; - } - } else { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Missing audio packet(s) from: " << hex << lastSeq + 1 << " to " << hex << packet->seq - 1; - lastSeq = packet->seq; - } - } - } - else { - // Fool audio system into thinking it has valid data, this seems to be required - // for MacOS Built in audio but shouldn't cause any issues with other platforms. - memset(data, 0x0, maxlen); - return maxlen; - } - - return sentlen; -} - -qint64 audioHandler::writeData(const char* data, qint64 len) -{ - qint64 sentlen = 0; - QMutexLocker locker(&mutex); - audioPacket *current; - - while (sentlen < len) { - if (!audioBuffer.isEmpty()) - { - if (audioBuffer.last().sent == chunkSize) - { - audioBuffer.append(audioPacket()); - audioBuffer.last().sent = 0; - } - } - else - { - audioBuffer.append(audioPacket()); - audioBuffer.last().sent = 0; - } - current = &audioBuffer.last(); - - int send = qMin((int)(len - sentlen), (int)chunkSize-current->sent); - - current->datain.append(QByteArray::fromRawData(data + sentlen, send )); - - sentlen = sentlen + send; - - current->seq = 0; // Not used in TX - current->time = QTime::currentTime(); - current->sent = current->datain.length(); - - if (current->sent == chunkSize) - { - chunkAvailable = true; - } - else if (audioBuffer.length()<=1 && current->sent != chunkSize) { - chunkAvailable = false; - } - - } - - return (sentlen); // Always return the same number as we received -} - -qint64 audioHandler::bytesAvailable() const -{ - return 0; -} - -bool audioHandler::isSequential() const -{ - return true; -} - -void audioHandler::notified() -{ -} - - -void audioHandler::stateChanged(QAudio::State state) -{ - // Process the state - switch (state) - { - case QAudio::IdleState: - { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Audio now in idle state: " << audioBuffer.length() << " packets in buffer"; - if (audioOutput != Q_NULLPTR && audioOutput->error() == QAudio::UnderrunError) - { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "buffer underrun"; - audioOutput->suspend(); - } - break; - } - case QAudio::ActiveState: - { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Audio now in active state: " << audioBuffer.length() << " packets in buffer"; - break; - } - case QAudio::SuspendedState: - { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Audio now in suspended state: " << audioBuffer.length() << " packets in buffer"; - break; - } - case QAudio::StoppedState: - { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Audio now in stopped state: " << audioBuffer.length() << " packets in buffer"; - break; - } - default: { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Unhandled audio state: " << audioBuffer.length() << " packets in buffer"; - } - } -} - - - -void audioHandler::incomingAudio(audioPacket data) -{ - if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) { - QMutexLocker locker(&mutex); - - // Incoming data is 8bits? - if (radioSampleBits == 8) - { - QByteArray inPacket((int)data.datain.length() * 2, (char)0xff); - qint16* in = (qint16*)inPacket.data(); - for (int f = 0; f < data.datain.length(); f++) - { - if (isUlaw) - { - in[f] = ulaw_decode[(quint8)data.datain[f]]; - } - else - { - // Convert 8-bit sample to 16-bit - in[f] = (qint16)(((quint8)data.datain[f] << 8) - 32640); + // We still don't have enough buffer space for this? + break; } + //qDebug(logAudio()) << "Adding partial:" << send; } - data.datain = inPacket; // Replace incoming data with converted. + + if (currentLatency > (int)audioLatency) { + qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Packet " << hex << packet.seq << + " arrived too late (increase output latency!) " << + dec << packet.time.msecsTo(QTime::currentTime()) << "ms"; + lastSeq = packet.seq; + if (!ringBuf->try_read(packet)) + break; + currentLatency = packet.time.msecsTo(QTime::currentTime()); + } + + int send = qMin((int)nBytes - sentlen, packet.data.length()); + memcpy(buffer + sentlen, packet.data.constData(), send); + sentlen = sentlen + send; + if (send < packet.data.length()) + { + //qDebug(logAudio()) << "Asking for partial, sent:" << send << "packet length" << packet.data.length(); + tempBuf = packet; + tempBuf.sent = tempBuf.sent + send; + lastSeq = packet.seq; + break; + } + + if (packet.seq <= lastSeq) { + qDebug(logAudio()) << (isInput ? "Input" : "Output") << "Duplicate/early audio packet: " << hex << lastSeq << " got " << hex << packet.seq; + } + else if (packet.seq != lastSeq + 1) { + qDebug(logAudio()) << (isInput ? "Input" : "Output") << "Missing audio packet(s) from: " << hex << lastSeq + 1 << " to " << hex << packet.seq - 1; + } + lastSeq = packet.seq; } + } + //qDebug(logAudio()) << "looking for: " << nBytes << " got: " << sentlen; - //qInfo(logAudio()) << "Adding packet to buffer:" << data.seq << ": " << data.datain.length(); + // fill the rest of the buffer with silence + if (nBytes > sentlen) { + memset(buffer+sentlen,0,nBytes-sentlen); + } + return 0; +} - /* We now have an array of 16bit samples in the NATIVE samplerate of the radio - If the radio sample rate is below 48000, we need to resample. - */ - if (ratioDen != 1) { - - // We need to resample - quint32 outFrames = ((data.datain.length() / 2) * ratioDen) / radioChannels; - quint32 inFrames = (data.datain.length() / 2) / radioChannels; - data.dataout.resize(outFrames * 2 * radioChannels); // Preset the output buffer size. - - int err = 0; - if (this->radioChannels == 1) { - err = wf_resampler_process_int(resampler, 0, (const qint16*)data.datain.constData(), &inFrames, (qint16*)data.dataout.data(), &outFrames); - } - else { - err = wf_resampler_process_interleaved_int(resampler, (const qint16*)data.datain.constData(), &inFrames, (qint16*)data.dataout.data(), &outFrames); - } - if (err) { - qInfo(logAudio()) <<(isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } +int audioHandler::writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status) +{ + Q_UNUSED(outputBuffer); + Q_UNUSED(streamTime); + Q_UNUSED(status); + int sentlen = 0; + int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample + const char* data = (const char*)inputBuffer; + //qDebug(logAudio()) << "nFrames" << nFrames << "nBytes" << nBytes; + while (sentlen < nBytes) { + if (tempBuf.sent != nBytes) + { + int send = qMin((int)(nBytes - sentlen), (int)nBytes - tempBuf.sent); + tempBuf.data.append(QByteArray::fromRawData(data + sentlen, send)); + sentlen = sentlen + send; + tempBuf.seq = 0; // Not used in TX + tempBuf.time = QTime::currentTime(); + tempBuf.sent = tempBuf.sent + send; } else { - data.dataout = data.datain; - } - - inputBuffer.insert(data.seq, data); - - // Restart playback - if (audioOutput->state() == QAudio::SuspendedState) - { - qInfo(logAudio()) << "Output Audio Suspended, Resuming..."; - audioOutput->resume(); + if (!ringBuf->try_write(tempBuf)) + { + qDebug(logAudio()) << "outgoing audio buffer full!"; + break; + } + tempBuf.data.clear(); + tempBuf.sent = 0; } } + + //qDebug(logAudio()) << "sentlen" << sentlen; + + return 0; +} + + +void audioHandler::incomingAudio(audioPacket inPacket) +{ + // No point buffering audio until stream is actually running. + // Regardless of the radio stream format, the buffered audio will ALWAYS be + // 16bit sample interleaved stereo 48K (or whatever the native sample rate is) + + if (!audio.isStreamRunning()) + { + qDebug(logAudio()) << "Packet received before stream was started"; + return; + } + //qDebug(logAudio()) << "Got" << radioSampleBits << "bits, length" << inPacket.data.length(); + // Incoming data is 8bits? + if (radioSampleBits == 8) + { + // Current packet is 8bit so need to create a new buffer that is 16bit + QByteArray outPacket((int)inPacket.data.length() * 2 *(devChannels/radioChannels), (char)0xff); + qint16* out = (qint16*)outPacket.data(); + for (int f = 0; f < inPacket.data.length(); f++) + { + for (int g = radioChannels; g <= devChannels; g++) + { + if (isUlaw) + *out++ = ulaw_decode[(quint8)inPacket.data[f]] * this->volume; + else + *out++ = (qint16)(((quint8)inPacket.data[f] << 8) - 32640 * this->volume); + } + } + inPacket.data.clear(); + inPacket.data = outPacket; // Replace incoming data with converted. + } + else + { + // This is already a 16bit stream, do we need to convert to stereo? + if (radioChannels == 1 && devChannels > 1) { + // Yes + QByteArray outPacket(inPacket.data.length() * 2, (char)0xff); // Preset the output buffer size. + qint16* in = (qint16*)inPacket.data.data(); + qint16* out = (qint16*)outPacket.data(); + for (int f = 0; f < inPacket.data.length() / 2; f++) + { + *out++ = (qint16)*in * this->volume; + *out++ = (qint16)*in++ * this->volume; + } + inPacket.data.clear(); + inPacket.data = outPacket; // Replace incoming data with converted. + } + else + { + // We already have the same number of channels so just update volume. + qint16* in = (qint16*)inPacket.data.data(); + for (int f = 0; f < inPacket.data.length() / 2; f++) + { + *in = *in * this->volume; + in++; + } + } + + } + + /* We now have an array of 16bit samples in the NATIVE samplerate of the radio + If the radio sample rate is below 48000, we need to resample. + */ + //qDebug(logAudio()) << "Now 16 bit stereo, length" << inPacket.data.length(); + + if (ratioDen != 1) { + + // We need to resample + // We have a stereo 16bit stream. + quint32 outFrames = ((inPacket.data.length() / 2 / devChannels) * ratioDen); + quint32 inFrames = (inPacket.data.length() / 2 / devChannels); + QByteArray outPacket(outFrames * 4, (char)0xff); // Preset the output buffer size. + + const qint16* in = (qint16*)inPacket.data.constData(); + qint16* out = (qint16*)outPacket.data(); + + int err = 0; + err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames); + if (err) { + qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + } + inPacket.data.clear(); + inPacket.data = outPacket; // Replace incoming data with converted. + } + + //qDebug(logAudio()) << "Adding packet to buffer:" << inPacket.seq << ": " << inPacket.data.length(); + + if (!ringBuf->try_write(inPacket)) + { + qDebug(logAudio()) << "Buffer full! capacity:" << ringBuf->capacity() << "length" << ringBuf->size(); + } + return; } void audioHandler::changeLatency(const quint16 newSize) { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << latency; - latency = newSize; + qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << audioLatency; + audioLatency = newSize; } -void audioHandler::getLatency() +int audioHandler::getLatency() { - emit sendLatency(latency); -} - -bool audioHandler::isChunkAvailable() -{ - return (chunkAvailable); + return currentLatency; } void audioHandler::getNextAudioChunk(QByteArray& ret) { - if (!audioBuffer.isEmpty() && chunkAvailable) + audioPacket packet; + packet.sent = 0; + + if (ringBuf != Q_NULLPTR && ringBuf->try_read(packet)) { - QMutexLocker locker(&mutex); - - // Skip through audio buffer deleting any old entry. - auto packet = audioBuffer.begin(); - while (packet != audioBuffer.end()) + //qDebug(logAudio) << "Chunksize" << this->chunkSize << "Packet size" << packet.data.length(); + // Packet will arrive as stereo interleaved 16bit 48K + if (ratioNum != 1) { - if (packet->time.msecsTo(QTime::currentTime()) > latency) { - //qInfo(logAudio()) << "TX Packet too old " << dec << packet->time.msecsTo(QTime::currentTime()) << "ms"; - packet = audioBuffer.erase(packet); // returns next packet + quint32 outFrames = ((packet.data.length() / 2 / devChannels) / ratioNum); + quint32 inFrames = (packet.data.length() / 2 / devChannels); + QByteArray outPacket((int)outFrames * 2 * devChannels, (char)0xff); + + const qint16* in = (qint16*)packet.data.constData(); + qint16* out = (qint16*)outPacket.data(); + + int err = 0; + err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames); + if (err) { + qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; } - else { - if (packet->datain.length() == chunkSize && ret.length() == 0) - { - /* We now have an array of samples in the computer native format (48000) - If the radio sample rate is below 48000, we need to resample. - */ + //qInfo(logAudio()) << "Resampler run " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; + //qInfo(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length(); + packet.data.clear(); + packet.data = outPacket; // Copy output packet back to input buffer. + } - if (ratioNum != 1) - { - // We need to resample (we are STILL 16 bit!) - quint32 outFrames = ((packet->datain.length() / 2) / ratioNum) / radioChannels; - quint32 inFrames = (packet->datain.length() / 2) / radioChannels; - packet->dataout.resize(outFrames * 2 * radioChannels); // Preset the output buffer size. + //qDebug(logAudio()) << "Now resampled, length" << packet.data.length(); - const qint16* in = (qint16*)packet->datain.constData(); - qint16* out = (qint16*)packet->dataout.data(); - int err = 0; - if (this->radioChannels == 1) { - err = wf_resampler_process_int(resampler, 0, in, &inFrames, out, &outFrames); - } - else { - err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames); - } - if (err) { - qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - } - //qInfo(logAudio()) << "Resampler run " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; - //qInfo(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length(); - if (radioSampleBits == 8) - { - packet->datain=packet->dataout; // Copy output packet back to input buffer. - packet->dataout.clear(); // Buffer MUST be cleared ready to be re-filled by the upsampling below. - } - } - else if (radioSampleBits == 16 ){ - // Only copy buffer if radioSampleBits is 16, as it will be handled below otherwise. - packet->dataout = packet->datain; - } + // Do we need to convert mono to stereo? + if (radioChannels == 1 && devChannels > 1) + { + // Strip out right channel? + QByteArray outPacket(packet.data.length()/2, (char)0xff); + const qint16* in = (qint16*)packet.data.constData(); + qint16* out = (qint16*)outPacket.data(); + for (int f = 0; f < outPacket.length()/2; f++) + { + *out++ = *in++; + in++; // Skip each even channel. + } + packet.data.clear(); + packet.data = outPacket; // Copy output packet back to input buffer. + } + + //qDebug(logAudio()) << "Now mono, length" << packet.data.length(); - // Do we need to convert 16-bit to 8-bit? - if (radioSampleBits == 8) { - packet->dataout.resize(packet->datain.length() / 2); - qint16* in = (qint16*)packet->datain.data(); - for (int f = 0; f < packet->dataout.length(); f++) - { - quint8 outdata = 0; - if (isUlaw) { - qint16 enc = qFromLittleEndian(in + f); - if (enc >= 0) - outdata = ulaw_encode[enc]; - else - outdata = 0x7f & ulaw_encode[-enc]; - } - else { - outdata = (quint8)(((qFromLittleEndian(in + f) >> 8) ^ 0x80) & 0xff); - } - packet->dataout[f] = (char)outdata; - } - } - ret=packet->dataout; - packet = audioBuffer.erase(packet); // returns next packet + // Do we need to convert 16-bit to 8-bit? + if (radioSampleBits == 8) { + QByteArray outPacket((int)packet.data.length() / 2, (char)0xff); + qint16* in = (qint16*)packet.data.data(); + for (int f = 0; f < outPacket.length(); f++) + { + quint8 outdata = 0; + if (isUlaw) { + qint16 enc = qFromLittleEndian(in + f); + if (enc >= 0) + outdata = ulaw_encode[enc]; + else + outdata = 0x7f & ulaw_encode[-enc]; } else { - packet++; + outdata = (quint8)(((qFromLittleEndian(in + f) >> 8) ^ 0x80) & 0xff); } + outPacket[f] = (char)outdata; } + packet.data.clear(); + packet.data = outPacket; // Copy output packet back to input buffer. } + ret = packet.data; + //qDebug(logAudio()) << "Now radio format, length" << packet.data.length(); } - return; + + + return; + } diff --git a/audiohandler.h b/audiohandler.h index 4f5738d2..d9786442 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -4,19 +4,25 @@ #include #include -#include #include +#include #include -#include -#include -#include -#include -#include +#include +#include "rtaudio/RtAudio.h" + +typedef signed short MY_TYPE; +#define FORMAT RTAUDIO_SINT16 +#define SCALE 32767.0 + +#define LOG100 4.60517018599 + #include #include #include #include #include "resampler/speex_resampler.h" +#include "ring/ring.h" + #include @@ -24,47 +30,33 @@ #define INTERNAL_SAMPLE_RATE 48000 +#define MULAW_BIAS 33 +#define MULAW_MAX 0x1fff + struct audioPacket { quint32 seq; QTime time; quint16 sent; - QByteArray datain; - QByteArray dataout; + QByteArray data; }; - -class audioHandler : public QIODevice +class audioHandler : public QObject { Q_OBJECT - public: audioHandler(QObject* parent = 0); ~audioHandler(); - void getLatency(); + int getLatency(); - bool setDevice(QAudioDeviceInfo deviceInfo); - - void start(); - void flush(); - void stop(); - - qint64 readData(char* data, qint64 maxlen); - qint64 writeData(const char* data, qint64 len); - qint64 bytesAvailable() const; - bool isSequential() const; void getNextAudioChunk(QByteArray &data); - bool isChunkAvailable(); -public slots: - bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isulaw, const bool isinput, QAudioDeviceInfo port, quint8 resampleQuality); - void incomingAudio(const audioPacket data); - void changeLatency(const quint16 newSize); private slots: - void notified(); - void stateChanged(QAudio::State state); + bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isulaw, const bool isinput, int port, quint8 resampleQuality); + void changeLatency(const quint16 newSize); void setVolume(unsigned char volume); + void incomingAudio(const audioPacket data); signals: void audioMessage(QString message); @@ -73,32 +65,51 @@ signals: private: + int readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status); + + static int staticRead(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) { + return static_cast(userData)->readData(outputBuffer, inputBuffer, nFrames, streamTime, status); + } + + int writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status); + + static int staticWrite(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) { + return static_cast(userData)->writeData(outputBuffer, inputBuffer, nFrames, streamTime, status); + } + void reinit(); - QMutex mutex; - bool isInitialized; - QAudioOutput* audioOutput; - QAudioInput* audioInput; + RtAudio audio; + int audioDevice = 0; + RtAudio::StreamParameters aParams; + RtAudio::DeviceInfo info; + + SpeexResamplerState* resampler = Q_NULLPTR; + bool isUlaw; - quint16 latency; + quint16 audioLatency; bool isInput; // Used to determine whether input or output audio - int chunkSize; + unsigned int chunkSize; bool chunkAvailable; quint32 lastSeq; - QAudioFormat format; - QAudioDeviceInfo deviceInfo; quint16 radioSampleRate; quint8 radioSampleBits; quint8 radioChannels; - QVector audioBuffer; - QMapinputBuffer; - SpeexResamplerState* resampler=NULL; + QMapaudioBuffer; + unsigned int ratioNum; unsigned int ratioDen; + + wilt::Ring *ringBuf=Q_NULLPTR; + volatile bool ready = false; + audioPacket tempBuf; + quint16 currentLatency; + qreal volume=1.0; + int devChannels; }; #endif // AUDIOHANDLER_H diff --git a/main.cpp b/main.cpp index 24f50ea1..9a7143e7 100644 --- a/main.cpp +++ b/main.cpp @@ -117,12 +117,12 @@ int main(int argc, char *argv[]) // Set handler qInstallMessageHandler(messageHandler); - qInfo(logSystem()) << "Starting wfview"; - - - qDebug(logSystem()) << "SerialPortCL as set by parser: " << serialPortCL; - qDebug(logSystem()) << "remote host as set by parser: " << hostCL; - qDebug(logSystem()) << "CIV as set by parser: " << civCL; + qInfo(logSystem()) << QString("Starting wfview: build %1 on %2 at %3 by %5@%6").arg(GITSHORT).arg(__DATE__).arg(__TIME__).arg(UNAME).arg(HOST); + qInfo(logSystem()) << QString("Operating System: %1 (%2)").arg(QSysInfo::prettyProductName()).arg(QSysInfo::buildCpuArchitecture()); + qInfo(logSystem()) << QString("Build Qt Version %1. Current Qt Version: %2").arg(QT_VERSION_STR).arg(qVersion()); + qDebug(logSystem()) << QString("SerialPortCL as set by parser: %1").arg(serialPortCL); + qDebug(logSystem()) << QString("remote host as set by parser: %1").arg(hostCL); + qDebug(logSystem()) << QString("CIV as set by parser: %1").arg(civCL); a.setWheelScrollLines(1); // one line per wheel click wfmain w( serialPortCL, hostCL, settingsFile); diff --git a/opus-tools b/opus-tools new file mode 160000 index 00000000..ae5d6d59 --- /dev/null +++ b/opus-tools @@ -0,0 +1 @@ +Subproject commit ae5d6d59e82ef40300a4dece7897499685f87184 diff --git a/pttyhandler.cpp b/pttyhandler.cpp index d24f5f0a..61551e56 100644 --- a/pttyhandler.cpp +++ b/pttyhandler.cpp @@ -15,7 +15,7 @@ pttyHandler::pttyHandler(QString pty) { //constructor - if (pty == "" || pty == "None") + if (pty == "" || pty.toLower() == "none") { // Just return if pty is not configured. return; @@ -53,7 +53,7 @@ void pttyHandler::openPort() } #else // Generic method in Linux/MacOS to find a pty - ptfd = ::posix_openpt(O_RDWR | O_NOCTTY); + ptfd = ::posix_openpt(O_RDWR | O_NONBLOCK); if (ptfd >=0) { @@ -94,7 +94,7 @@ void pttyHandler::openPort() #ifndef Q_OS_WIN ptDevSlave = QString::fromLocal8Bit(ptsname(ptfd)); - if (portName != "" && portName != "None") + if (portName != "" && portName.toLower() != "none") { if (!QFile::link(ptDevSlave, portName)) { @@ -115,7 +115,17 @@ pttyHandler::~pttyHandler() void pttyHandler::receiveDataFromRigToPtty(const QByteArray& data) { - if (isConnected && (unsigned char)data[2] != (unsigned char)0xE1 && (unsigned char)data[3] != (unsigned char)0xE1) + + int fePos=data.lastIndexOf((char)0xfe); + if (fePos>0) + fePos=fePos-1; + else + { + qDebug(logSerial()) << "Invalid command"; + printHex(data,false,true); + } + + if (isConnected && (unsigned char)data.mid(fePos)[2] != (unsigned char)0xE1 && (unsigned char)data.mid(fePos)[3] != (unsigned char)0xE1) { // send to the pseudo port as well // index 2 is dest, 0xE1 is wfview, 0xE0 is assumed to be the other device. @@ -258,7 +268,7 @@ void pttyHandler::closePort() delete port; } #else - if (isConnected && portName != "" && portName != "None") + if (isConnected && portName != "" && portName.toLower() != "none") { QFile::remove(portName); } diff --git a/rigcommander.cpp b/rigcommander.cpp index f8b78a18..2539f396 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -170,9 +170,6 @@ void rigCommander::setup() // common elements between the two constructors go here: setCIVAddr(civAddr); spectSeqMax = 0; // this is now set after rig ID determined - payloadPrefix = QByteArray("\xFE\xFE"); - payloadPrefix.append(civAddr); - payloadPrefix.append((char)compCivAddr); payloadSuffix = QByteArray("\xFD"); @@ -183,6 +180,8 @@ void rigCommander::setup() pttAllowed = true; // This is for developing, set to false for "safe" debugging. Set to true for deployment. } + + void rigCommander::process() { // new thread enters here. Do nothing but do check for errors. @@ -841,6 +840,9 @@ quint16 rigCommander::decodeTone(QByteArray eTone, bool &tinv, bool &rinv) // CTCSS: 1B 01 00 12 73 = PL 127.3, decode as 1273 // D(T)CS: 1B 01 TR 01 23 = T/R Invert bits + DCS code 123 + if (eTone.length() < 5) { + return 0; + } tinv = false; rinv = false; quint16 result = 0; @@ -963,10 +965,13 @@ void rigCommander::setPTT(bool pttOn) void rigCommander::setCIVAddr(unsigned char civAddr) { - // Note: This is the radio's CIV address + // Note: This sets the radio's CIV address // the computer's CIV address is defined in the header file. - // TODO: this function *could* be written to re-write the CIV preamble. + this->civAddr = civAddr; + payloadPrefix = QByteArray("\xFE\xFE"); + payloadPrefix.append(civAddr); + payloadPrefix.append((char)compCivAddr); } void rigCommander::handleNewData(const QByteArray& data) diff --git a/ring/LICENSE b/ring/LICENSE new file mode 100644 index 00000000..b9a4d39d --- /dev/null +++ b/ring/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Trevor Wilson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ring/README.md b/ring/README.md new file mode 100644 index 00000000..2cdbbe9a --- /dev/null +++ b/ring/README.md @@ -0,0 +1,10 @@ +# Ring Library + +## Overview + +This library provides source for a multi-producer multi-consumer lock-free ring buffer. It provides a very simple interface for writing and reading from the buffer. The source includes a `Ring_` class, that provides the raw implementation and C-like facilities, as well as a templated `Ring` class for typed reads and writes. + + +## Contact + +If you have any questions, concerns, or recommendations please feel free to e-mail me at kmdreko@gmail.com. If you notice a bug or defect, create an issue to report it. diff --git a/ring/ring.cpp b/ring/ring.cpp new file mode 100644 index 00000000..d50555df --- /dev/null +++ b/ring/ring.cpp @@ -0,0 +1,290 @@ +//////////////////////////////////////////////////////////////////////////////// +// FILE: ring.cpp +// DATE: 2016-02-25 +// AUTH: Trevor Wilson +// DESC: Implements a lock-free, multi-consumer, multi-producer ring buffer +// class + +//////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2016 Trevor Wilson +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "ring.h" +using namespace wilt; + +#include +// - std::memcpy + +Ring_::Ring_() + : beg_(nullptr) + , end_(nullptr) +{ + std::atomic_init(&used_, static_cast(0)); + std::atomic_init(&free_, static_cast(0)); + std::atomic_init(&rbuf_, static_cast(0)); + std::atomic_init(&rptr_, static_cast(0)); + std::atomic_init(&wptr_, static_cast(0)); + std::atomic_init(&wbuf_, static_cast(0)); +} + +Ring_::Ring_(std::size_t size) + : beg_(new char[size]) + , end_(beg_ + size) +{ + std::atomic_init(&used_, static_cast(0)); + std::atomic_init(&free_, static_cast(size)); + std::atomic_init(&rbuf_, beg_); + std::atomic_init(&rptr_, beg_); + std::atomic_init(&wptr_, beg_); + std::atomic_init(&wbuf_, beg_); +} + +Ring_::Ring_(Ring_&& ring) + : beg_(ring.beg_) + , end_(ring.end_) +{ + std::atomic_init(&used_, ring.used_.load()); + std::atomic_init(&free_, ring.free_.load()); + std::atomic_init(&rbuf_, ring.rbuf_.load()); + std::atomic_init(&rptr_, ring.rptr_.load()); + std::atomic_init(&wptr_, ring.wptr_.load()); + std::atomic_init(&wbuf_, ring.wbuf_.load()); + + ring.beg_ = nullptr; + ring.end_ = nullptr; + + ring.used_.store(0); + ring.free_.store(0); + ring.rbuf_.store(nullptr); + ring.rptr_.store(nullptr); + ring.wptr_.store(nullptr); + ring.wbuf_.store(nullptr); +} + +Ring_& Ring_::operator= (Ring_&& ring) +{ + delete[] beg_; + + beg_ = ring.beg_; + end_ = ring.end_; + + used_.store(ring.used_.load()); + free_.store(ring.free_.load()); + rbuf_.store(ring.rbuf_.load()); + rptr_.store(ring.rptr_.load()); + wptr_.store(ring.wptr_.load()); + wbuf_.store(ring.wbuf_.load()); + + ring.beg_ = nullptr; + ring.end_ = nullptr; + + ring.used_.store(0); + ring.free_.store(0); + ring.rbuf_.store(nullptr); + ring.rptr_.store(nullptr); + ring.wptr_.store(nullptr); + ring.wbuf_.store(nullptr); + + return *this; +} + +Ring_::~Ring_() +{ + delete[] beg_; +} + +std::size_t Ring_::size() const +{ + // The 'used' space can be negative in an over-reserved case, but it can be + // clamped to 0 for simplicity. + + auto s = used_.load(); + return s < 0 ? 0 : static_cast(s); +} + +std::size_t Ring_::capacity() const +{ + return static_cast(end_ - beg_); +} + +void Ring_::read(void* data, std::size_t length) noexcept +{ + auto block = acquire_read_block_(length); + + copy_read_block_(block, (char*)data, length); + release_read_block_(block, length); +} + +void Ring_::write(const void* data, std::size_t length) noexcept +{ + auto block = acquire_write_block_(length); + + copy_write_block_(block, (const char*)data, length); + release_write_block_(block, length); +} + +bool Ring_::try_read(void* data, std::size_t length) noexcept +{ + auto block = try_acquire_read_block_(length); + if (block == nullptr) + return false; + + copy_read_block_(block, (char*)data, length); + release_read_block_(block, length); + + return true; +} + +bool Ring_::try_write(const void* data, std::size_t length) noexcept +{ + auto block = try_acquire_write_block_(length); + if (block == nullptr) + return false; + + copy_write_block_(block, (const char*)data, length); + release_write_block_(block, length); + + return true; +} + +char* Ring_::normalize_(char* ptr) +{ + return ptr < end_ ? ptr : ptr - capacity(); +} + +char* Ring_::acquire_read_block_(std::size_t length) +{ + auto size = static_cast(length); + while (true) // loop while conflict + { + auto old_rptr = rptr_.load(std::memory_order_consume); // read rptr + while (used_.load(std::memory_order_consume) < size) // check for data + ; // spin until success + + auto new_rptr = normalize_(old_rptr + size); // get block end + used_.fetch_sub(size); // reserve + if (rptr_.compare_exchange_strong(old_rptr, new_rptr)) // try commit + return old_rptr; // committed + + used_.fetch_add(size, std::memory_order_relaxed); // un-reserve + } +} + +char* Ring_::try_acquire_read_block_(std::size_t length) +{ + auto size = static_cast(length); + while (true) // loop while conflict + { + auto old_rptr = rptr_.load(std::memory_order_consume); // read rptr + if (used_.load(std::memory_order_consume) < size) // check for data + return nullptr; // return failure + + auto new_rptr = normalize_(old_rptr + size); // get block end + used_.fetch_sub(size); // reserve + if (rptr_.compare_exchange_strong(old_rptr, new_rptr)) // try commit + return old_rptr; // committed + + used_.fetch_add(size, std::memory_order_relaxed); // un-reserve + } +} + +void Ring_::copy_read_block_(const char* block, char* data, std::size_t length) +{ + if (block + length < end_) + { + std::memcpy(data, block, length); + } + else + { + auto first = end_ - block; + std::memcpy(data, block, first); + std::memcpy(data + first, beg_, length - first); + } +} + +void Ring_::release_read_block_(char* old_rptr, std::size_t length) +{ + auto new_rptr = normalize_(old_rptr + length); // get block end + while (rbuf_.load() != old_rptr) // check for earlier reads + ; // spin until reads complete + + rbuf_.store(new_rptr); // finish commit + free_.fetch_add(length, std::memory_order_relaxed); // add to free space +} + +char* Ring_::acquire_write_block_(std::size_t length) +{ + auto size = static_cast(length); + while (true) // loop while conflict + { + auto old_wbuf = wbuf_.load(std::memory_order_consume); // read wbuf + while (free_.load(std::memory_order_consume) < size) // check for space + ; // spin until success + + auto new_wbuf = normalize_(old_wbuf + size); // get block end + free_.fetch_sub(size); // reserve + if (wbuf_.compare_exchange_strong(old_wbuf, new_wbuf)) // try commit + return old_wbuf; // committed + + free_.fetch_add(size, std::memory_order_relaxed); // un-reserve + } +} + +char* Ring_::try_acquire_write_block_(std::size_t length) +{ + auto size = static_cast(length); + while (true) // loop while conflict + { + auto old_wbuf = wbuf_.load(std::memory_order_consume); // read wbuf + if (free_.load(std::memory_order_consume) < size) // check for space + return nullptr; // return failure + + auto new_wbuf = normalize_(old_wbuf + size); // get block end + free_.fetch_sub(size); // reserve + if (wbuf_.compare_exchange_strong(old_wbuf, new_wbuf)) // try commit + return old_wbuf; // committed + + free_.fetch_add(size, std::memory_order_relaxed); // un-reserve + } +} + +void Ring_::copy_write_block_(char* block, const char* data, std::size_t length) +{ + if (block + length < end_) + { + std::memcpy(block, data, length); + } + else + { + auto first = end_ - block; + std::memcpy(block, data, first); + std::memcpy(beg_, data + first, length - first); + } +} + +void Ring_::release_write_block_(char* old_wbuf, std::size_t length) +{ + auto new_wbuf = normalize_(old_wbuf + length); // get block end + while (wptr_.load() != old_wbuf) // wait for earlier writes + ; // spin until writes complete + + wptr_.store(new_wbuf); // finish commit + used_.fetch_add(length, std::memory_order_relaxed); // add to used space +} diff --git a/ring/ring.h b/ring/ring.h new file mode 100644 index 00000000..cec58526 --- /dev/null +++ b/ring/ring.h @@ -0,0 +1,440 @@ +//////////////////////////////////////////////////////////////////////////////// +// FILE: ring.h +// DATE: 2016-02-25 +// AUTH: Trevor Wilson +// DESC: Defines a lock-free, multi-consumer, multi-producer ring buffer class + +//////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2016 Trevor Wilson +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef WILT_RING_H +#define WILT_RING_H + +#include +// - std::atomic +#include +// - std::size_t +// - std::ptrdiff_t +#include +// - ::new(ptr) +#include +// - std::is_nothrow_copy_constructible +// - std::is_nothrow_move_constructible +// - std::is_nothrow_move_assignable +// - std::is_nothrow_destructible +#include +// - std::move + +namespace wilt +{ + ////////////////////////////////////////////////////////////////////////////// + // This structure aims to access elements in a ring buffer from multiple + // concurrent readers and writers in a lock-free manner. + // + // The class works by allocating the array and storing two pointers (for the + // beginning and end of the allocated space). Two atomic pointers are used to + // track the beginning and end of the currently used storage space. To + // facilitate concurrent reads and writes, theres a read buffer pointer before + // the read pointer for data currently being read, and a corresponding write + // buffer pointer beyond the write pointer for data currently being written. + // These buffer pointers cannot overlap. Just using these pointers suffer from + // some minute inefficiencies and a few ABA problems. Therfore, atomic + // integers are used to store the currently used and currently free sizes. + // + // It allows multiple readers and multiple writers by implementing a reserve- + // commit system. A thread wanting to read will check the used size to see if + // there's enough data. If there is, it subtracts from the used size to + // 'reserve' the read. It then does a compare-exchange to 'commit' by + // increasing the read pointer. If that fails, then it backs out ('un- + // reserves') by adding back to the used size and tries again. If it + // succeeds, then it proceeds to read the data. In order to complete, the + // reader must update the read buffer pointer to where it just finished + // reading from. However, because other readers that started before may not be + // done yet, the reader must wait until the read buffer pointer points to + // where the read started. Only, then is the read buffer pointer updated, and + // the free size increased. So while this implementation is lock-free, it is + // not wait-free. This same principle works the same when writing (ammended + // for the appropriate pointers). + // + // If two readers try to read at the same time and there is only enough data + // for one of them. The used size MAY be negative because they both 'reserve' + // the data. This is an over-reserved state. But the compare-exchange will + // only allow one reader to 'commit' to the read and the other will 'un- + // reserve' the read. + // + // |beg |rptr used=5 |wbuf - unused + // |----|----|++++|====|====|====|====|====|++++|----| + modifying + // free=3 |rbuf |wptr |end = used + // + // The diagram above shows a buffer of size 10 storing 5 bytes with a reader + // reading one byte and one writer reading one byte. + // + // Out of the box, the class works by reading and writing raw bytes from POD + // data types and arrays. A wrapper could allow for a nicer interface for + // pushing and popping elements. As it stands, this structure cannot be easily + // modified to store types of variable size. + + class Ring_ + { + private: + //////////////////////////////////////////////////////////////////////////// + // TYPE DEFINITIONS + //////////////////////////////////////////////////////////////////////////// + + typedef char* data_ptr; + typedef std::atomic size_type; + typedef std::atomic atom_ptr; + + private: + //////////////////////////////////////////////////////////////////////////// + // PRIVATE MEMBERS + //////////////////////////////////////////////////////////////////////////// + // Beginning and end pointers don't need to be atomic because they don't + // change. used_ and free_ can be negative in certain cases (and that's ok). + + data_ptr beg_; // pointer to beginning of data block + data_ptr end_; // pointer to end of data block + + alignas(64) + size_type used_; // size of unreserved used space + + alignas(64) + size_type free_; // size of unreserved free space + + alignas(64) + atom_ptr rbuf_; // pointer to beginning of data being read + atom_ptr rptr_; // pointer to beginning of data + + alignas(64) + atom_ptr wptr_; // pointer to end of data + atom_ptr wbuf_; // pointer to end of data being written + + public: + //////////////////////////////////////////////////////////////////////////// + // CONSTRUCTORS AND DESTRUCTORS + //////////////////////////////////////////////////////////////////////////// + + // Constructs a ring without a buffer (capacity() == 0) + Ring_(); + + // Constructs a ring with a buffer with a size + Ring_(std::size_t size); + + // Moves the buffer between rings, assumes no concurrent operations + Ring_(Ring_&& ring); + + // Moves the buffer between rings, assumes no concurrent operations on + // either ring. Deallocates the buffer + Ring_& operator= (Ring_&& ring); + + // No copying + Ring_(const Ring_&) = delete; + Ring_& operator= (const Ring_&) = delete; + + // Deallocates the buffer + ~Ring_(); + + public: + //////////////////////////////////////////////////////////////////////////// + // QUERY FUNCTIONS + //////////////////////////////////////////////////////////////////////////// + // Functions only report on the state of the ring + + // Returns the current amount of non-reserved used space (amount of written + // data that a read hasn't yet reserved). Over-reserved scenarios mean this + // number is not the ultimate source of truth with concurrent operations, + // but its the closest safe approximation. This, of course, doesn't report + // writes that have not completed. + std::size_t size() const; + + // Maximum amount of data that can be held + std::size_t capacity() const; + + public: + //////////////////////////////////////////////////////////////////////////// + // ACCESSORS AND MODIFIERS + //////////////////////////////////////////////////////////////////////////// + // All operations assume object has not been moved. Blocking operations run + // until operation is completed. Non-blocking operations fail if there is + // not enough space + + void read(void* data, std::size_t length) noexcept; + void write(const void* data, std::size_t length) noexcept; + bool try_read(void* data, std::size_t length) noexcept; + bool try_write(const void* data, std::size_t length) noexcept; + + protected: + //////////////////////////////////////////////////////////////////////////// + // PROTECTED FUNCTIONS + //////////////////////////////////////////////////////////////////////////// + // Helper functions + + // Wraps a pointer within the array. Assumes 'beg_ <= ptr < end_+capacity()' + char* normalize_(char*); + + char* acquire_read_block_(std::size_t length); + char* try_acquire_read_block_(std::size_t length); + void copy_read_block_(const char* block, char* data, std::size_t length); + void release_read_block_(char* block, std::size_t length); + + char* acquire_write_block_(std::size_t length); + char* try_acquire_write_block_(std::size_t length); + void copy_write_block_(char* block, const char* data, std::size_t length); + void release_write_block_(char* block, std::size_t length); + + char* begin_alloc_() { return beg_; } + const char* begin_alloc_() const { return beg_; } + char* end_alloc_() { return end_; } + const char* end_alloc_() const { return end_; } + char* begin_data_() { return rptr_; } + const char* begin_data_() const { return rptr_; } + char* end_data_() { return wptr_; } + const char* end_data_() const { return wptr_; } + + }; // class Ring_ + + template + class Ring : protected Ring_ + { + public: + //////////////////////////////////////////////////////////////////////////// + // CONSTRUCTORS AND DESTRUCTORS + //////////////////////////////////////////////////////////////////////////// + + // Constructs a ring without a buffer (capacity() == 0) + Ring(); + + // Constructs a ring with a buffer with a size + Ring(std::size_t size); + + // Moves the buffer between rings, assumes no concurrent operations + Ring(Ring&& ring); + + // Moves the buffer between rings, assumes no concurrent operations on + // either ring. Deallocates the buffer + Ring& operator= (Ring&& ring); + + // No copying + Ring(const Ring_&) = delete; + Ring& operator= (const Ring_&) = delete; + + // Deallocates the buffer, destructs stored data. + ~Ring(); + + public: + //////////////////////////////////////////////////////////////////////////// + // QUERY FUNCTIONS + //////////////////////////////////////////////////////////////////////////// + // Functions only report on the state of the ring + + // Returns the current amount of non-reserved used space (amount of written + // data that a read hasn't yet reserved). Over-reserved scenarios mean this + // number is not the ultimate source of truth with concurrent operations, + // but its the closest safe approximation. This, of course, doesn't report + // writes that have not completed. + std::size_t size() const; + + // Maximum amount of data that can be held + std::size_t capacity() const; + + public: + //////////////////////////////////////////////////////////////////////////// + // ACCESSORS AND MODIFIERS + //////////////////////////////////////////////////////////////////////////// + // All operations assume object has not been moved. Blocking operations run + // until operation is completed. Non-blocking operations fail if there is + // not enough space + + void read(T& data) noexcept; // blocking read + void write(const T& data) noexcept; // blocking write + void write(T&& data) noexcept; // blocking write + bool try_read(T& data) noexcept; // non-blocking read + bool try_write(const T& data) noexcept; // non-blocking write + bool try_write(T&& data) noexcept; // non-blocking write + + private: + //////////////////////////////////////////////////////////////////////////// + // PRIVATE HELPER FUNCTIONS + //////////////////////////////////////////////////////////////////////////// + + void destruct_(); + + }; // class Ring + + template + Ring::Ring() + : Ring_() + { } + + template + Ring::Ring(std::size_t size) + : Ring_(size * sizeof(T)) + { } + + template + Ring::Ring(Ring&& ring) + : Ring_(std::move(ring)) + { } + + template + Ring& Ring::operator= (Ring&& ring) + { + destruct_(); + + Ring_::operator= (ring); + + return *this; + } + + template + Ring::~Ring() + { + destruct_(); + } + + template + void Ring::destruct_() + { + if (size() == 0) + return; + + auto itr = begin_data_(); + auto end = end_data_(); + do + { + auto t = reinterpret_cast(itr); + t->~T(); + + itr = normalize_(itr + sizeof(T)); + } while (itr != end); + } + + template + std::size_t Ring::size() const + { + return Ring_::size() / sizeof(T); + } + + template + std::size_t Ring::capacity() const + { + return Ring_::capacity() / sizeof(T); + } + + template + void Ring::read(T& data) noexcept + { + static_assert(std::is_nothrow_move_assignable::value, "T move assignment must not throw"); + static_assert(std::is_nothrow_destructible::value, "T destructor must not throw"); + + auto block = acquire_read_block_(sizeof(T)); + + // critical section + auto t = reinterpret_cast(block); + data = std::move(*t); + t->~T(); + + release_read_block_(block, sizeof(T)); + } + + template + void Ring::write(const T& data) noexcept + { + static_assert(std::is_nothrow_copy_constructible::value, "T copy constructor must not throw"); + + auto block = acquire_write_block_(sizeof(T)); + + // critical section + new(block) T(data); + + release_write_block_(block, sizeof(T)); + } + + template + void Ring::write(T&& data) noexcept + { + static_assert(std::is_nothrow_move_constructible::value, "T move constructor must not throw"); + + auto block = acquire_write_block_(sizeof(T)); + + // critical section + new(block) T(std::move(data)); + + release_write_block_(block, sizeof(T)); + } + + template + bool Ring::try_read(T& data) noexcept + { + static_assert(std::is_nothrow_move_assignable::value, "T move assignment must not throw"); + static_assert(std::is_nothrow_destructible::value, "T destructor must not throw"); + + auto block = try_acquire_read_block_(sizeof(T)); + if (block == nullptr) + return false; + + // critical section + auto t = reinterpret_cast(block); + data = std::move(*t); + t->~T(); + + release_read_block_(block, sizeof(T)); + + return true; + } + + template + bool Ring::try_write(const T& data) noexcept + { + static_assert(std::is_nothrow_copy_constructible::value, "T copy constructor must not throw"); + + auto block = try_acquire_write_block_(sizeof(T)); + if (block == nullptr) + return false; + + // critical section + new(block) T(data); + + release_write_block_(block, sizeof(T)); + + return true; + } + + template + bool Ring::try_write(T&& data) noexcept + { + static_assert(std::is_nothrow_move_constructible::value, "T move constructor must not throw"); + + auto block = try_acquire_write_block_(sizeof(T)); + if (block == nullptr) + return false; + + // critical section + new(block) T(std::move(data)); + + release_write_block_(block, sizeof(T)); + + return true; + } + +} // namespace wilt + +#endif // !WILT_RING_H \ No newline at end of file diff --git a/rtaudio b/rtaudio new file mode 160000 index 00000000..c9bf99d4 --- /dev/null +++ b/rtaudio @@ -0,0 +1 @@ +Subproject commit c9bf99d414cf81d19ef0ddd00212a4a58ccd99c6 diff --git a/udphandler.cpp b/udphandler.cpp index ac2c4221..d2fde850 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -14,8 +14,8 @@ udpHandler::udpHandler(udpPreferences prefs) : txLatency(prefs.audioTXLatency), rxCodec(prefs.audioRXCodec), txCodec(prefs.audioTXCodec), - audioInputPort(prefs.inputDevice), - audioOutputPort(prefs.outputDevice), + audioInputPort(prefs.audioInput), + audioOutputPort(prefs.audioOutput), resampleQuality(prefs.resampleQuality) { @@ -189,7 +189,15 @@ void udpHandler::dataReceived() totallost = totallost + civ->packetsLost; } - emit haveNetworkStatus(" rtt: " + QString::number(latency) + " ms, loss: (" + QString::number(totallost) + "/" + QString::number(totalsent) + ")"); + QString tempLatency; + if (rxLatency > audio->audioLatency) + { + tempLatency = QString("%1 ms").arg(audio->audioLatency,3); + } + else { + tempLatency = QString("%1 ms").arg(audio->audioLatency,3); + } + emit haveNetworkStatus(QString("
rx latency: %1 ms / rtt: %2 ms / loss: %3/%4
").arg(tempLatency).arg(latency, 3).arg(totallost,3).arg(totalsent,3)); } break; } @@ -582,7 +590,7 @@ void udpCivData::watchdog() void udpCivData::send(QByteArray d) { - // qInfo(logUdp()) << "Sending: (" << d.length() << ") " << d; + //qInfo(logUdp()) << "Sending: (" << d.length() << ") " << d; data_packet p; memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! p.len = sizeof(p)+d.length(); @@ -634,6 +642,7 @@ void udpCivData::dataReceived() //qInfo(logUdp()) << "Received: " << datagram.data(); QByteArray r = datagram.data(); + switch (r.length()) { case (CONTROL_SIZE): // Control packet @@ -670,7 +679,9 @@ void udpCivData::dataReceived() lastReceived = QTime::currentTime(); if (quint16(in->datalen + 0x15) == (quint16)in->len) { - emit receive(r.mid(0x15)); + //if (r.mid(0x15).length() != 157) + emit receive(r.mid(0x15)); + //qDebug(logUdp()) << "Got incoming CIV datagram" << r.mid(0x15).length(); } } @@ -679,6 +690,7 @@ void udpCivData::dataReceived() } } udpBase::dataReceived(r); // Call parent function to process the rest. + r.clear(); datagram.clear(); @@ -687,7 +699,7 @@ void udpCivData::dataReceived() // Audio stream -udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, QAudioDeviceInfo outputPort, QAudioDeviceInfo inputPort,quint8 resampleQuality) +udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, int outputPort, int inputPort, quint8 resampleQuality) { qInfo(logUdp()) << "Starting udpAudio"; this->localIP = local; @@ -736,8 +748,9 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint rxAudioThread->start(); - connect(this, SIGNAL(setupRxAudio(quint8, quint8, quint16, quint16, bool, bool, QAudioDeviceInfo, quint8)), rxaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool,QAudioDeviceInfo, quint8))); + connect(this, SIGNAL(setupRxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), rxaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); + // signal/slot not currently used. connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); @@ -758,7 +771,8 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint txAudioThread->start(); - connect(this, SIGNAL(setupTxAudio(quint8, quint8, quint16, quint16, bool, bool,QAudioDeviceInfo,quint8)), txaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool,QAudioDeviceInfo,quint8))); + connect(this, SIGNAL(setupTxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), txaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); + connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); sendControl(false, 0x03, 0x00); // First connect packet @@ -842,7 +856,7 @@ void udpAudio::watchdog() /* Just log it at the moment, maybe try signalling the control channel that it needs to try requesting civ/audio again? */ - qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required"; + qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required?"; alerted = true; } } @@ -855,10 +869,12 @@ void udpAudio::watchdog() void udpAudio::sendTxAudio() { - - if (txaudio && txaudio->isChunkAvailable()) { - QByteArray audio; - txaudio->getNextAudioChunk(audio); + if (txaudio == Q_NULLPTR) { + return; + } + QByteArray audio; + txaudio->getNextAudioChunk(audio); + if (audio.length() > 0) { int counter = 1; int len = 0; @@ -942,11 +958,12 @@ void udpAudio::dataReceived() tempAudio.seq = (quint32)seqPrefix << 16 | in->seq; tempAudio.time = lastReceived; tempAudio.sent = 0; - tempAudio.datain = r.mid(0x18); + tempAudio.data = r.mid(0x18); // Prefer signal/slot to forward audio as it is thread/safe // Need to do more testing but latency appears fine. + //audioLatency = rxaudio->incomingAudio(tempAudio); emit haveAudioData(tempAudio); - //rxaudio->incomingAudio(tempAudio); + audioLatency = rxaudio->getLatency(); } break; } diff --git a/udphandler.h b/udphandler.h index 3db6c1ed..a835d957 100644 --- a/udphandler.h +++ b/udphandler.h @@ -41,10 +41,11 @@ struct udpPreferences { quint16 audioLANPort; QString username; QString password; - QString audioOutput; - QString audioInput; - QAudioDeviceInfo inputDevice; - QAudioDeviceInfo outputDevice; + int audioOutput; + int audioInput; + QString audioOutputName; + QString audioInputName; + quint16 audioRXLatency; quint16 audioTXLatency; quint16 audioRXSampleRate; @@ -174,14 +175,16 @@ class udpAudio : public udpBase Q_OBJECT public: - udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, QAudioDeviceInfo outputPort, QAudioDeviceInfo inputPort,quint8 resampleQuality); + udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 rxlatency, quint16 txlatency, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec, int outputPort, int inputPort, quint8 resampleQuality); ~udpAudio(); + int audioLatency = 0; + signals: void haveAudioData(audioPacket data); - void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, QAudioDeviceInfo port, quint8 resampleQuality); - void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, QAudioDeviceInfo port, quint8 resampleQuality); + void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, int port, quint8 resampleQuality); + void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, int port, quint8 resampleQuality); void haveChangeLatency(quint16 value); void haveSetVolume(unsigned char value); @@ -288,8 +291,8 @@ private: quint8 rxCodec; quint8 txCodec; - QAudioDeviceInfo audioInputPort; - QAudioDeviceInfo audioOutputPort; + int audioInputPort; + int audioOutputPort; quint8 resampleQuality; diff --git a/udpserver.cpp b/udpserver.cpp index dd26a51d..14ae346d 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -392,11 +392,11 @@ void udpServer::controlReceived() txAudioThread->start(); - connect(this, SIGNAL(setupTxAudio(quint8, quint8, quint16, quint16, bool, bool, QAudioDeviceInfo, quint8)), txaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool, QAudioDeviceInfo, quint8))); + connect(this, SIGNAL(setupTxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), txaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); - emit setupTxAudio(samples, channels, current->txSampleRate, current->txBufferLen, uLaw, false, config.outputDevice, config.resampleQuality); - hasTxAudio = datagram.senderAddress(); + emit setupTxAudio(samples, channels, current->txSampleRate, current->txBufferLen, uLaw, false, config.audioOutput, config.resampleQuality); + hasTxAudio=datagram.senderAddress(); connect(this, SIGNAL(haveAudioData(audioPacket)), txaudio, SLOT(incomingAudio(audioPacket))); @@ -425,10 +425,10 @@ void udpServer::controlReceived() rxaudio->moveToThread(rxAudioThread); rxAudioThread->start(); - connect(this, SIGNAL(setupRxAudio(quint8, quint8, quint16, quint16, bool, bool, QAudioDeviceInfo, quint8)), rxaudio, SLOT(init(quint8, quint8, quint16, quint16, bool, bool, QAudioDeviceInfo, quint8))); - connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); + connect(this, SIGNAL(setupRxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), rxaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); + connect(rxAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); - emit setupRxAudio(samples, channels, current->rxSampleRate, 150, uLaw, true, config.inputDevice, config.resampleQuality); + emit setupRxAudio(samples, channels, current->rxSampleRate,150, uLaw, true, config.audioInput, config.resampleQuality); rxAudioTimer = new QTimer(); rxAudioTimer->setTimerType(Qt::PreciseTimer); @@ -695,7 +695,7 @@ void udpServer::audioReceived() tempAudio.seq = (quint32)current->seqPrefix << 16 | in->seq; tempAudio.time = QTime::currentTime();; tempAudio.sent = 0; - tempAudio.datain = r.mid(0x18); + tempAudio.data = r.mid(0x18); //qInfo(logUdpServer()) << "sending tx audio " << in->seq; emit haveAudioData(tempAudio); } @@ -1298,16 +1298,16 @@ void udpServer::dataForServer(QByteArray d) void udpServer::sendRxAudio() { - if (rxaudio && rxaudio->isChunkAvailable()) { - QByteArray audio; + QByteArray audio; + if (rxaudio) { + audio.clear(); rxaudio->getNextAudioChunk(audio); int len = 0; - while (len < audio.length()) { audioPacket partial; - partial.datain = audio.mid(len, 1364); + partial.data = audio.mid(len, 1364); receiveAudioData(partial); - len = len + partial.datain.length(); + len = len + partial.data.length(); } } } @@ -1322,15 +1322,15 @@ void udpServer::receiveAudioData(const audioPacket &d) if (client != Q_NULLPTR && client->connected) { audio_packet p; memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p) + d.datain.length(); + p.len = sizeof(p) + d.data.length(); p.sentid = client->myId; p.rcvdid = client->remoteId; p.ident = 0x0080; // audio is always this? - p.datalen = (quint16)qToBigEndian((quint16)d.datain.length()); + p.datalen = (quint16)qToBigEndian((quint16)d.data.length()); p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN! p.seq = client->txSeq; QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - t.append(d.datain); + t.append(d.data); client->txMutex.lock(); client->txSeqBuf.append(SEQBUFENTRY()); client->txSeqBuf.last().seqNum = p.seq; diff --git a/udpserver.h b/udpserver.h index e2db2fc5..25083d8e 100644 --- a/udpserver.h +++ b/udpserver.h @@ -50,8 +50,10 @@ public slots: signals: void haveDataFromServer(QByteArray); void haveAudioData(audioPacket data); - void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, QAudioDeviceInfo port, quint8 resampleQuality); - void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, QAudioDeviceInfo port, quint8 resampleQuality); + + void setupTxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, int device, quint8 resampleQuality); + void setupRxAudio(const quint8 samples, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isUlaw, const bool isInput, int device, quint8 resampleQuality); + private: diff --git a/udpserversetup.h b/udpserversetup.h index 1fc9dcf5..92966df8 100644 --- a/udpserversetup.h +++ b/udpserversetup.h @@ -21,10 +21,8 @@ struct SERVERCONFIG { quint16 controlPort; quint16 civPort; quint16 audioPort; - QString audioOutput; - QString audioInput; - QAudioDeviceInfo inputDevice; - QAudioDeviceInfo outputDevice; + int audioOutput; + int audioInput; quint8 resampleQuality; quint32 baudRate; diff --git a/ulaw.h b/ulaw.h new file mode 100644 index 00000000..9d673a51 --- /dev/null +++ b/ulaw.h @@ -0,0 +1,728 @@ +#ifndef ULAW_H +#define ULAW_H + + +static unsigned char ulaw_encode[8193] = +{ 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, + 0xf9, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, + 0xf3, 0xf2, 0xf2, 0xf1, 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, + 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, + 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, + 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, + 0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcb, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, + 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, + 0xc8, 0xc8, 0xc8, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 +}; + +static const qint16 ulaw_decode[256] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, + -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, + -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, + -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 +}; + + + +#endif diff --git a/wfmain.cpp b/wfmain.cpp index f0ba1ca8..f6b033d8 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -32,381 +32,6 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s connect(srv, SIGNAL(serverConfig(SERVERCONFIG, bool)), this, SLOT(serverConfigRequested(SERVERCONFIG, bool))); qRegisterMetaType(); // Needs to be registered early. - - haveRigCaps = false; - - ui->bandStkLastUsedBtn->setVisible(false); - ui->bandStkVoiceBtn->setVisible(false); - ui->bandStkDataBtn->setVisible(false); - ui->bandStkCWBtn->setVisible(false); - - keyF1 = new QShortcut(this); - keyF1->setKey(Qt::Key_F1); - connect(keyF1, SIGNAL(activated()), this, SLOT(shortcutF1())); - - keyF2 = new QShortcut(this); - keyF2->setKey(Qt::Key_F2); - connect(keyF2, SIGNAL(activated()), this, SLOT(shortcutF2())); - - keyF3 = new QShortcut(this); - keyF3->setKey(Qt::Key_F3); - connect(keyF3, SIGNAL(activated()), this, SLOT(shortcutF3())); - - keyF4 = new QShortcut(this); - keyF4->setKey(Qt::Key_F4); - connect(keyF4, SIGNAL(activated()), this, SLOT(shortcutF4())); - - keyF5 = new QShortcut(this); - keyF5->setKey(Qt::Key_F5); - connect(keyF5, SIGNAL(activated()), this, SLOT(shortcutF5())); - - keyF6 = new QShortcut(this); - keyF6->setKey(Qt::Key_F6); - connect(keyF6, SIGNAL(activated()), this, SLOT(shortcutF6())); - - keyF7 = new QShortcut(this); - keyF7->setKey(Qt::Key_F7); - connect(keyF7, SIGNAL(activated()), this, SLOT(shortcutF7())); - - keyF8 = new QShortcut(this); - keyF8->setKey(Qt::Key_F8); - connect(keyF8, SIGNAL(activated()), this, SLOT(shortcutF8())); - - keyF9 = new QShortcut(this); - keyF9->setKey(Qt::Key_F9); - connect(keyF9, SIGNAL(activated()), this, SLOT(shortcutF9())); - - keyF10 = new QShortcut(this); - keyF10->setKey(Qt::Key_F10); - connect(keyF10, SIGNAL(activated()), this, SLOT(shortcutF10())); - - keyF11 = new QShortcut(this); - keyF11->setKey(Qt::Key_F11); - connect(keyF11, SIGNAL(activated()), this, SLOT(shortcutF11())); - - keyF12 = new QShortcut(this); - keyF12->setKey(Qt::Key_F12); - connect(keyF12, SIGNAL(activated()), this, SLOT(shortcutF12())); - - keyControlT = new QShortcut(this); - keyControlT->setKey(Qt::CTRL + Qt::Key_T); - connect(keyControlT, SIGNAL(activated()), this, SLOT(shortcutControlT())); - - keyControlR = new QShortcut(this); - keyControlR->setKey(Qt::CTRL + Qt::Key_R); - connect(keyControlR, SIGNAL(activated()), this, SLOT(shortcutControlR())); - - keyControlI = new QShortcut(this); - keyControlI->setKey(Qt::CTRL + Qt::Key_I); - connect(keyControlI, SIGNAL(activated()), this, SLOT(shortcutControlI())); - - keyControlU = new QShortcut(this); - keyControlU->setKey(Qt::CTRL + Qt::Key_U); - connect(keyControlU, SIGNAL(activated()), this, SLOT(shortcutControlU())); - - keyStar = new QShortcut(this); - keyStar->setKey(Qt::Key_Asterisk); - connect(keyStar, SIGNAL(activated()), this, SLOT(shortcutStar())); - - keySlash = new QShortcut(this); - keySlash->setKey(Qt::Key_Slash); - connect(keySlash, SIGNAL(activated()), this, SLOT(shortcutSlash())); - - keyMinus = new QShortcut(this); - keyMinus->setKey(Qt::Key_Minus); - connect(keyMinus, SIGNAL(activated()), this, SLOT(shortcutMinus())); - - keyPlus = new QShortcut(this); - keyPlus->setKey(Qt::Key_Plus); - connect(keyPlus, SIGNAL(activated()), this, SLOT(shortcutPlus())); - - keyShiftMinus = new QShortcut(this); - keyShiftMinus->setKey(Qt::SHIFT + Qt::Key_Minus); - connect(keyShiftMinus, SIGNAL(activated()), this, SLOT(shortcutShiftMinus())); - - keyShiftPlus = new QShortcut(this); - keyShiftPlus->setKey(Qt::SHIFT + Qt::Key_Plus); - connect(keyShiftPlus, SIGNAL(activated()), this, SLOT(shortcutShiftPlus())); - - keyControlMinus = new QShortcut(this); - keyControlMinus->setKey(Qt::CTRL + Qt::Key_Minus); - connect(keyControlMinus, SIGNAL(activated()), this, SLOT(shortcutControlMinus())); - - keyControlPlus = new QShortcut(this); - keyControlPlus->setKey(Qt::CTRL + Qt::Key_Plus); - connect(keyControlPlus, SIGNAL(activated()), this, SLOT(shortcutControlPlus())); - - keyQuit = new QShortcut(this); - keyQuit->setKey(Qt::CTRL + Qt::Key_Q); - connect(keyQuit, SIGNAL(activated()), this, SLOT(on_exitBtn_clicked())); - - keyPageUp = new QShortcut(this); - keyPageUp->setKey(Qt::Key_PageUp); - connect(keyPageUp, SIGNAL(activated()), this, SLOT(shortcutPageUp())); - - keyPageDown = new QShortcut(this); - keyPageDown->setKey(Qt::Key_PageDown); - connect(keyPageDown, SIGNAL(activated()), this, SLOT(shortcutPageDown())); - - keyF = new QShortcut(this); - keyF->setKey(Qt::Key_F); - connect(keyF, SIGNAL(activated()), this, SLOT(shortcutF())); - - keyM = new QShortcut(this); - keyM->setKey(Qt::Key_M); - connect(keyM, SIGNAL(activated()), this, SLOT(shortcutM())); - - ui->baudRateCombo->insertItem(0, QString("115200"), 115200); - ui->baudRateCombo->insertItem(1, QString("57600"), 57600); - ui->baudRateCombo->insertItem(2, QString("38400"), 38400); - ui->baudRateCombo->insertItem(3, QString("28800"), 28800); - ui->baudRateCombo->insertItem(4, QString("19200"), 19200); - ui->baudRateCombo->insertItem(5, QString("9600"), 9600); - ui->baudRateCombo->insertItem(6, QString("4800"), 4800); - ui->baudRateCombo->insertItem(7, QString("2400"), 2400); - ui->baudRateCombo->insertItem(8, QString("1200"), 1200); - ui->baudRateCombo->insertItem(9, QString("300"), 300); - - - // Enumerate audio devices, need to do before settings are loaded. - const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); - for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { - ui->audioOutputCombo->addItem(deviceInfo.deviceName(),QVariant::fromValue(deviceInfo)); - } - const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); - for (const QAudioDeviceInfo& deviceInfo : audioInputs) { - ui->audioInputCombo->addItem(deviceInfo.deviceName(),QVariant::fromValue(deviceInfo)); - } - - ui->serialDeviceListCombo->blockSignals(true); - ui->serialDeviceListCombo->addItem("Auto", 0); - int i = 0; - foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) - { - portList.append(serialPortInfo.portName()); -#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) - ui->serialDeviceListCombo->addItem(QString("/dev/")+serialPortInfo.portName(), i++); - ui->serialDeviceListCombo->addItem("Manual...", 256); -#else - ui->serialDeviceListCombo->addItem(serialPortInfo.portName(), i++); -#endif - } - ui->serialDeviceListCombo->blockSignals(false); - - ui->vspCombo->blockSignals(true); - -#ifdef Q_OS_WIN - ui->vspCombo->addItem(QString("None"), i++); - - foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) - { - ui->vspCombo->addItem(serialPortInfo.portName()); - } -#else - // Provide reasonable names for the symbolic link to the pty device -#ifdef Q_OS_MAC - QString vspName = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/rig-pty"; -#else - QString vspName=QDir::homePath()+"/rig-pty"; -#endif - for (i=1;i<8;i++) { - ui->vspCombo->addItem(vspName + QString::number(i)); - - if (QFile::exists(vspName+QString::number(i))) { - auto * model = qobject_cast(ui->vspCombo->model()); - auto *item = model->item(ui->vspCombo->count()-1); - item->setEnabled(false); - } - } - ui->vspCombo->addItem(vspName+QString::number(i)); - ui->vspCombo->addItem(QString("None"), i++); - -#endif - ui->vspCombo->setEditable(true); - ui->vspCombo->blockSignals(false); - - - setDefaultColors(); // set of UI colors with defaults populated - setDefPrefs(); // other default options - if (settingsFile.isNull()) { - settings = new QSettings(); - } - else - { - QString file = settingsFile; - QFile info(settingsFile); - QString path=""; - if (!QFileInfo(info).isAbsolute()) - { - path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - if (path.isEmpty()) - { - path = QDir::homePath(); - } - path = path + "/"; - file = info.fileName(); - } - - qDebug() << "Loading settings from:" << path + file; - settings = new QSettings(path + file, QSettings::Format::IniFormat); - } - loadSettings(); // Look for saved preferences - setTuningSteps(); // TODO: Combine into preferences - - ui->spectrumModeCombo->addItem("Center", (spectrumMode)spectModeCenter); - ui->spectrumModeCombo->addItem("Fixed", (spectrumMode)spectModeFixed); - ui->spectrumModeCombo->addItem("Scroll-C", (spectrumMode)spectModeScrollC); - ui->spectrumModeCombo->addItem("Scroll-F", (spectrumMode)spectModeScrollF); - - // Start server if enabled in config - if (serverConfig.enabled) { - serverConfig.lan = prefs.enableLAN; - if (!serverConfig.lan) { - serverConfig.resampleQuality = udpPrefs.resampleQuality; - serverConfig.audioInput = udpPrefs.audioInput; - serverConfig.audioOutput = udpPrefs.audioOutput; - serverConfig.baudRate = prefs.serialPortBaud; - serverConfig.inputDevice = udpPrefs.inputDevice; - serverConfig.outputDevice = udpPrefs.outputDevice; - } - udp = new udpServer(serverConfig); - - serverThread = new QThread(this); - - udp->moveToThread(serverThread); - - connect(this, SIGNAL(initServer()), udp, SLOT(init())); - connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); - - - serverThread->start(); - - emit initServer(); - - connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); - - } - - plot = ui->plot; // rename it waterfall. - wf = ui->waterfall; - - freqIndicatorLine = new QCPItemLine(plot); - freqIndicatorLine->setAntialiased(true); - freqIndicatorLine->setPen(QPen(Qt::blue)); - - - ui->modeSelectCombo->addItem("LSB", 0x00); - ui->modeSelectCombo->addItem("USB", 0x01); - ui->modeSelectCombo->addItem("FM", 0x05); - ui->modeSelectCombo->addItem("AM", 0x02); - ui->modeSelectCombo->addItem("CW", 0x03); - ui->modeSelectCombo->addItem("CW-R", 0x07); - ui->modeSelectCombo->addItem("RTTY", 0x04); - ui->modeSelectCombo->addItem("RTTY-R", 0x08); - - - ui->modeFilterCombo->addItem("1", 1); - ui->modeFilterCombo->addItem("2", 2); - ui->modeFilterCombo->addItem("3", 3); - ui->modeFilterCombo->addItem("Setup...", 99); - - ui->tuningStepCombo->blockSignals(true); - - ui->tuningStepCombo->addItem("1 Hz", (unsigned int) 1); - ui->tuningStepCombo->addItem("10 Hz", (unsigned int) 10); - ui->tuningStepCombo->addItem("100 Hz", (unsigned int) 100); - ui->tuningStepCombo->addItem("1 kHz", (unsigned int) 1000); - ui->tuningStepCombo->addItem("2.5 kHz", (unsigned int) 2500); - ui->tuningStepCombo->addItem("5 kHz", (unsigned int) 5000); - ui->tuningStepCombo->addItem("6.125 kHz", (unsigned int) 6125); // PMR - ui->tuningStepCombo->addItem("8.333 kHz", (unsigned int) 8333); // airband stepsize - ui->tuningStepCombo->addItem("9 kHz", (unsigned int) 9000); // European medium wave stepsize - ui->tuningStepCombo->addItem("10 kHz", (unsigned int) 10000); - ui->tuningStepCombo->addItem("12.5 kHz", (unsigned int) 12500); - ui->tuningStepCombo->addItem("100 kHz", (unsigned int) 100000); - ui->tuningStepCombo->addItem("250 kHz", (unsigned int) 250000); - ui->tuningStepCombo->addItem("1 MHz", (unsigned int) 1000000); //for 23 cm and HF - - - ui->tuningStepCombo->setCurrentIndex(2); - ui->tuningStepCombo->blockSignals(false); - - ui->wfthemeCombo->addItem("Jet", QCPColorGradient::gpJet); - ui->wfthemeCombo->addItem("Cold", QCPColorGradient::gpCold); - ui->wfthemeCombo->addItem("Hot", QCPColorGradient::gpHot); - ui->wfthemeCombo->addItem("Thermal", QCPColorGradient::gpThermal); - ui->wfthemeCombo->addItem("Night", QCPColorGradient::gpNight); - ui->wfthemeCombo->addItem("Ion", QCPColorGradient::gpIon); - ui->wfthemeCombo->addItem("Gray", QCPColorGradient::gpGrayscale); - ui->wfthemeCombo->addItem("Geography", QCPColorGradient::gpGeography); - ui->wfthemeCombo->addItem("Hues", QCPColorGradient::gpHues); - ui->wfthemeCombo->addItem("Polar", QCPColorGradient::gpPolar); - ui->wfthemeCombo->addItem("Spectrum", QCPColorGradient::gpSpectrum); - ui->wfthemeCombo->addItem("Candy", QCPColorGradient::gpCandy); - - spans << "2.5k" << "5.0k" << "10k" << "25k"; - spans << "50k" << "100k" << "250k" << "500k"; - ui->scopeBWCombo->insertItems(0, spans); - - edges << "1" << "2" << "3" << "4"; - ui->scopeEdgeCombo->insertItems(0, edges); - - ui->splitter->setHandleWidth(5); - - // Set scroll wheel response (tick interval) - // and set arrow key response (single step) - ui->rfGainSlider->setTickInterval(100); - ui->rfGainSlider->setSingleStep(10); - - ui->afGainSlider->setTickInterval(100); - ui->afGainSlider->setSingleStep(10); - - ui->sqlSlider->setTickInterval(100); - ui->sqlSlider->setSingleStep(10); - - ui->txPowerSlider->setTickInterval(100); - ui->txPowerSlider->setSingleStep(10); - - ui->micGainSlider->setTickInterval(100); - ui->micGainSlider->setSingleStep(10); - - ui->scopeRefLevelSlider->setTickInterval(50); - ui->scopeRefLevelSlider->setSingleStep(20); - - rigStatus = new QLabel(this); - ui->statusBar->addPermanentWidget(rigStatus); - ui->statusBar->showMessage("Connecting to rig...", 1000); - - pttLed = new QLedLabel(this); - ui->statusBar->addPermanentWidget(pttLed); - pttLed->setState(QLedLabel::State::StateOk); - - connectedLed = new QLedLabel(this); - ui->statusBar->addPermanentWidget(connectedLed); - - rigName = new QLabel(this); - ui->statusBar->addPermanentWidget(rigName); - rigName->setText("NONE"); - rigName->setFixedWidth(50); - - delayedCmdIntervalLAN_ms = 10; // interval for regular delayed commands, including initial rig/UI state queries - delayedCmdIntervalSerial_ms = 100; // interval for regular delayed commands, including initial rig/UI state queries - delayedCmdStartupInterval_ms = 250; // interval for rigID polling - delayedCommand = new QTimer(this); - delayedCommand->setInterval(delayedCmdStartupInterval_ms); // 250ms until we find rig civ and id, then 100ms. - delayedCommand->setSingleShot(true); - connect(delayedCommand, SIGNAL(timeout()), this, SLOT(runDelayedCommand())); - - periodicPollingTimer = new QTimer(this); - periodicPollingTimer->setInterval(10); - periodicPollingTimer->setSingleShot(false); - connect(periodicPollingTimer, SIGNAL(timeout()), this, SLOT(runPeriodicCommands())); - - freq.MHzDouble = 0.0; - freq.Hz = 0; - - openRig(); - qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); @@ -416,6 +41,151 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType(); qRegisterMetaType(); + haveRigCaps = false; + + setupKeyShortcuts(); + + setupMainUI(); + + setSerialDevicesUI(); + + setAudioDevicesUI(); + + setDefaultColors(); + setDefPrefs(); + + getSettingsFilePath(settingsFile); + + setupPlots(); + loadSettings(); // Look for saved preferences + setTuningSteps(); // TODO: Combine into preferences + + setUIToPrefs(); + + setServerToPrefs(); + + setInitialTiming(); + + openRig(); + + rigConnections(); + + if (serverConfig.enabled && udp != Q_NULLPTR) { + // Server + connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); + connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); + connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); + } + + amTransmitting = false; +} + +wfmain::~wfmain() +{ + rigThread->quit(); + rigThread->wait(); + if (serverThread != Q_NULLPTR) { + serverThread->quit(); + serverThread->wait(); + } + if (rigCtl != Q_NULLPTR) { + delete rigCtl; + } + delete rpt; + delete ui; + delete settings; + delete shuttleDev; +} + +void wfmain::closeEvent(QCloseEvent *event) +{ + // Are you sure? + QMessageBox::StandardButton resBtn = QMessageBox::question( this, QString("Confirm close"), + tr("Are you sure you wish to exit?\n"), + QMessageBox::No | QMessageBox::Yes, + QMessageBox::Yes); + if (resBtn == QMessageBox::Yes) { + QApplication::exit(); + } else { + event->ignore(); + } +} + +void wfmain::openRig() +{ + // This function is intended to handle opening a connection to the rig. + // the connection can be either serial or network, + // and this function is also responsible for initiating the search for a rig model and capabilities. + // Any errors, such as unable to open connection or unable to open port, are to be reported to the user. + + //TODO: if(hasRunPreviously) + + //TODO: if(useNetwork){... + + // } else { + + // if (prefs.fileWasNotFound) { + // showRigSettings(); // rig setting dialog box for network/serial, CIV, hostname, port, baud rate, serial device, etc + // TODO: How do we know if the setting was loaded? + + + // TODO: Use these if they are found + if(!serialPortCL.isEmpty()) + { + qDebug(logSystem()) << "Serial port specified by user: " << serialPortCL; + } else { + qDebug(logSystem()) << "Serial port not specified. "; + } + + if(!hostCL.isEmpty()) + { + qDebug(logSystem()) << "Remote host name specified by user: " << hostCL; + } + + // Start rigctld + if (prefs.enableRigCtlD && rigCtl == Q_NULLPTR) { + rigCtl = new rigCtlD(this); + + rigCtl->startServer(prefs.rigCtlPort); + connect(this, SIGNAL(sendRigCaps(rigCapabilities)), rigCtl, SLOT(receiveRigCaps(rigCapabilities))); + } + + makeRig(); + + if (prefs.enableLAN) + { + ui->lanEnableBtn->setChecked(true); + emit sendCommSetup(prefs.radioCIVAddr, udpPrefs,prefs.virtualSerialPort); + } else { + ui->serialEnableBtn->setChecked(true); + if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) + { + findSerialPort(); + + } else { + if(serialPortCL.isEmpty()) + { + serialPortRig = prefs.serialPortRadio; + } else { + serialPortRig = serialPortCL; + } + } + + // Here, the radioCIVAddr is being set from a default preference, which is for the 7300. + // However, we will not use it initially. OTOH, if it is set explicitedly to a value in the prefs, + // then we skip auto detection. + emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort); + } + + ui->statusBar->showMessage(QString("Connecting to rig using serial port ").append(serialPortRig), 1000); + + +} + +void wfmain::rigConnections() +{ + connect(this, SIGNAL(setCIVAddr(unsigned char)), rig, SLOT(setCIVAddr(unsigned char))); + connect(this, SIGNAL(sendPowerOn()), rig, SLOT(powerOn())); connect(this, SIGNAL(sendPowerOff()), rig, SLOT(powerOff())); @@ -558,179 +328,35 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s connect(cal, SIGNAL(setRefAdjustCourse(unsigned char)), rig, SLOT(setRefAdjustCourse(unsigned char))); connect(cal, SIGNAL(setRefAdjustFine(unsigned char)), rig, SLOT(setRefAdjustFine(unsigned char))); - - // Plot user interaction - connect(plot, SIGNAL(mouseDoubleClick(QMouseEvent*)), this, SLOT(handlePlotDoubleClick(QMouseEvent*))); - connect(wf, SIGNAL(mouseDoubleClick(QMouseEvent*)), this, SLOT(handleWFDoubleClick(QMouseEvent*))); - connect(plot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(handlePlotClick(QMouseEvent*))); - connect(wf, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(handleWFClick(QMouseEvent*))); - connect(wf, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(handleWFScroll(QWheelEvent*))); - connect(plot, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(handlePlotScroll(QWheelEvent*))); - - - if (serverConfig.enabled && udp != Q_NULLPTR) { - // Server - connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); - connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); - connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); - } - - ui->plot->addGraph(); // primary - ui->plot->addGraph(0, 0); // secondary, peaks, same axis as first? - ui->waterfall->addGraph(); - - - - colorMap = new QCPColorMap(wf->xAxis, wf->yAxis); - colorMapData = NULL; - -#if QCUSTOMPLOT_VERSION < 0x020001 - wf->addPlottable(colorMap); -#endif - - colorScale = new QCPColorScale(wf); - - ui->tabWidget->setCurrentIndex(0); - - QColor color(20+200/4.0*1,70*(1.6-1/4.0), 150, 150); - plot->graph(1)->setLineStyle(QCPGraph::lsLine); - plot->graph(1)->setPen(QPen(color.lighter(200))); - plot->graph(1)->setBrush(QBrush(color)); - - drawPeaks = false; - - SMeterReadings.fill(0,30); - powerMeterReadings.fill(0,30); - - - ui->freqMhzLineEdit->setValidator( new QDoubleValidator(0, 100, 6, this)); - ui->controlPortTxt->setValidator(new QIntValidator(this)); - - pttTimer = new QTimer(this); - pttTimer->setInterval(180*1000); // 3 minute max transmit time in ms - pttTimer->setSingleShot(true); - connect(pttTimer, SIGNAL(timeout()), this, SLOT(handlePttLimit())); - amTransmitting = false; - ui->tuneLockChk->setChecked(false); - freqLock = false; - - - freqIndicatorLine->start->setCoords(0.5,0); - freqIndicatorLine->end->setCoords(0.5,160); - - qDebug(logSystem()) << "Running with debugging options enabled."; -#ifdef QT_DEBUG - ui->debugBtn->setVisible(true); - ui->satOpsBtn->setVisible(true); -#else - ui->debugBtn->setVisible(false); - ui->satOpsBtn->setVisible(false); -#endif - - // Initial state of UI: - ui->fullScreenChk->setChecked(prefs.useFullScreen); - on_fullScreenChk_clicked(prefs.useFullScreen); - - ui->useDarkThemeChk->setChecked(prefs.useDarkMode); - on_useDarkThemeChk_clicked(prefs.useDarkMode); - - ui->useSystemThemeChk->setChecked(prefs.useSystemTheme); - on_useSystemThemeChk_clicked(prefs.useSystemTheme); - - ui->drawPeakChk->setChecked(prefs.drawPeaks); - on_drawPeakChk_clicked(prefs.drawPeaks); - drawPeaks = prefs.drawPeaks; - - //getInitialRigState(); - oldFreqDialVal = ui->freqDial->value(); } -wfmain::~wfmain() +//void wfmain::removeRigConnections() +//{ + +//} + +void wfmain::makeRig() { - rigThread->quit(); - rigThread->wait(); - if (serverThread != Q_NULLPTR) { - serverThread->quit(); - serverThread->wait(); - } - if (rigCtl != Q_NULLPTR) { - delete rigCtl; - } - delete rpt; - delete ui; - delete settings; - delete shuttleDev; -} - -void wfmain::closeEvent(QCloseEvent *event) -{ - // Are you sure? - QMessageBox::StandardButton resBtn = QMessageBox::question( this, QString("Confirm close"), - tr("Are you sure you wish to exit?\n"), - QMessageBox::No | QMessageBox::Yes, - QMessageBox::Yes); - if (resBtn == QMessageBox::Yes) { - QApplication::exit(); - } else { - event->ignore(); - } -} - -void wfmain::openRig() -{ - // This function is intended to handle opening a connection to the rig. - // the connection can be either serial or network, - // and this function is also responsible for initiating the search for a rig model and capabilities. - // Any errors, such as unable to open connection or unable to open port, are to be reported to the user. - - //TODO: if(hasRunPreviously) - - //TODO: if(useNetwork){... - - // } else { - - // if (prefs.fileWasNotFound) { - // showRigSettings(); // rig setting dialog box for network/serial, CIV, hostname, port, baud rate, serial device, etc - // TODO: How do we know if the setting was loaded? - - - // TODO: Use these if they are found - if(!serialPortCL.isEmpty()) - { - qDebug(logSystem()) << "Serial port specified by user: " << serialPortCL; - } else { - qDebug(logSystem()) << "Serial port not specified. "; - } - - if(!hostCL.isEmpty()) - { - qDebug(logSystem()) << "Remote host name specified by user: " << hostCL; - } - - // Start rigctld - if (prefs.enableRigCtlD) { - rigCtl = new rigCtlD(this); - - rigCtl->startServer(prefs.rigCtlPort); - connect(this, SIGNAL(sendRigCaps(rigCapabilities)), rigCtl, SLOT(receiveRigCaps(rigCapabilities))); - } - if (rigThread == Q_NULLPTR) { rig = new rigCommander(); rigThread = new QThread(this); + // Thread: rig->moveToThread(rigThread); - connect(rigThread, SIGNAL(started()), rig, SLOT(process())); connect(rigThread, SIGNAL(finished()), rig, SLOT(deleteLater())); rigThread->start(); + + // Rig status and Errors: connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString))); - + + // Rig comm setup: connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, QString))); connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString))); + connect(rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); connect(this, SIGNAL(sendCloseComm()), rig, SLOT(closeComm())); @@ -746,84 +372,78 @@ void wfmain::openRig() connect(rigCtl, SIGNAL(setPTT(bool)), rig, SLOT(setPTT(bool))); } } +} - if (prefs.enableLAN) +void wfmain::removeRig() +{ + if (rigThread != Q_NULLPTR) { - ui->lanEnableBtn->setChecked(true); - emit sendCommSetup(prefs.radioCIVAddr, udpPrefs,prefs.virtualSerialPort); - } else { - ui->serialEnableBtn->setChecked(true); - if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) - { - // Find the ICOM - // qInfo(logSystem()) << "Searching for serial port..."; - QDirIterator it73("/dev/serial/by-id", QStringList() << "*IC-7300*", QDir::Files, QDirIterator::Subdirectories); - QDirIterator it97("/dev/serial", QStringList() << "*IC-9700*A*", QDir::Files, QDirIterator::Subdirectories); - QDirIterator it785x("/dev/serial", QStringList() << "*IC-785*A*", QDir::Files, QDirIterator::Subdirectories); - QDirIterator it705("/dev/serial", QStringList() << "*IC-705*A", QDir::Files, QDirIterator::Subdirectories); - QDirIterator it7610("/dev/serial", QStringList() << "*IC-7610*A", QDir::Files, QDirIterator::Subdirectories); - QDirIterator itR8600("/dev/serial", QStringList() << "*IC-R8600*A", QDir::Files, QDirIterator::Subdirectories); - QDirIterator itTest("/tmp/test", QStringList() << "*radio*", QDir::NoFilter, QDirIterator::Subdirectories); - - qDebug() << "test iterator isEmpty: " << itTest.filePath().isEmpty(); - - if(!it73.filePath().isEmpty()) - { - // IC-7300 - serialPortRig = it73.filePath(); // first - } else if(!it97.filePath().isEmpty()) - { - // IC-9700 - serialPortRig = it97.filePath(); - } else if(!it785x.filePath().isEmpty()) - { - // IC-785x - serialPortRig = it785x.filePath(); - } else if(!it705.filePath().isEmpty()) - { - // IC-705 - serialPortRig = it705.filePath(); - } else if(!it7610.filePath().isEmpty()) - { - // IC-7610 - serialPortRig = it7610.filePath(); - } else if(!itR8600.filePath().isEmpty()) - { - // IC-R8600 - serialPortRig = itR8600.filePath(); - } else { - //fall back: - - - qInfo(logSystem()) << "Could not find Icom serial port. Falling back to OS default. Use --port to specify, or modify preferences."; -#ifdef Q_OS_MAC - serialPortRig = QString("/dev/tty.SLAB_USBtoUART"); -#endif -#ifdef Q_OS_LINUX - serialPortRig = QString("/dev/ttyUSB0"); -#endif -#ifdef Q_OS_WIN - serialPortRig = QString("COM1"); -#endif - } - - } else { - if(serialPortCL.isEmpty()) - { - serialPortRig = prefs.serialPortRadio; - } else { - serialPortRig = serialPortCL; - } + if (rigCtl != Q_NULLPTR) { + rigCtl->disconnect(); } + rigThread->disconnect(); + + rig->disconnect(); + + delete rigThread; + delete rig; - // Here, the radioCIVAddr is being set from a default preference, which is for the 7300. - // However, we will not use it initially. OTOH, if it is set explicitedly to a value in the prefs, - // then we skip auto detection. - emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort); } - ui->statusBar->showMessage(QString("Connecting to rig using serial port ").append(serialPortRig), 1000); +} + +void wfmain::findSerialPort() +{ + // Find the ICOM radio connected, or, if none, fall back to OS default. + // qInfo(logSystem()) << "Searching for serial port..."; + QDirIterator it73("/dev/serial/by-id", QStringList() << "*IC-7300*", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it97("/dev/serial", QStringList() << "*IC-9700*A*", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it785x("/dev/serial", QStringList() << "*IC-785*A*", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it705("/dev/serial", QStringList() << "*IC-705*A", QDir::Files, QDirIterator::Subdirectories); + QDirIterator it7610("/dev/serial", QStringList() << "*IC-7610*A", QDir::Files, QDirIterator::Subdirectories); + QDirIterator itR8600("/dev/serial", QStringList() << "*IC-R8600*A", QDir::Files, QDirIterator::Subdirectories); + QDirIterator itTest("/tmp/test", QStringList() << "*radio*", QDir::NoFilter, QDirIterator::Subdirectories); + + qDebug() << "test iterator isEmpty: " << itTest.filePath().isEmpty(); + + if(!it73.filePath().isEmpty()) + { + // IC-7300 + serialPortRig = it73.filePath(); // first + } else if(!it97.filePath().isEmpty()) + { + // IC-9700 + serialPortRig = it97.filePath(); + } else if(!it785x.filePath().isEmpty()) + { + // IC-785x + serialPortRig = it785x.filePath(); + } else if(!it705.filePath().isEmpty()) + { + // IC-705 + serialPortRig = it705.filePath(); + } else if(!it7610.filePath().isEmpty()) + { + // IC-7610 + serialPortRig = it7610.filePath(); + } else if(!itR8600.filePath().isEmpty()) + { + // IC-R8600 + serialPortRig = itR8600.filePath(); + } else { + //fall back: + qInfo(logSystem()) << "Could not find Icom serial port. Falling back to OS default. Use --port to specify, or modify preferences."; +#ifdef Q_OS_MAC + serialPortRig = QString("/dev/tty.SLAB_USBtoUART"); +#endif +#ifdef Q_OS_LINUX + serialPortRig = QString("/dev/ttyUSB0"); +#endif +#ifdef Q_OS_WIN + serialPortRig = QString("COM1"); +#endif + } } void wfmain::receiveCommReady() @@ -903,6 +523,505 @@ void wfmain::receiveStatusUpdate(QString text) this->rigStatus->setText(text); } +void wfmain::setupPlots() +{ + +// Line 290-- + spectrumDrawLock = true; + plot = ui->plot; // rename it waterfall. + wf = ui->waterfall; + + freqIndicatorLine = new QCPItemLine(plot); + freqIndicatorLine->setAntialiased(true); + freqIndicatorLine->setPen(QPen(Qt::blue)); +// + + ui->plot->addGraph(); // primary + ui->plot->addGraph(0, 0); // secondary, peaks, same axis as first? + ui->waterfall->addGraph(); + + colorMap = new QCPColorMap(wf->xAxis, wf->yAxis); + colorMapData = NULL; + +#if QCUSTOMPLOT_VERSION < 0x020001 + wf->addPlottable(colorMap); +#endif + + colorScale = new QCPColorScale(wf); + + ui->tabWidget->setCurrentIndex(0); + + QColor color(20+200/4.0*1,70*(1.6-1/4.0), 150, 150); + plot->graph(1)->setLineStyle(QCPGraph::lsLine); + plot->graph(1)->setPen(QPen(color.lighter(200))); + plot->graph(1)->setBrush(QBrush(color)); + + freqIndicatorLine->start->setCoords(0.5,0); + freqIndicatorLine->end->setCoords(0.5,160); + + // Plot user interaction + connect(plot, SIGNAL(mouseDoubleClick(QMouseEvent*)), this, SLOT(handlePlotDoubleClick(QMouseEvent*))); + connect(wf, SIGNAL(mouseDoubleClick(QMouseEvent*)), this, SLOT(handleWFDoubleClick(QMouseEvent*))); + connect(plot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(handlePlotClick(QMouseEvent*))); + connect(wf, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(handleWFClick(QMouseEvent*))); + connect(wf, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(handleWFScroll(QWheelEvent*))); + connect(plot, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(handlePlotScroll(QWheelEvent*))); + spectrumDrawLock = false; +} + +void wfmain::setupMainUI() +{ + ui->bandStkLastUsedBtn->setVisible(false); + ui->bandStkVoiceBtn->setVisible(false); + ui->bandStkDataBtn->setVisible(false); + ui->bandStkCWBtn->setVisible(false); + + ui->baudRateCombo->insertItem(0, QString("115200"), 115200); + ui->baudRateCombo->insertItem(1, QString("57600"), 57600); + ui->baudRateCombo->insertItem(2, QString("38400"), 38400); + ui->baudRateCombo->insertItem(3, QString("28800"), 28800); + ui->baudRateCombo->insertItem(4, QString("19200"), 19200); + ui->baudRateCombo->insertItem(5, QString("9600"), 9600); + ui->baudRateCombo->insertItem(6, QString("4800"), 4800); + ui->baudRateCombo->insertItem(7, QString("2400"), 2400); + ui->baudRateCombo->insertItem(8, QString("1200"), 1200); + ui->baudRateCombo->insertItem(9, QString("300"), 300); + + ui->spectrumModeCombo->addItem("Center", (spectrumMode)spectModeCenter); + ui->spectrumModeCombo->addItem("Fixed", (spectrumMode)spectModeFixed); + ui->spectrumModeCombo->addItem("Scroll-C", (spectrumMode)spectModeScrollC); + ui->spectrumModeCombo->addItem("Scroll-F", (spectrumMode)spectModeScrollF); + + ui->modeSelectCombo->addItem("LSB", 0x00); + ui->modeSelectCombo->addItem("USB", 0x01); + ui->modeSelectCombo->addItem("FM", 0x05); + ui->modeSelectCombo->addItem("AM", 0x02); + ui->modeSelectCombo->addItem("CW", 0x03); + ui->modeSelectCombo->addItem("CW-R", 0x07); + ui->modeSelectCombo->addItem("RTTY", 0x04); + ui->modeSelectCombo->addItem("RTTY-R", 0x08); + + + ui->modeFilterCombo->addItem("1", 1); + ui->modeFilterCombo->addItem("2", 2); + ui->modeFilterCombo->addItem("3", 3); + ui->modeFilterCombo->addItem("Setup...", 99); + + ui->tuningStepCombo->blockSignals(true); + + ui->tuningStepCombo->addItem("1 Hz", (unsigned int) 1); + ui->tuningStepCombo->addItem("10 Hz", (unsigned int) 10); + ui->tuningStepCombo->addItem("100 Hz", (unsigned int) 100); + ui->tuningStepCombo->addItem("1 kHz", (unsigned int) 1000); + ui->tuningStepCombo->addItem("2.5 kHz", (unsigned int) 2500); + ui->tuningStepCombo->addItem("5 kHz", (unsigned int) 5000); + ui->tuningStepCombo->addItem("6.125 kHz", (unsigned int) 6125); // PMR + ui->tuningStepCombo->addItem("8.333 kHz", (unsigned int) 8333); // airband stepsize + ui->tuningStepCombo->addItem("9 kHz", (unsigned int) 9000); // European medium wave stepsize + ui->tuningStepCombo->addItem("10 kHz", (unsigned int) 10000); + ui->tuningStepCombo->addItem("12.5 kHz", (unsigned int) 12500); + ui->tuningStepCombo->addItem("100 kHz", (unsigned int) 100000); + ui->tuningStepCombo->addItem("250 kHz", (unsigned int) 250000); + ui->tuningStepCombo->addItem("1 MHz", (unsigned int) 1000000); //for 23 cm and HF + + + ui->tuningStepCombo->setCurrentIndex(2); + ui->tuningStepCombo->blockSignals(false); + + ui->wfthemeCombo->addItem("Jet", QCPColorGradient::gpJet); + ui->wfthemeCombo->addItem("Cold", QCPColorGradient::gpCold); + ui->wfthemeCombo->addItem("Hot", QCPColorGradient::gpHot); + ui->wfthemeCombo->addItem("Thermal", QCPColorGradient::gpThermal); + ui->wfthemeCombo->addItem("Night", QCPColorGradient::gpNight); + ui->wfthemeCombo->addItem("Ion", QCPColorGradient::gpIon); + ui->wfthemeCombo->addItem("Gray", QCPColorGradient::gpGrayscale); + ui->wfthemeCombo->addItem("Geography", QCPColorGradient::gpGeography); + ui->wfthemeCombo->addItem("Hues", QCPColorGradient::gpHues); + ui->wfthemeCombo->addItem("Polar", QCPColorGradient::gpPolar); + ui->wfthemeCombo->addItem("Spectrum", QCPColorGradient::gpSpectrum); + ui->wfthemeCombo->addItem("Candy", QCPColorGradient::gpCandy); + + spans << "2.5k" << "5.0k" << "10k" << "25k"; + spans << "50k" << "100k" << "250k" << "500k"; + ui->scopeBWCombo->insertItems(0, spans); + + edges << "1" << "2" << "3" << "4"; + ui->scopeEdgeCombo->insertItems(0, edges); + + ui->splitter->setHandleWidth(5); + + // Set scroll wheel response (tick interval) + // and set arrow key response (single step) + ui->rfGainSlider->setTickInterval(100); + ui->rfGainSlider->setSingleStep(10); + + ui->afGainSlider->setTickInterval(100); + ui->afGainSlider->setSingleStep(10); + + ui->sqlSlider->setTickInterval(100); + ui->sqlSlider->setSingleStep(10); + + ui->txPowerSlider->setTickInterval(100); + ui->txPowerSlider->setSingleStep(10); + + ui->micGainSlider->setTickInterval(100); + ui->micGainSlider->setSingleStep(10); + + ui->scopeRefLevelSlider->setTickInterval(50); + ui->scopeRefLevelSlider->setSingleStep(20); + + ui->freqMhzLineEdit->setValidator( new QDoubleValidator(0, 100, 6, this)); + ui->controlPortTxt->setValidator(new QIntValidator(this)); + + qDebug(logSystem()) << "Running with debugging options enabled."; +#ifdef QT_DEBUG + ui->debugBtn->setVisible(true); + ui->satOpsBtn->setVisible(true); +#else + ui->debugBtn->setVisible(false); + ui->satOpsBtn->setVisible(false); +#endif + + rigStatus = new QLabel(this); + ui->statusBar->addPermanentWidget(rigStatus); + ui->statusBar->showMessage("Connecting to rig...", 1000); + + pttLed = new QLedLabel(this); + ui->statusBar->addPermanentWidget(pttLed); + pttLed->setState(QLedLabel::State::StateOk); + + connectedLed = new QLedLabel(this); + ui->statusBar->addPermanentWidget(connectedLed); + + rigName = new QLabel(this); + ui->statusBar->addPermanentWidget(rigName); + rigName->setText("NONE"); + rigName->setFixedWidth(50); + + SMeterReadings.fill(0,30); + powerMeterReadings.fill(0,30); + + freq.MHzDouble = 0.0; + freq.Hz = 0; + oldFreqDialVal = ui->freqDial->value(); + + ui->tuneLockChk->setChecked(false); + freqLock = false; +} + +void wfmain::getSettingsFilePath(QString settingsFile) +{ + if (settingsFile.isNull()) { + settings = new QSettings(); + } + else + { + QString file = settingsFile; + QFile info(settingsFile); + QString path=""; + if (!QFileInfo(info).isAbsolute()) + { + path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + if (path.isEmpty()) + { + path = QDir::homePath(); + } + path = path + "/"; + file = info.fileName(); + } + + qInfo(logSystem()) << "Loading settings from:" << path + file; + settings = new QSettings(path + file, QSettings::Format::IniFormat); + } +} + + +void wfmain::setInitialTiming() +{ + delayedCmdIntervalLAN_ms = 10; // interval for regular delayed commands, including initial rig/UI state queries + delayedCmdIntervalSerial_ms = 100; // interval for regular delayed commands, including initial rig/UI state queries + delayedCmdStartupInterval_ms = 250; // interval for rigID polling + delayedCommand = new QTimer(this); + delayedCommand->setInterval(delayedCmdStartupInterval_ms); // 250ms until we find rig civ and id, then 100ms. + delayedCommand->setSingleShot(true); + connect(delayedCommand, SIGNAL(timeout()), this, SLOT(runDelayedCommand())); + + periodicPollingTimer = new QTimer(this); + periodicPollingTimer->setInterval(10); + periodicPollingTimer->setSingleShot(false); + connect(periodicPollingTimer, SIGNAL(timeout()), this, SLOT(runPeriodicCommands())); + + pttTimer = new QTimer(this); + pttTimer->setInterval(180*1000); // 3 minute max transmit time in ms + pttTimer->setSingleShot(true); + connect(pttTimer, SIGNAL(timeout()), this, SLOT(handlePttLimit())); +} + +void wfmain::setServerToPrefs() +{ + + // Start server if enabled in config + if (serverConfig.enabled) { + serverConfig.lan = prefs.enableLAN; + if (!serverConfig.lan) { + serverConfig.resampleQuality = udpPrefs.resampleQuality; + serverConfig.audioInput = udpPrefs.audioInput; + serverConfig.audioOutput = udpPrefs.audioOutput; + serverConfig.baudRate = prefs.serialPortBaud; + serverConfig.audioInput = udpPrefs.audioInput; + serverConfig.audioOutput = udpPrefs.audioOutput; + } + + udp = new udpServer(serverConfig); + + serverThread = new QThread(this); + + udp->moveToThread(serverThread); + + connect(this, SIGNAL(initServer()), udp, SLOT(init())); + connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); + + + serverThread->start(); + + emit initServer(); + + connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); + + } +} + +void wfmain::setUIToPrefs() +{ + ui->fullScreenChk->setChecked(prefs.useFullScreen); + on_fullScreenChk_clicked(prefs.useFullScreen); + + ui->useDarkThemeChk->setChecked(prefs.useDarkMode); + on_useDarkThemeChk_clicked(prefs.useDarkMode); + + ui->useSystemThemeChk->setChecked(prefs.useSystemTheme); + on_useSystemThemeChk_clicked(prefs.useSystemTheme); + + ui->drawPeakChk->setChecked(prefs.drawPeaks); + on_drawPeakChk_clicked(prefs.drawPeaks); + drawPeaks = prefs.drawPeaks; +} + +void wfmain::setAudioDevicesUI() +{ + // Enumerate audio devices, need to do before settings are loaded. + std::map apiMap; + apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; + apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; + apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound"; + apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI"; + apiMap[RtAudio::UNIX_JACK] = "Jack Client"; + apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; + apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; + apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; + apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; + + std::vector< RtAudio::Api > apis; + RtAudio::getCompiledApi(apis); + + qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion()); + + qInfo(logAudio()) << "Compiled APIs:"; + for (unsigned int i = 0; i < apis.size(); i++) { + qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]); + } + + RtAudio::DeviceInfo info; + qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio.getCurrentApi()]); + + unsigned int devices = audio.getDeviceCount(); + qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; + + for (unsigned int i = 1; i < devices; i++) { + info = audio.getDeviceInfo(i); + if (info.outputChannels > 0) { + qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); + ui->audioOutputCombo->addItem(QString::fromStdString(info.name), i); + } + if (info.inputChannels > 0) { + qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name); + ui->audioInputCombo->addItem(QString::fromStdString(info.name), i); + } + } +} + +void wfmain::setSerialDevicesUI() +{ + ui->serialDeviceListCombo->blockSignals(true); + ui->serialDeviceListCombo->addItem("Auto", 0); + int i = 0; + foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) + { + portList.append(serialPortInfo.portName()); +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) + ui->serialDeviceListCombo->addItem(QString("/dev/")+serialPortInfo.portName(), i++); +#else + ui->serialDeviceListCombo->addItem(serialPortInfo.portName(), i++); +#endif + } +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) + ui->serialDeviceListCombo->addItem("Manual...", 256); +#endif + ui->serialDeviceListCombo->blockSignals(false); + + ui->vspCombo->blockSignals(true); + +#ifdef Q_OS_WIN + ui->vspCombo->addItem(QString("None"), i++); + + foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) + { + ui->vspCombo->addItem(serialPortInfo.portName()); + } +#else + // Provide reasonable names for the symbolic link to the pty device +#ifdef Q_OS_MAC + QString vspName = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/rig-pty"; +#else + QString vspName=QDir::homePath()+"/rig-pty"; +#endif + for (i=1;i<8;i++) { + ui->vspCombo->addItem(vspName + QString::number(i)); + + if (QFile::exists(vspName+QString::number(i))) { + auto * model = qobject_cast(ui->vspCombo->model()); + auto *item = model->item(ui->vspCombo->count()-1); + item->setEnabled(false); + } + } + ui->vspCombo->addItem(vspName+QString::number(i)); + ui->vspCombo->addItem(QString("None"), i++); + +#endif + ui->vspCombo->setEditable(true); + ui->vspCombo->blockSignals(false); +} + + +void wfmain::setupKeyShortcuts() +{ + keyF1 = new QShortcut(this); + keyF1->setKey(Qt::Key_F1); + connect(keyF1, SIGNAL(activated()), this, SLOT(shortcutF1())); + + keyF2 = new QShortcut(this); + keyF2->setKey(Qt::Key_F2); + connect(keyF2, SIGNAL(activated()), this, SLOT(shortcutF2())); + + keyF3 = new QShortcut(this); + keyF3->setKey(Qt::Key_F3); + connect(keyF3, SIGNAL(activated()), this, SLOT(shortcutF3())); + + keyF4 = new QShortcut(this); + keyF4->setKey(Qt::Key_F4); + connect(keyF4, SIGNAL(activated()), this, SLOT(shortcutF4())); + + keyF5 = new QShortcut(this); + keyF5->setKey(Qt::Key_F5); + connect(keyF5, SIGNAL(activated()), this, SLOT(shortcutF5())); + + keyF6 = new QShortcut(this); + keyF6->setKey(Qt::Key_F6); + connect(keyF6, SIGNAL(activated()), this, SLOT(shortcutF6())); + + keyF7 = new QShortcut(this); + keyF7->setKey(Qt::Key_F7); + connect(keyF7, SIGNAL(activated()), this, SLOT(shortcutF7())); + + keyF8 = new QShortcut(this); + keyF8->setKey(Qt::Key_F8); + connect(keyF8, SIGNAL(activated()), this, SLOT(shortcutF8())); + + keyF9 = new QShortcut(this); + keyF9->setKey(Qt::Key_F9); + connect(keyF9, SIGNAL(activated()), this, SLOT(shortcutF9())); + + keyF10 = new QShortcut(this); + keyF10->setKey(Qt::Key_F10); + connect(keyF10, SIGNAL(activated()), this, SLOT(shortcutF10())); + + keyF11 = new QShortcut(this); + keyF11->setKey(Qt::Key_F11); + connect(keyF11, SIGNAL(activated()), this, SLOT(shortcutF11())); + + keyF12 = new QShortcut(this); + keyF12->setKey(Qt::Key_F12); + connect(keyF12, SIGNAL(activated()), this, SLOT(shortcutF12())); + + keyControlT = new QShortcut(this); + keyControlT->setKey(Qt::CTRL + Qt::Key_T); + connect(keyControlT, SIGNAL(activated()), this, SLOT(shortcutControlT())); + + keyControlR = new QShortcut(this); + keyControlR->setKey(Qt::CTRL + Qt::Key_R); + connect(keyControlR, SIGNAL(activated()), this, SLOT(shortcutControlR())); + + keyControlI = new QShortcut(this); + keyControlI->setKey(Qt::CTRL + Qt::Key_I); + connect(keyControlI, SIGNAL(activated()), this, SLOT(shortcutControlI())); + + keyControlU = new QShortcut(this); + keyControlU->setKey(Qt::CTRL + Qt::Key_U); + connect(keyControlU, SIGNAL(activated()), this, SLOT(shortcutControlU())); + + keyStar = new QShortcut(this); + keyStar->setKey(Qt::Key_Asterisk); + connect(keyStar, SIGNAL(activated()), this, SLOT(shortcutStar())); + + keySlash = new QShortcut(this); + keySlash->setKey(Qt::Key_Slash); + connect(keySlash, SIGNAL(activated()), this, SLOT(shortcutSlash())); + + keyMinus = new QShortcut(this); + keyMinus->setKey(Qt::Key_Minus); + connect(keyMinus, SIGNAL(activated()), this, SLOT(shortcutMinus())); + + keyPlus = new QShortcut(this); + keyPlus->setKey(Qt::Key_Plus); + connect(keyPlus, SIGNAL(activated()), this, SLOT(shortcutPlus())); + + keyShiftMinus = new QShortcut(this); + keyShiftMinus->setKey(Qt::SHIFT + Qt::Key_Minus); + connect(keyShiftMinus, SIGNAL(activated()), this, SLOT(shortcutShiftMinus())); + + keyShiftPlus = new QShortcut(this); + keyShiftPlus->setKey(Qt::SHIFT + Qt::Key_Plus); + connect(keyShiftPlus, SIGNAL(activated()), this, SLOT(shortcutShiftPlus())); + + keyControlMinus = new QShortcut(this); + keyControlMinus->setKey(Qt::CTRL + Qt::Key_Minus); + connect(keyControlMinus, SIGNAL(activated()), this, SLOT(shortcutControlMinus())); + + keyControlPlus = new QShortcut(this); + keyControlPlus->setKey(Qt::CTRL + Qt::Key_Plus); + connect(keyControlPlus, SIGNAL(activated()), this, SLOT(shortcutControlPlus())); + + keyQuit = new QShortcut(this); + keyQuit->setKey(Qt::CTRL + Qt::Key_Q); + connect(keyQuit, SIGNAL(activated()), this, SLOT(on_exitBtn_clicked())); + + keyPageUp = new QShortcut(this); + keyPageUp->setKey(Qt::Key_PageUp); + connect(keyPageUp, SIGNAL(activated()), this, SLOT(shortcutPageUp())); + + keyPageDown = new QShortcut(this); + keyPageDown->setKey(Qt::Key_PageDown); + connect(keyPageDown, SIGNAL(activated()), this, SLOT(shortcutPageDown())); + + keyF = new QShortcut(this); + keyF->setKey(Qt::Key_F); + connect(keyF, SIGNAL(activated()), this, SLOT(shortcutF())); + + keyM = new QShortcut(this); + keyM->setKey(Qt::Key_M); + connect(keyM, SIGNAL(activated()), this, SLOT(shortcutM())); +} + void wfmain::setDefPrefs() { defPrefs.useFullScreen = false; @@ -925,8 +1044,8 @@ void wfmain::setDefPrefs() udpDefPrefs.audioLANPort = 50003; udpDefPrefs.username = QString(""); udpDefPrefs.password = QString(""); - udpDefPrefs.audioOutput = QAudioDeviceInfo::defaultOutputDevice().deviceName(); - udpDefPrefs.audioInput = QAudioDeviceInfo::defaultInputDevice().deviceName(); + udpDefPrefs.audioOutput = 0; + udpDefPrefs.audioInput = 0; udpDefPrefs.audioRXLatency = 150; udpDefPrefs.audioTXLatency = 150; udpDefPrefs.audioRXSampleRate = 48000; @@ -1052,7 +1171,7 @@ void wfmain::loadSettings() } ui->lanEnableBtn->setChecked(prefs.enableLAN); - ui->connectBtn->setEnabled(prefs.enableLAN); + ui->connectBtn->setEnabled(true); prefs.enableRigCtlD = settings->value("EnableRigCtlD", defPrefs.enableRigCtlD).toBool(); prefs.rigCtlPort = settings->value("RigCtlPort", defPrefs.rigCtlPort).toInt(); @@ -1087,7 +1206,7 @@ void wfmain::loadSettings() udpPrefs.audioRXSampleRate = settings->value("AudioRXSampleRate", udpDefPrefs.audioRXSampleRate).toInt(); udpPrefs.audioTXSampleRate = settings->value("AudioTXSampleRate",udpDefPrefs.audioTXSampleRate).toInt(); ui->audioSampleRateCombo->setEnabled(ui->lanEnableBtn->isChecked()); - int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(udpDefPrefs.audioRXSampleRate)); + int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(udpPrefs.audioRXSampleRate)); if (audioSampleRateIndex != -1) { ui->audioSampleRateCombo->setCurrentIndex(audioSampleRateIndex); } @@ -1116,24 +1235,20 @@ void wfmain::loadSettings() if (ui->audioTXCodecCombo->itemData(f).toInt() == udpPrefs.audioTXCodec) ui->audioTXCodecCombo->setCurrentIndex(f); - udpPrefs.audioOutput = settings->value("AudioOutput", udpDefPrefs.audioOutput).toString(); - qInfo(logGui()) << "Got Audio Output: " << udpPrefs.audioOutput; - //ui->audioOutputCombo->setEnabled(ui->lanEnableBtn->isChecked()); - int audioOutputIndex = ui->audioOutputCombo->findText(udpPrefs.audioOutput); + udpPrefs.audioOutputName = settings->value("AudioOutput", udpDefPrefs.audioOutputName).toString(); + qInfo(logGui()) << "Got Audio Output: " << udpPrefs.audioOutputName; + int audioOutputIndex = ui->audioOutputCombo->findText(udpPrefs.audioOutputName); if (audioOutputIndex != -1) { ui->audioOutputCombo->setCurrentIndex(audioOutputIndex); - QVariant v = ui->audioOutputCombo->currentData(); - udpPrefs.outputDevice = v.value(); + udpPrefs.audioOutput = ui->audioOutputCombo->itemData(audioOutputIndex).toInt(); } - udpPrefs.audioInput = settings->value("AudioInput", udpDefPrefs.audioInput).toString(); - qInfo(logGui()) << "Got Audio Input: " << udpPrefs.audioInput; - //ui->audioInputCombo->setEnabled(ui->lanEnableBtn->isChecked()); - int audioInputIndex = ui->audioInputCombo->findText(udpPrefs.audioInput); + udpPrefs.audioInputName = settings->value("AudioInput", udpDefPrefs.audioInputName).toString(); + qInfo(logGui()) << "Got Audio Input: " << udpPrefs.audioInputName; + int audioInputIndex = ui->audioInputCombo->findText(udpPrefs.audioInputName); if (audioInputIndex != -1) { ui->audioInputCombo->setCurrentIndex(audioInputIndex); - QVariant v = ui->audioInputCombo->currentData(); - udpPrefs.inputDevice = v.value(); + udpPrefs.audioInput = ui->audioInputCombo->itemData(audioInputIndex).toInt(); } udpPrefs.resampleQuality = settings->value("ResampleQuality", udpDefPrefs.resampleQuality).toInt(); @@ -1245,10 +1360,10 @@ void wfmain::saveSettings() settings->setValue("AudioTXLatency", udpPrefs.audioTXLatency); settings->setValue("AudioRXSampleRate", udpPrefs.audioRXSampleRate); settings->setValue("AudioRXCodec", udpPrefs.audioRXCodec); - settings->setValue("AudioTXSampleRate", udpPrefs.audioRXSampleRate); + settings->setValue("AudioTXSampleRate", udpPrefs.audioTXSampleRate); settings->setValue("AudioTXCodec", udpPrefs.audioTXCodec); - settings->setValue("AudioOutput", udpPrefs.audioOutput); - settings->setValue("AudioInput", udpPrefs.audioInput); + settings->setValue("AudioOutput", udpPrefs.audioOutputName); + settings->setValue("AudioInput", udpPrefs.audioInputName); settings->setValue("ResampleQuality", udpPrefs.resampleQuality); settings->setValue("ClientName", udpPrefs.clientName); settings->endGroup(); @@ -1338,35 +1453,63 @@ void wfmain::saveSettings() settings->sync(); // Automatic, not needed (supposedly) } + void wfmain::prepareWf() +{ + prepareWf(160); +} + +void wfmain::prepareWf(unsigned int wfLength) { // All this code gets moved in from the constructor of wfmain. if(haveRigCaps) { - // do things - spectWidth = rigCaps.spectLenMax; // was fixed at 475 - wfLength = 160; // fixed for now, time-length of waterfall + // TODO: Lock the function that draws on the spectrum while we are updating. + spectrumDrawLock = true; + + spectWidth = rigCaps.spectLenMax; + this->wfLength = wfLength; // fixed for now, time-length of waterfall // Initialize before use! QByteArray empty((int)spectWidth, '\x01'); spectrumPeaks = QByteArray( (int)spectWidth, '\x01' ); - for(quint16 i=0; iclearData(); + colorMap->data()->clear(); colorMap->data()->setValueRange(QCPRange(0, wfLength-1)); colorMap->data()->setKeyRange(QCPRange(0, spectWidth-1)); colorMap->setDataRange(QCPRange(0, rigCaps.spectAmpMax)); - colorMap->setGradient(QCPColorGradient::gpJet); // TODO: Add preference - colorMapData = new QCPColorMapData(spectWidth, wfLength, QCPRange(0, spectWidth-1), QCPRange(0, wfLength-1)); + colorMap->setGradient(static_cast(ui->wfthemeCombo->currentData().toInt())); + + if(colorMapData == Q_NULLPTR) + { + colorMapData = new QCPColorMapData(spectWidth, wfLength, QCPRange(0, spectWidth-1), QCPRange(0, wfLength-1)); + } else { + //delete colorMapData; // TODO: Figure out why it crashes if we delete first. + colorMapData = new QCPColorMapData(spectWidth, wfLength, QCPRange(0, spectWidth-1), QCPRange(0, wfLength-1)); + } colorMap->setData(colorMapData); - spectRowCurrent = 0; + wf->yAxis->setRangeReversed(true); wf->xAxis->setVisible(false); rigName->setText(rigCaps.modelName); + + spectrumDrawLock = false; } else { qInfo(logSystem()) << "Cannot prepare WF view without rigCaps. Waiting on this."; return; @@ -2487,42 +2630,44 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e } - //ui->qcp->addGraph(); - plot->graph(0)->setData(x,y); - if((freq.MHzDouble < endFreq) && (freq.MHzDouble > startFreq)) + if(!spectrumDrawLock) { - freqIndicatorLine->start->setCoords(freq.MHzDouble,0); - freqIndicatorLine->end->setCoords(freq.MHzDouble,160); - } - if(drawPeaks) - { - plot->graph(1)->setData(x,y2); // peaks - } - plot->yAxis->setRange(0, rigCaps.spectAmpMax+1); - plot->xAxis->setRange(startFreq, endFreq); - plot->replot(); - - if(specLen == spectWidth) - { - wfimage.prepend(spectrum); - if(wfimage.length() > wfLength) + //ui->qcp->addGraph(); + plot->graph(0)->setData(x,y); + if((freq.MHzDouble < endFreq) && (freq.MHzDouble > startFreq)) { - wfimage.remove(wfLength); + freqIndicatorLine->start->setCoords(freq.MHzDouble,0); + freqIndicatorLine->end->setCoords(freq.MHzDouble,160); } - - // Waterfall: - for(int row = 0; row < wfLength; row++) + if(drawPeaks) { - for(int col = 0; col < spectWidth; col++) + plot->graph(1)->setData(x,y2); // peaks + } + plot->yAxis->setRange(0, rigCaps.spectAmpMax+1); + plot->xAxis->setRange(startFreq, endFreq); + plot->replot(); + + if(specLen == spectWidth) + { + wfimage.prepend(spectrum); + if(wfimage.length() > wfLength) { - colorMap->data()->setCell( col, row, wfimage.at(row).at(col)); + wfimage.remove(wfLength); } - } - wf->yAxis->setRange(0,wfLength - 1); - wf->xAxis->setRange(0, spectWidth-1); - wf->replot(); - spectRowCurrent = (spectRowCurrent + 1) % wfLength; + // Waterfall: + for(int row = 0; row < wfLength; row++) + { + for(int col = 0; col < spectWidth; col++) + { + colorMap->data()->setCell( col, row, wfimage.at(row).at(col)); + } + } + + wf->yAxis->setRange(0,wfLength - 1); + wf->xAxis->setRange(0, spectWidth-1); + wf->replot(); + } } } @@ -3468,7 +3613,7 @@ void wfmain::on_serialEnableBtn_clicked(bool checked) prefs.enableLAN = !checked; ui->serialDeviceListCombo->setEnabled(checked); - ui->connectBtn->setEnabled(!checked); + ui->connectBtn->setEnabled(true); ui->ipAddressTxt->setEnabled(!checked); ui->controlPortTxt->setEnabled(!checked); ui->usernameTxt->setEnabled(!checked); @@ -3488,7 +3633,7 @@ void wfmain::on_serialEnableBtn_clicked(bool checked) void wfmain::on_lanEnableBtn_clicked(bool checked) { prefs.enableLAN = checked; - ui->connectBtn->setEnabled(checked); + ui->connectBtn->setEnabled(true); ui->ipAddressTxt->setEnabled(checked); ui->controlPortTxt->setEnabled(checked); ui->usernameTxt->setEnabled(checked); @@ -3522,18 +3667,16 @@ void wfmain::on_passwordTxt_textChanged(QString text) udpPrefs.password = text; } -void wfmain::on_audioOutputCombo_currentIndexChanged(QString text) +void wfmain::on_audioOutputCombo_currentIndexChanged(int value) { - udpPrefs.audioOutput = text; - QVariant v = ui->audioOutputCombo->currentData(); - udpPrefs.outputDevice = v.value(); + udpPrefs.audioOutput = ui->audioOutputCombo->itemData(value).toInt(); + udpPrefs.audioOutputName = ui->audioOutputCombo->itemText(value); } -void wfmain::on_audioInputCombo_currentIndexChanged(QString text) +void wfmain::on_audioInputCombo_currentIndexChanged(int value) { - udpPrefs.audioInput = text; - QVariant v = ui->audioInputCombo->currentData(); - udpPrefs.inputDevice = v.value(); + udpPrefs.audioInput = ui->audioInputCombo->itemData(value).toInt(); + udpPrefs.audioInputName = ui->audioInputCombo->itemText(value); } void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text) @@ -4344,6 +4487,7 @@ void wfmain::on_rigCIVaddrHexLine_editingFinished() if(okconvert && (propCIVAddr < 0xe0) && (propCIVAddr != 0)) { prefs.radioCIVAddr = propCIVAddr; + emit setCIVAddr(propCIVAddr); showStatusBarText(QString("Setting radio CI-V address to: 0x%1. Press Save Settings to retain.").arg(propCIVAddr, 2, 16)); } else { showStatusBarText(QString("Could not use provided CI-V address. Address must be < 0xE0")); @@ -4362,10 +4506,15 @@ void wfmain::on_baudRateCombo_activated(int index) (void)index; } +void wfmain::on_wfLengthSlider_valueChanged(int value) +{ + prepareWf(value); +} + // --- DEBUG FUNCTION --- void wfmain::on_debugBtn_clicked() { qInfo(logSystem()) << "Debug button pressed."; - qInfo(logSystem()) << "getting mode."; - emit getRigID(); + prepareWf(160); + } diff --git a/wfmain.h b/wfmain.h index 4d524c3d..990aaf78 100644 --- a/wfmain.h +++ b/wfmain.h @@ -45,6 +45,9 @@ public: ~wfmain(); signals: + // Basic to rig: + void setCIVAddr(unsigned char newRigCIVAddr); + // Power void sendPowerOn(); void sendPowerOff(); @@ -346,9 +349,9 @@ private slots: void on_passwordTxt_textChanged(QString text); - void on_audioOutputCombo_currentIndexChanged(QString text); + void on_audioOutputCombo_currentIndexChanged(int value); - void on_audioInputCombo_currentIndexChanged(QString text); + void on_audioInputCombo_currentIndexChanged(int value); void on_toFixedBtn_clicked(); @@ -444,6 +447,8 @@ private slots: void on_baudRateCombo_activated(int); + void on_wfLengthSlider_valueChanged(int value); + private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); @@ -457,6 +462,7 @@ private: void setAppTheme(bool isCustom); void setPlotTheme(QCustomPlot *plot, bool isDark); void prepareWf(); + void prepareWf(unsigned int wfLength); void getInitialRigState(); void setBandButtons(); void showButton(QPushButton *btn); @@ -514,6 +520,20 @@ private: QTimer * periodicPollingTimer; QTimer * pttTimer; + void setupPlots(); + void makeRig(); + void rigConnections(); + void removeRig(); + void findSerialPort(); + + void setupKeyShortcuts(); + void setupMainUI(); + void setUIToPrefs(); + void setSerialDevicesUI(); + void setAudioDevicesUI(); + void setServerToPrefs(); + void setInitialTiming(); + void getSettingsFilePath(QString settingsFile); QStringList modes; int currentModeIndex; @@ -527,8 +547,7 @@ private: quint16 spectWidth; quint16 wfLength; - - quint16 spectRowCurrent; + bool spectrumDrawLock; QByteArray spectrumPeaks; @@ -708,6 +727,7 @@ private: SERVERCONFIG serverConfig; shuttle *shuttleDev; + RtAudio audio; }; Q_DECLARE_METATYPE(struct rigCapabilities) diff --git a/wfmain.ui b/wfmain.ui index 33cf6f9a..d2a6adea 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -18,7 +18,7 @@ - 3 + 0 @@ -767,6 +767,60 @@ + + + + 0 + + + + + + 0 + 70 + + + + + 16777215 + 80 + + + + Waterfall Length + + + Waterfall Length + + + 100 + + + 1024 + + + 160 + + + Qt::Vertical + + + + + + + + 16777215 + 15 + + + + Len + + + + + diff --git a/wfview.pro b/wfview.pro index 1236d899..aef3f0f4 100644 --- a/wfview.pro +++ b/wfview.pro @@ -18,10 +18,10 @@ CONFIG(debug, release|debug) { } else { # For Release builds only: -QMAKE_CXXFLAGS += -s +linux:QMAKE_CXXFLAGS += -s QMAKE_CXXFLAGS += -fvisibility=hidden QMAKE_CXXFLAGS += -fvisibility-inlines-hidden -QMAKE_LFLAGS += -O2 -s +linux:QMAKE_LFLAGS += -O2 -s } # The following define makes your compiler emit warnings if you use @@ -35,6 +35,23 @@ DEFINES += QCUSTOMPLOT_COMPILE_LIBRARY DEFINES += OUTSIDE_SPEEX DEFINES += RANDOM_PREFIX=wf +# RTAudio defines +win32:DEFINES += __WINDOWS_WASAPI__ +#win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries +linux:DEFINES += __LINUX_ALSA__ +#linux:DEFINES += __LINUX_OSS__ +#linux:DEFINES += __LINUX_PULSE__ +macx:DEFINES += __MACOSX_CORE__ + +#option(RTAUDIO_API_DS "Build DirectSound API" OFF) +#option(RTAUDIO_API_ASIO "Build ASIO API" OFF) +#option(RTAUDIO_API_WASAPI "Build WASAPI API" ${WIN32}) +#option(RTAUDIO_API_OSS "Build OSS4 API" ${xBSD}) +#option(RTAUDIO_API_ALSA "Build ALSA API" ${LINUX}) +#option(RTAUDIO_API_PULSE "Build PulseAudio API" ${pulse_FOUND}) +#option(RTAUDIO_API_JACK "Build JACK audio server API" ${HAVE_JACK}) +#option(RTAUDIO_API_CORE "Build CoreAudio API" ${APPLE}) + macx:INCLUDEPATH += /usr/local/include /opt/local/include macx:LIBS += -L/usr/local/lib -L/opt/local/lib @@ -81,20 +98,24 @@ CONFIG(debug, release|debug) { linux: QCPLIB = qcustomplot } -linux:LIBS += -L./ -l$$QCPLIB +linux:LIBS += -L./ -l$$QCPLIB -lasound +macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread !linux:SOURCES += ../qcustomplot/qcustomplot.cpp !linux:HEADERS += ../qcustomplot/qcustomplot.h !linux:INCLUDEPATH += ../qcustomplot +INCLUDEPATH += opus-tools/src +INCLUDEPATH += rtaudio + SOURCES += main.cpp\ - wfmain.cpp \ + wfmain.cpp \ commhandler.cpp \ rigcommander.cpp \ freqmemory.cpp \ rigidentities.cpp \ - udphandler.cpp \ - logcategories.cpp \ + udphandler.cpp \ + logcategories.cpp \ audiohandler.cpp \ calibrationwindow.cpp \ satellitesetup.cpp \ @@ -102,34 +123,39 @@ SOURCES += main.cpp\ udpserver.cpp \ meter.cpp \ qledlabel.cpp \ - pttyhandler.cpp \ - resampler/resample.c \ + pttyhandler.cpp \ + opus-tools/src/resample.c \ repeatersetup.cpp \ - rigctld.cpp \ - shuttle.cpp + rigctld.cpp \ + rtaudio/RtAudio.cpp \ + ring/ring.cpp + shuttle.cpp \ HEADERS += wfmain.h \ commhandler.h \ rigcommander.h \ freqmemory.h \ rigidentities.h \ - udphandler.h \ - logcategories.h \ + udphandler.h \ + logcategories.h \ audiohandler.h \ calibrationwindow.h \ satellitesetup.h \ udpserversetup.h \ - udpserver.h \ - packettypes.h \ + udpserver.h \ + packettypes.h \ meter.h \ - qledlabel.h \ - pttyhandler.h \ - resampler/speex_resampler.h \ - resampler/arch.h \ - resampler/resample_sse.h \ + qledlabel.h \ + pttyhandler.h \ + opus-tools/src/speex_resampler.h \ + opus-tools/src/arch.h \ + opus-tools/src/resample_sse.h \ repeatersetup.h \ repeaterattributes.h \ - rigctld.h \ + rigctld.h \ + rtaudio/RtAudio.h \ + ulaw.h \ + ring/ring.h \ shuttle.h