diff -U 3 -H -d -r -N -- mumble-1.2.2/src/mumble/JackAudio.cpp mumble-1.2.2/src/mumble/JackAudio.cpp --- mumble-1.2.2/src/mumble/JackAudio.cpp 1970-01-01 01:00:00.000000000 +0100 +++ mumble-1.2.2/src/mumble/JackAudio.cpp 2011-01-26 06:02:00.000000000 +0000 @@ -0,0 +1,314 @@ +/* Copyright (C) 2011, Benjamin Jemlich + Copyright (C) 2011, Filipe Coelho + + 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. +*/ + +#include "JackAudio.h" +#include "User.h" +#include "Global.h" +#include "MainWindow.h" +#include "Timer.h" + +#include + +static JackAudioSystem *jasys = NULL; + +class JackAudioInputRegistrar : public AudioInputRegistrar { + public: + JackAudioInputRegistrar(); + virtual AudioInput *create(); + virtual const QList getDeviceChoices(); + virtual void setDeviceChoice(const QVariant &, Settings &); + virtual bool canEcho(const QString &) const; +}; + +class JackAudioOutputRegistrar : public AudioOutputRegistrar { + public: + JackAudioOutputRegistrar(); + virtual AudioOutput *create(); + virtual const QList getDeviceChoices(); + virtual void setDeviceChoice(const QVariant &, Settings &); +}; + +class JackAudioInit : public DeferInit { + public: + JackAudioInputRegistrar *airJackAudio; + JackAudioOutputRegistrar *aorJackAudio; + void initialize() { + jasys = new JackAudioSystem(); + jasys->init_jack(); + jasys->qmWait.lock(); + jasys->qwcWait.wait(&jasys->qmWait, 1000); + jasys->qmWait.unlock(); + if (jasys->bJackIsGood) { + airJackAudio = new JackAudioInputRegistrar(); + aorJackAudio = new JackAudioOutputRegistrar(); + } else { + airJackAudio = NULL; + aorJackAudio = NULL; + delete jasys; + jasys = NULL; + } + }; + void destroy() { + if (airJackAudio) + delete airJackAudio; + if (aorJackAudio) + delete aorJackAudio; + if (jasys) { + jasys->close_jack(); + delete jasys; + jasys = NULL; + } + }; +}; + +static JackAudioInit jackinit; //unused + +JackAudioSystem::JackAudioSystem() { + bJackIsGood = false; + iSampleRate = 0; +} + +JackAudioSystem::~JackAudioSystem() { +} + +void JackAudioSystem::init_jack() +{ + client = jack_client_open("mumble", JackNullOption, 0); + + if (client) { + in_port = jack_port_register(client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + out_port = jack_port_register(client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + jack_set_process_callback(client, process_callback, this); + jack_set_sample_rate_callback(client, srate_callback, this); + jack_on_shutdown(client, shutdown_callback, this); + + iSampleRate = jack_get_sample_rate(client); + + if (jack_activate(client) || in_port == NULL || out_port == NULL) { + client = NULL; + return; + } + + int port_flags; + unsigned i = -1; + const char** ports = jack_get_ports(client, 0, 0, JackPortIsPhysical); + + if (ports) { + while (ports[++i]) + { + jack_port_t* port = jack_port_by_name(client, ports[i]); + port_flags = jack_port_flags(port); + + if (port_flags & (JackPortIsPhysical|JackPortIsOutput) && strstr(jack_port_type(port), "audio")) { + jack_connect(client, ports[i], jack_port_name(in_port)); + } + if (port_flags & (JackPortIsPhysical|JackPortIsInput) && strstr(jack_port_type(port), "audio")) { + jack_connect(client, jack_port_name(out_port), ports[i]); + } + } + } + + jack_free(ports); + + // If we made it this far, then everything is okay + qhInput.insert(QString(), tr("Hardware Ports")); + qhOutput.insert(QString(), tr("Hardware Ports")); + bJackIsGood = true; + + } else { + bJackIsGood = false; + client = NULL; + } +} + +void JackAudioSystem::close_jack() +{ + if (client) { + jack_deactivate(client); + jack_client_close(client); + client = NULL; + } +} + +int JackAudioSystem::process_callback(jack_nframes_t nframes, void *arg) +{ + JackAudioSystem *jas = (JackAudioSystem*)arg; + + if (jas && jas->bJackIsGood) { + AudioInputPtr ai = g.ai; + AudioOutputPtr ao = g.ao; + JackAudioInput *jai = (JackAudioInput*)(ai.get()); + JackAudioOutput *jao = (JackAudioOutput*)(ao.get()); + + if (jai && jai->bRunning && jai->iMicChannels > 0 && !jai->isFinished()) { + void* input = jack_port_get_buffer(jas->in_port, nframes); + if ((float*)input != 0) + jai->addMic(input, nframes); + } + + if (jao && jao->bRunning && jao->iChannels > 0 && !jao->isFinished()) { + jack_default_audio_sample_t* output = (jack_default_audio_sample_t*)jack_port_get_buffer(jas->out_port, nframes); + memset(output, 0, sizeof(jack_default_audio_sample_t)*nframes); //TEST + jao->mix(output, nframes); + } + } + + return 0; +} + +int JackAudioSystem::srate_callback(jack_nframes_t frames, void *arg) +{ + JackAudioSystem *jas = (JackAudioSystem*)arg; + jas->iSampleRate = frames; + return 0; +} + +void JackAudioSystem::shutdown_callback(void *arg) +{ + JackAudioSystem *jas = (JackAudioSystem*)arg; + jas->bJackIsGood = false; +} + +JackAudioInputRegistrar::JackAudioInputRegistrar() : AudioInputRegistrar(QLatin1String("JACK"), 10) { +} + +AudioInput *JackAudioInputRegistrar::create() { + return new JackAudioInput(); +} + +const QList JackAudioInputRegistrar::getDeviceChoices() { + QList qlReturn; + + QStringList qlInputDevs = jasys->qhInput.keys(); + qSort(qlInputDevs); + + foreach(const QString &dev, qlInputDevs) { + qlReturn << audioDevice(jasys->qhInput.value(dev), dev); + } + + return qlReturn; +} + +void JackAudioInputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) { + Q_UNUSED(choice); + Q_UNUSED(s); +} + +bool JackAudioInputRegistrar::canEcho(const QString &osys) const { + Q_UNUSED(osys); + return false; +} + +JackAudioOutputRegistrar::JackAudioOutputRegistrar() : AudioOutputRegistrar(QLatin1String("JACK"), 10) { +} + +AudioOutput *JackAudioOutputRegistrar::create() { + return new JackAudioOutput(); +} + +const QList JackAudioOutputRegistrar::getDeviceChoices() { + QList qlReturn; + + QStringList qlOutputDevs = jasys->qhOutput.keys(); + qSort(qlOutputDevs); + + foreach(const QString &dev, qlOutputDevs) { + qlReturn << audioDevice(jasys->qhOutput.value(dev), dev); + } + + return qlReturn; +} + +void JackAudioOutputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) { + Q_UNUSED(choice); + Q_UNUSED(s); +} + +JackAudioInput::JackAudioInput() { + bRunning = true; + iMicChannels = 0; +}; + +JackAudioInput::~JackAudioInput() { + bRunning = false; + iMicChannels = 0; + qmMutex.lock(); + qwcWait.wakeAll(); + qmMutex.unlock(); + wait(); +} + +void JackAudioInput::run() { + if (jasys && jasys->bJackIsGood) { + iMicFreq = jasys->iSampleRate; + iMicChannels = 1; + eMicFormat = SampleFloat; + initializeMixer(); + } + + qmMutex.lock(); + while (bRunning) + qwcWait.wait(&qmMutex); + qmMutex.unlock(); +} + +JackAudioOutput::JackAudioOutput() { + bRunning = true; + iChannels = 0; +} + +JackAudioOutput::~JackAudioOutput() { + bRunning = false; + iChannels = 0; + qmMutex.lock(); + qwcWait.wakeAll(); + qmMutex.unlock(); + wait(); +} + +void JackAudioOutput::run() { + if (jasys && jasys->bJackIsGood) { + unsigned int chanmasks[32]; + + chanmasks[0] = SPEAKER_FRONT_LEFT; + chanmasks[1] = SPEAKER_FRONT_RIGHT; + + eSampleFormat = SampleFloat; + iMixerFreq = jasys->iSampleRate; + iChannels = 1; + initializeMixer(chanmasks); + } + + qmMutex.lock(); + while (bRunning) + qwcWait.wait(&qmMutex); + qmMutex.unlock(); +} diff -U 3 -H -d -r -N -- mumble-1.2.2/src/mumble/JackAudio.h mumble-1.2.2/src/mumble/JackAudio.h --- mumble-1.2.2/src/mumble/JackAudio.h 1970-01-01 01:00:00.000000000 +0100 +++ mumble-1.2.2/src/mumble/JackAudio.h 2011-01-26 06:03:58.000000000 +0000 @@ -0,0 +1,97 @@ +/* Copyright (C) 2011, Benjamin Jemlich + Copyright (C) 2011, Filipe Coelho + + 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 _JACKAUDIO_H +#define _JACKAUDIO_H + +#include "AudioInput.h" +#include "AudioOutput.h" +#include + +class JackAudioOutput; +class JackAudioInput; + +class JackAudioSystem : public QObject { + private: + Q_OBJECT + Q_DISABLE_COPY(JackAudioSystem) + protected: + jack_client_t* client; + jack_port_t* in_port; + jack_port_t* out_port; + + static int process_callback(jack_nframes_t nframes, void *arg); + static int srate_callback(jack_nframes_t frames, void *arg); + static void shutdown_callback(void *arg); + public: + QHash qhInput; + QHash qhOutput; + bool bJackIsGood; + int iSampleRate; + QMutex qmWait; + QWaitCondition qwcWait; + + void init_jack(); + void close_jack(); + + JackAudioSystem(); + ~JackAudioSystem(); +}; + +class JackAudioInput : public AudioInput { + friend class JackAudioSystem; + private: + Q_OBJECT + Q_DISABLE_COPY(JackAudioInput) + protected: + QMutex qmMutex; + QWaitCondition qwcWait; + public: + JackAudioInput(); + ~JackAudioInput(); + void run(); +}; + +class JackAudioOutput : public AudioOutput { + friend class JackAudioSystem; + private: + Q_OBJECT + Q_DISABLE_COPY(JackAudioOutput) + protected: + QMutex qmMutex; + QWaitCondition qwcWait; + public: + JackAudioOutput(); + ~JackAudioOutput(); + void run(); +}; + +#endif diff -U 3 -H -d -r -N -- mumble-1.2.2/src/mumble/mumble.pro mumble-1.2.2/src/mumble/mumble.pro --- mumble-1.2.2/src/mumble/mumble.pro 2010-02-09 16:34:51.000000000 +0000 +++ mumble-1.2.2/src/mumble/mumble.pro 2011-01-26 01:45:55.000000000 +0000 @@ -93,11 +93,17 @@ unix { HAVE_PULSEAUDIO=$$system(pkg-config --modversion --silence-errors libpulse) HAVE_PORTAUDIO=$$system(pkg-config --modversion --silence-errors portaudio-2.0) + HAVE_JACKAUDIO=$$system(pkg-config --modversion --silence-errors jack) !isEmpty(HAVE_PORTAUDIO):!CONFIG(no-portaudio) { CONFIG *= portaudio } + !isEmpty(HAVE_JACKAUDIO):!CONFIG(no-jackaudio) { + CONFIG -= portaudio + CONFIG *= jackaudio + } + !isEmpty(HAVE_PULSEAUDIO):!CONFIG(no-pulseaudio) { CONFIG -= portaudio CONFIG *= pulseaudio @@ -110,6 +116,13 @@ QMAKE_CXXFLAGS_DEBUG *= -I../../speex/include -I../../speexbuild } + jackaudio { + DEFINES *= USE_JACKAUDIO + PKGCONFIG *= jack + HEADERS *= JackAudio.h + SOURCES *= JackAudio.cpp + } + CONFIG *= link_pkgconfig PKGCONFIG *= openssl sndfile