From 7d6e90ca2bd437949c53b7f6091b316934ebddef Mon Sep 17 00:00:00 2001 From: Benjamin Jemlich Date: Mon, 15 Mar 2010 19:53:13 +0100 Subject: [PATCH] Add mp3 playback support in the test client --- .gitignore | 4 +- .gitmodules | 3 + CMakeLists.txt | 22 ++- PacketDataStream.h | 356 ++++++++++++++++++++++++++++++++++++++ celt | 1 + celt-build/CMakeLists.txt | 25 +++ celt-build/config.h | 185 ++++++++++++++++++++ main.cc | 126 ++++++++++++++ 8 files changed, 719 insertions(+), 3 deletions(-) create mode 100644 .gitmodules create mode 100644 PacketDataStream.h create mode 160000 celt create mode 100644 celt-build/CMakeLists.txt create mode 100644 celt-build/config.h diff --git a/.gitignore b/.gitignore index 7d9c45d..cb3650d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -*.db +*.a +*.so *.pb.* *.cmake Makefile @@ -6,3 +7,4 @@ main pkcs11.txt CMakeCache.txt CMakeFiles/* +celt-build/CMakeFiles/* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f1abfd8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "celt"] + path = celt + url = git://git.xiph.org/celt.git/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 7eff32c..c87f123 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,18 +4,36 @@ set (libmumbleclient_VERSION_MAJOR 0) set (libmumbleclient_VERSION_MINOR 0) set(CMAKE_INCLUDE_CURRENT_DIR ON) +include(FindPkgConfig) + find_package(Boost 1.38.0 REQUIRED COMPONENTS system thread) find_package(OpenSSL REQUIRED) find_package(Protobuf REQUIRED) +pkg_check_modules(CELT celt>=0.7.0) + +if (NOT CELT_FOUND) + add_subdirectory(celt-build) + set(CELT_LIBRARIES celt0) + set(CELT_INCLUDE_DIR celt/libcelt) +endif() + SET(CMAKE_BUILD_TYPE Debug) SET(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -pedantic -Wfatal-errors -Wshadow") SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra -Wfatal-errors -Wshadow -Woverloaded-virtual -Wold-style-cast") -include_directories(${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR}) +include_directories(${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${CELT_INCLUDE_DIR}) PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS Mumble.proto) add_library(mumbleclient client.cc client_lib.cc CryptState.cpp ${PROTO_SRCS} ${PROTO_HDRS}) + +option(WITH_MPG123 "Add mp3 playback support" ON) + +if (WITH_MPG123) + add_definitions(-DWITH_MPG123) + set(LIBRARIES mpg123) +endif() + add_executable (main main.cc) -target_link_libraries (main mumbleclient ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${PROTOBUF_LIBRARY}) +target_link_libraries (main mumbleclient ${LIBRARIES} ${CELT_LIBRARIES} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${PROTOBUF_LIBRARY}) diff --git a/PacketDataStream.h b/PacketDataStream.h new file mode 100644 index 0000000..0b38eb9 --- /dev/null +++ b/PacketDataStream.h @@ -0,0 +1,356 @@ +/* Copyright (C) 2005-2010, Thorvald Natvig + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _PACKETDATASTREAM_H +#define _PACKETDATASTREAM_H + +#include + +#include +#include + +/* + * GCC doesn't yet do inter-object-file inlining, so unfortunately, this all has to be defined here. + */ + +class PacketDataStream { + private: +// Q_DISABLE_COPY(PacketDataStream) + private: + unsigned char *data; + uint32_t maxsize; + uint32_t offset; + uint32_t overshoot; + bool ok; + public: + uint32_t size() const { + return offset; + } + + uint32_t capacity() const { + return maxsize; + } + + bool isValid() const { + return ok; + } + + uint32_t left() const { + return maxsize - offset; + } + + uint32_t undersize() const { + return overshoot; + } + + void append(const uint64_t v) { + if (offset < maxsize) + data[offset++] = static_cast(v); + else { + ok = false; + overshoot++; + } + } + + void append(const char *d, uint32_t len) { + if (left() >= len) { + memcpy(& data[offset], d, len); + offset += len; + } else { + int l = left(); + memset(& data[offset], 0, l); + offset += l; + overshoot += len - l; + ok = false; + } + } + + void append(const std::string& str) { + uint32_t len = str.size(); + if (left() >= len) { + memcpy(& data[offset], str.data(), len); + offset += len; + } else { + int l = left(); + memset(& data[offset], 0, l); + offset += l; + overshoot += len - l; + ok = false; + } + } + + void skip(uint32_t len) { + if (left() >= len) + offset += len; + else + ok = false; + } + + uint64_t next() { + if (offset < maxsize) + return data[offset++]; + else { + ok = false; + return 0; + } + } + + uint8_t next8() { + if (offset < maxsize) + return data[offset++]; + else { + ok = false; + return 0; + } + } + + void rewind() { + offset = 0; + } + + void truncate() { + maxsize = offset; + } + + const unsigned char *dataPtr() const { + return reinterpret_cast(& data[offset]); + } + + const char *charPtr() const { + return reinterpret_cast(& data[offset]); + } + + std::vector dataBlock(uint32_t len) { + if (len <= left()) { + std::vector v(len); + memcpy(&v[0], charPtr(), len); + offset +=len; + return v; + } else { + ok = false; + return std::vector(); + } + } + + protected: + void setup(unsigned char *d, int msize) { + data = d; + offset = 0; + overshoot = 0; + maxsize = msize; + ok = true; + } + public: + PacketDataStream(const char *d, int msize) { + setup(const_cast(reinterpret_cast(d)), msize); + } + + PacketDataStream(char *d, int msize) { + setup(reinterpret_cast(d), msize); + } + + PacketDataStream(const unsigned char *d, int msize) { + setup(const_cast(d), msize); + } + + PacketDataStream(unsigned char *d, int msize) { + setup(d, msize); + } + + PacketDataStream &operator <<(const uint64_t value) { + uint64_t i = value; + + if ((i & 0x8000000000000000LL) && (~i < 0x100000000LL)) { + // Signed number. + i = ~i; + if (i <= 0x3) { + // Shortcase for -1 to -4 + append(0xFC | i); + return *this; + } else { + append(0xF8); + } + } + if (i < 0x80) { + // Need top bit clear + append(i); + } else if (i < 0x4000) { + // Need top two bits clear + append((i >> 8) | 0x80); + append(i & 0xFF); + } else if (i < 0x200000) { + // Need top three bits clear + append((i >> 16) | 0xC0); + append((i >> 8) & 0xFF); + append(i & 0xFF); + } else if (i < 0x10000000) { + // Need top four bits clear + append((i >> 24) | 0xE0); + append((i >> 16) & 0xFF); + append((i >> 8) & 0xFF); + append(i & 0xFF); + } else if (i < 0x100000000LL) { + // It's a full 32-bit integer. + append(0xF0); + append((i >> 24) & 0xFF); + append((i >> 16) & 0xFF); + append((i >> 8) & 0xFF); + append(i & 0xFF); + } else { + // It's a 64-bit value. + append(0xF4); + append((i >> 56) & 0xFF); + append((i >> 48) & 0xFF); + append((i >> 40) & 0xFF); + append((i >> 32) & 0xFF); + append((i >> 24) & 0xFF); + append((i >> 16) & 0xFF); + append((i >> 8) & 0xFF); + append(i & 0xFF); + } + return *this; + } + + PacketDataStream &operator >>(uint64_t &i) { + uint64_t v = next(); + + if ((v & 0x80) == 0x00) { + i=(v & 0x7F); + } else if ((v & 0xC0) == 0x80) { + i=(v & 0x3F) << 8 | next(); + } else if ((v & 0xF0) == 0xF0) { + switch (v & 0xFC) { + case 0xF0: + i=next() << 24 | next() << 16 | next() << 8 | next(); + break; + case 0xF4: + i=next() << 56 | next() << 48 | next() << 40 | next() << 32 | next() << 24 | next() << 16 | next() << 8 | next(); + break; + case 0xF8: + *this >> i; + i = ~i; + break; + case 0xFC: + i=v & 0x03; + i = ~i; + break; + default: + ok = false; + i = 0; + break; + } + } else if ((v & 0xF0) == 0xE0) { + i=(v & 0x0F) << 24 | next() << 16 | next() << 8 | next(); + } else if ((v & 0xE0) == 0xC0) { + i=(v & 0x1F) << 16 | next() << 8 | next(); + } + return *this; + } + + PacketDataStream &operator <<(const bool b) { + uint32_t v = b ? 1 : 0; + return *this << v; + } + + PacketDataStream &operator >>(bool &b) { + uint32_t v; + *this >> v; + b = v ? true : false; + return *this; + } + +#define INTMAPOPERATOR(type) \ + PacketDataStream &operator <<(const type v) { \ + return *this << static_cast(v); \ + } \ + PacketDataStream &operator >>(type &v) { \ + uint64_t vv; \ + *this >> vv; \ + v = static_cast(vv); \ + return *this; \ + } + + + INTMAPOPERATOR(int); + INTMAPOPERATOR(unsigned int); + INTMAPOPERATOR(short); + INTMAPOPERATOR(unsigned short); + INTMAPOPERATOR(char); + INTMAPOPERATOR(unsigned char); + + union double64u { + uint64_t ui; + double d; + }; + + PacketDataStream &operator <<(const double v) { + double64u u; + u.d = v; + return *this << u.ui; + } + + PacketDataStream &operator >>(double &v) { + double64u u; + *this >> u.ui; + v = u.d; + return *this; + } + + union float32u { + uint8_t ui[4]; + float f; + }; + + PacketDataStream &operator <<(const float v) { + float32u u; + u.f = v; + append(u.ui[0]); + append(u.ui[1]); + append(u.ui[2]); + append(u.ui[3]); + return *this; + } + + PacketDataStream &operator >>(float &v) { + float32u u; + if (left() < 4) { + ok = false; + v = 0; + } + u.ui[0] = next8(); + u.ui[1] = next8(); + u.ui[2] = next8(); + u.ui[3] = next8(); + v = u.f; + return *this; + } +}; + +#endif diff --git a/celt b/celt new file mode 160000 index 0000000..5378bf8 --- /dev/null +++ b/celt @@ -0,0 +1 @@ +Subproject commit 5378bf82530b1d431c34010f693637cb826fbc51 diff --git a/celt-build/CMakeLists.txt b/celt-build/CMakeLists.txt new file mode 100644 index 0000000..ff40852 --- /dev/null +++ b/celt-build/CMakeLists.txt @@ -0,0 +1,25 @@ +set(SOURCE_DIR ../celt/libcelt) + +add_library(celt0 +${SOURCE_DIR}/bands.c +${SOURCE_DIR}/celt.c +${SOURCE_DIR}/cwrs.c +${SOURCE_DIR}/entcode.c +${SOURCE_DIR}/entdec.c +${SOURCE_DIR}/entenc.c +${SOURCE_DIR}/header.c +${SOURCE_DIR}/kiss_fft.c +${SOURCE_DIR}/kiss_fftr.c +${SOURCE_DIR}/laplace.c +${SOURCE_DIR}/mdct.c +${SOURCE_DIR}/modes.c +${SOURCE_DIR}/pitch.c +${SOURCE_DIR}/psy.c +${SOURCE_DIR}/quant_bands.c +${SOURCE_DIR}/rangedec.c +${SOURCE_DIR}/rangeenc.c +${SOURCE_DIR}/rate.c +${SOURCE_DIR}/vq.c +) + +set_target_properties(celt0 PROPERTIES COMPILE_DEFINITIONS HAVE_CONFIG_H) diff --git a/celt-build/config.h b/celt-build/config.h new file mode 100644 index 0000000..9ea5eda --- /dev/null +++ b/celt-build/config.h @@ -0,0 +1,185 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* This is a build of CELT */ +#define CELT_BUILD /**/ + +/* Version extra */ +#define CELT_EXTRA_VERSION "" + +/* Version major */ +#define CELT_MAJOR_VERSION 0 + +/* Version micro */ +#define CELT_MICRO_VERSION 1 + +/* Version minor */ +#define CELT_MINOR_VERSION 7 + +/* Complete version string */ +#define CELT_VERSION "0.7.1" + +/* Compile as fixed-point */ +/* #undef DOUBLE_PRECISION */ + +/* Assertions */ +/* #undef ENABLE_ASSERTIONS */ + +/* Debug fixed-point implementation */ +/* #undef FIXED_DEBUG */ + +/* Compile as fixed-point */ +/* #undef FIXED_POINT */ + +/* Compile as floating-point */ +#define FLOATING_POINT /**/ + +/* Float approximations */ +/* #undef FLOAT_APPROX */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long' function. */ +#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the `winmm' library (-lwinmm). */ +/* #undef HAVE_LIBWINMM */ + +/* Define if you have C99's lrint function. */ +#define HAVE_LRINT 1 + +/* Define if you have C99's lrintf function. */ +#define HAVE_LRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_AUDIOIO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOUNDCARD_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Compile as fixed-point */ +/* #undef MIXED_PRECISION */ + +/* Use new PLC code */ +#define NEW_PLC /**/ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 8 + +/* The size of `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 8 + +/* The size of `short', as computed by sizeof. */ +#define SIZEOF_SHORT 2 + +/* Static modes */ +/* #undef STATIC_MODES */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Make use of alloca */ +/* #undef USE_ALLOCA */ + +/* Use C99 variable-size arrays */ +#define VAR_ARRAYS /**/ + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to the equivalent of the C99 'restrict' keyword, or to + nothing if this is not supported. Do not define if restrict is + supported directly. */ +#define restrict __restrict +/* Work around a bug in Sun C++: it does not support _Restrict or + __restrict__, even though the corresponding Sun C compiler ends up with + "#define restrict _Restrict" or "#define restrict __restrict__" in the + previous line. Perhaps some future version of Sun C++ will work with + restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ +#if defined __SUNPRO_CC && !defined __RESTRICT +# define _Restrict +# define __restrict__ +#endif diff --git a/main.cc b/main.cc index 4cc7c92..0347380 100644 --- a/main.cc +++ b/main.cc @@ -1,3 +1,8 @@ +#include "celt.h" +#ifdef WITH_MPG123 +#include +#endif + #include #include #include @@ -6,8 +11,12 @@ #include "client_lib.h" #include "CryptState.h" #include "messages.h" +#include "PacketDataStream.h" #include "settings.h" +// Always 48000 for Mumble +static const int32_t kSampleRate = 48000; + bool recording = false; bool playback = false; @@ -110,6 +119,114 @@ void playbackFunction(MumbleClient::MumbleClient* mc) { std::cout << ">> playback thread" << std::endl; } +#ifdef WITH_MPG123 +void playMp3(MumbleClient::MumbleClient* mc) { + std::cout << "<< play mp3 thread" << std::endl; + +// struct sched_param param; +// param.sched_priority = 1; +// pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m); + + // FIXME(pcgod): 1-6 to match Mumble client + int frames = 6; +// int audio_quality = 96000; + int audio_quality = 60000; + + int err = mpg123_init(); + mpg123_handle *mh = mpg123_new(NULL, &err); + mpg123_param(mh, MPG123_VERBOSE, 255, 0); + mpg123_param(mh, MPG123_RVA, MPG123_RVA_MIX, 0); + mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_MONO_MIX, 0); + mpg123_param(mh, MPG123_FORCE_RATE, kSampleRate, 0); + mpg123_open(mh, ""); + + long rate = 0; + int channels = 0, encoding = 0; + mpg123_getformat(mh, &rate, &channels, &encoding); + mpg123_format_none(mh); + + rate = kSampleRate; + channels = MPG123_MONO; + err = mpg123_format(mh, rate, channels, encoding); + + // FIXME(pcgod): maybe broken for mono MP3s + size_t buffer_size = (kSampleRate / 100) * 2/* * channels*/; + + CELTMode *cmMode = celt_mode_create(kSampleRate, kSampleRate / 100, NULL); + CELTEncoder *ce = celt_encoder_create(cmMode, 1, NULL); + + celt_encoder_ctl(ce, CELT_SET_PREDICTION(0)); + celt_encoder_ctl(ce, CELT_SET_VBR_RATE(audio_quality)); + + std::deque packet_list; + + std::cout << "decoding..." << std::endl; + unsigned char* buffer = static_cast(malloc(buffer_size)); + do { + size_t done = 0; + unsigned char out[512]; + + err = mpg123_read(mh, buffer, buffer_size, &done); + int32_t len = celt_encode(ce, reinterpret_cast(buffer), NULL, out, std::min(audio_quality / (100 * 8), 127)); + // std::cout << (done / sizeof(short)) << " samples - bitrate: " << (len * 100 * 8) << std::endl; + + packet_list.push_back(std::string(reinterpret_cast(out), len)); + } while (err == MPG123_OK); + + if (err != MPG123_DONE) + std::cerr << "Warning: Decoding ended prematurely because: " << (err == MPG123_ERR ? mpg123_strerror(mh) : mpg123_plain_strerror(err)) << std::endl; + + std::cout << "finished decoding" << std::endl; + + free(buffer); + celt_encoder_destroy(ce); + celt_mode_destroy(cmMode); + mpg123_close(mh); + mpg123_delete(mh); + mpg123_exit(); + + + int32_t seq = 0; + while (playback && !packet_list.empty()) { + // build pds + char data[1024]; + int flags = 0; // target = 0 + flags |= (MumbleClient::UdpMessageType::UDPVoiceCELTAlpha << 5); + data[0] = static_cast(flags); + + PacketDataStream pds(data + 1, 1023); + seq += frames; + pds << seq; + // Append |frames| frames to pds + for (int i = 0; i < frames; ++i) { + if (packet_list.empty()) break; + const std::string& s = packet_list.front(); + + unsigned char head = s.size(); + // Add 0x80 to all but the last frame + if (i < frames - 1) + head |= 0x80; + + pds.append(head); + pds.append(s); + + packet_list.pop_front(); + } + +#define TCP 0 +#if TCP + mc->SendRawUdpTunnel(data, pds.size() + 1); +#else + mc->SendUdpMessage(data, pds.size() + 1); +#endif + boost::this_thread::sleep(boost::posix_time::milliseconds((frames) * 10)); + } + + playback = false; + std::cout << ">> play mp3 thread" << std::endl; +} +#endif + void AuthCallback() { std::cout << "I'm authenticated" << std::endl; } @@ -122,6 +239,13 @@ void TextMessageCallback(const std::string& message, MumbleClient::MumbleClient* playback = true; playback_thread = new boost::thread(playbackFunction, mc); } +#ifdef WITH_MPG123 + } else if (message == "playmp3") { + if (playback == false) { + playback = true; + playback_thread = new boost::thread(playMp3, mc); + } +#endif } else if (message == "stop") { recording = false; playback = false; @@ -164,6 +288,8 @@ int main(int /* argc */, char** /* argv[] */) { // MumbleClient::MumbleClient* mc2 = mcl->NewClient(); // mc2->Connect(MumbleClient::Settings("0xy.org", "64739", "testBot2", "")); +// mc2->SetTextMessageCallback(boost::bind(&TextMessageCallback, _1, mc2)); +// mc2->SetRawUdpTunnelCallback(boost::bind(&RawUdpTunnelCallback, _1, _2)); // Start event loop mcl->Run();