# HG changeset patch # User Chris Cannam # Date 1420475854 0 # Mon Jan 05 16:37:34 2015 +0000 # Branch win32_mediafoundation # Node ID 8473396b6dc7b0aa194b743dbae230d7daa75237 # Parent 116671914658deb87c45631e298643985baab2da # Parent 05150c68c42c74334fe269f579f7ee80e576d20b Merge from the default branch diff --git a/AudioFactory.cpp b/AudioFactory.cpp --- a/AudioFactory.cpp +++ b/AudioFactory.cpp @@ -4,6 +4,7 @@ #include "AudioFactory.h" #include "JACKPlaybackTarget.h" +#include "PulseAudioPlaybackTarget.h" #include "PortAudioPlaybackTarget.h" #include "JACKRecordSource.h" @@ -31,6 +32,15 @@ } #endif +#ifdef HAVE_LIBPULSE + target = new PulseAudioPlaybackTarget(source); + if (target->isTargetOK()) return target; + else { + std::cerr << "WARNING: AudioFactory::createCallbackTarget: Failed to open PulseAudio target" << std::endl; + delete target; + } +#endif + #ifdef HAVE_PORTAUDIO target = new PortAudioPlaybackTarget(source); if (target->isTargetOK()) return target; diff --git a/PulseAudioIO.cpp b/PulseAudioIO.cpp --- a/PulseAudioIO.cpp +++ b/PulseAudioIO.cpp @@ -21,7 +21,7 @@ namespace Turbot { PulseAudioIO::PulseAudioIO(ApplicationRecordTarget *target, - ApplicationPlaybackSource *source) : + ApplicationPlaybackSource *source) : SystemAudioIO(target, source), m_mutex(QMutex::Recursive), //!!!??? m_loop(0), @@ -32,6 +32,7 @@ m_loopThread(0), m_bufferSize(0), m_sampleRate(0), + m_paChannels(2), m_done(false), m_captureReady(false), m_playbackReady(false) @@ -56,7 +57,7 @@ m_sampleRate = m_source->getApplicationSampleRate(); } m_spec.rate = m_sampleRate; - m_spec.channels = 2; + m_spec.channels = m_paChannels; m_spec.format = PA_SAMPLE_FLOAT32NE; m_context = pa_context_new(m_api, "turbot"); @@ -206,14 +207,14 @@ tmpbuf[i] = new float[tmpbufsz]; } - output = new float[tmpbufsz * tmpbufch]; + output = new float[tmpbufsz * m_paChannels]; } m_source->getSourceSamples(nframes, tmpbuf); float peakLeft = 0.0, peakRight = 0.0; - for (int ch = 0; ch < 2; ++ch) { + for (int ch = 0; ch < m_paChannels; ++ch) { float peak = 0.0; @@ -221,22 +222,22 @@ // PulseAudio samples are interleaved for (int i = 0; i < nframes; ++i) { - output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain; - float sample = fabsf(output[i * 2 + ch]); + output[i * m_paChannels + ch] = tmpbuf[ch][i] * m_outputGain; + float sample = fabsf(output[i * m_paChannels + ch]); if (sample > peak) peak = sample; } } else if (ch == 1 && sourceChannels == 1) { for (int i = 0; i < nframes; ++i) { - output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain; - float sample = fabsf(output[i * 2 + ch]); + output[i * m_paChannels + ch] = tmpbuf[0][i] * m_outputGain; + float sample = fabsf(output[i * m_paChannels + ch]); if (sample > peak) peak = sample; } } else { for (int i = 0; i < nframes; ++i) { - output[i * 2 + ch] = 0; + output[i * m_paChannels + ch] = 0; } } @@ -381,7 +382,7 @@ cerr << "PulseAudioIO::streamStateChanged" << endl; #endif - assert(stream == m_in || stream == m_out); + assert(stream == m_out); QMutexLocker locker(&m_mutex); diff --git a/PulseAudioIO.h b/PulseAudioIO.h --- a/PulseAudioIO.h +++ b/PulseAudioIO.h @@ -72,6 +72,7 @@ int m_bufferSize; int m_sampleRate; + int m_paChannels; bool m_done; bool m_captureReady; diff --git a/PulseAudioIO.cpp b/PulseAudioPlaybackTarget.cpp copy from PulseAudioIO.cpp copy to PulseAudioPlaybackTarget.cpp --- a/PulseAudioIO.cpp +++ b/PulseAudioPlaybackTarget.cpp @@ -3,9 +3,8 @@ #ifdef HAVE_LIBPULSE -#include "PulseAudioIO.h" +#include "PulseAudioPlaybackTarget.h" #include "ApplicationPlaybackSource.h" -#include "ApplicationRecordTarget.h" #include "VectorOps.h" @@ -16,33 +15,31 @@ using std::cout; using std::endl; -//#define DEBUG_AUDIO_PULSE_AUDIO_IO 1 +//#define DEBUG_PULSE_AUDIO_PLAYBACK_TARGET 1 namespace Turbot { -PulseAudioIO::PulseAudioIO(ApplicationRecordTarget *target, - ApplicationPlaybackSource *source) : - SystemAudioIO(target, source), +PulseAudioPlaybackTarget::PulseAudioPlaybackTarget(ApplicationPlaybackSource *source) : + SystemPlaybackTarget(source), m_mutex(QMutex::Recursive), //!!!??? m_loop(0), m_api(0), m_context(0), - m_in(0), m_out(0), m_loopThread(0), m_bufferSize(0), m_sampleRate(0), + m_paChannels(2), m_done(false), - m_captureReady(false), m_playbackReady(false) { -#ifdef DEBUG_AUDIO_PULSE_AUDIO_IO - cerr << "PulseAudioIO: Initialising for PulseAudio" << endl; +#ifdef DEBUG_PULSE_AUDIO_PLAYBACK_TARGET + cerr << "PulseAudioPlaybackTarget: Initialising for PulseAudio" << endl; #endif m_loop = pa_mainloop_new(); if (!m_loop) { - cerr << "ERROR: PulseAudioIO: Failed to create main loop" << endl; + cerr << "ERROR: PulseAudioPlaybackTarget: Failed to create main loop" << endl; return; } @@ -56,12 +53,12 @@ m_sampleRate = m_source->getApplicationSampleRate(); } m_spec.rate = m_sampleRate; - m_spec.channels = 2; + m_spec.channels = m_paChannels; m_spec.format = PA_SAMPLE_FLOAT32NE; m_context = pa_context_new(m_api, "turbot"); if (!m_context) { - cerr << "ERROR: PulseAudioIO: Failed to create context object" << endl; + cerr << "ERROR: PulseAudioPlaybackTarget: Failed to create context object" << endl; return; } @@ -72,14 +69,14 @@ m_loopThread = new MainLoopThread(m_loop); m_loopThread->start(); -#ifdef DEBUG_PULSE_AUDIO_IO - cerr << "PulseAudioIO: initialised OK" << endl; +#ifdef DEBUG_PULSE_AUDIO_PLAYBACK_TARGET + cerr << "PulseAudioPlaybackTarget: initialised OK" << endl; #endif } -PulseAudioIO::~PulseAudioIO() +PulseAudioPlaybackTarget::~PulseAudioPlaybackTarget() { - cerr << "PulseAudioIO::~PulseAudioIO()" << endl; + cerr << "PulseAudioPlaybackTarget::~PulseAudioPlaybackTarget()" << endl; //!!! if (m_source) { // m_source->setTarget(0, m_bufferSize); @@ -89,7 +86,6 @@ QMutexLocker locker(&m_mutex); - if (m_in) pa_stream_unref(m_in); if (m_out) pa_stream_unref(m_out); if (m_context) pa_context_unref(m_context); @@ -99,35 +95,17 @@ pa_mainloop_free(m_loop); } - cerr << "PulseAudioIO::~PulseAudioIO() done" << endl; + cerr << "PulseAudioPlaybackTarget::~PulseAudioPlaybackTarget() done" << endl; } bool -PulseAudioIO::isSourceOK() const -{ - return (m_context != 0); -} - -bool -PulseAudioIO::isTargetOK() const +PulseAudioPlaybackTarget::isTargetOK() const { return (m_context != 0); } -bool -PulseAudioIO::isSourceReady() const -{ - return m_captureReady; -} - -bool -PulseAudioIO::isTargetReady() const -{ - return m_playbackReady; -} - double -PulseAudioIO::getCurrentTime() const +PulseAudioPlaybackTarget::getCurrentTime() const { if (!m_out) return 0.0; @@ -137,11 +115,11 @@ } void -PulseAudioIO::streamWriteStatic(pa_stream *stream, - size_t length, - void *data) +PulseAudioPlaybackTarget::streamWriteStatic(pa_stream *stream, + size_t length, + void *data) { - PulseAudioIO *target = (PulseAudioIO *)data; + PulseAudioPlaybackTarget *target = (PulseAudioPlaybackTarget *)data; assert(stream == target->m_out); @@ -149,13 +127,16 @@ } void -PulseAudioIO::streamWrite(int requested) +PulseAudioPlaybackTarget::streamWrite(int requested) { -#ifdef DEBUG_AUDIO_PULSE_AUDIO_IO - cout << "PulseAudioIO::streamWrite(" << requested << ")" << endl; +#ifdef DEBUG_PULSE_AUDIO_PLAYBACK_TARGET + cout << "PulseAudioPlaybackTarget::streamWrite(" << requested << ")" << endl; #endif if (m_done) return; + // requested is a byte count for an interleaved buffer, so number + // of frames = requested / (channels * sizeof(float)) + QMutexLocker locker(&m_mutex); pa_usec_t latency = 0; @@ -172,17 +153,28 @@ int sourceChannels = m_source->getApplicationChannelCount(); if (sourceChannels == 0) { + //!!! but this is incredibly cpu-intensive and poor for + //!!! battery life, because it happens all the time when not + //!!! playing. should pause and resume the stream somehow + int samples = requested / sizeof(float); + output = new float[samples]; + for (int i = 0; i < samples; ++i) { + output[i] = 0.f; + } + pa_stream_write(m_out, output, samples * sizeof(float), + 0, 0, PA_SEEK_RELATIVE); + m_source->setOutputLevels(0.f, 0.f); return; } int nframes = requested / (sourceChannels * sizeof(float)); //!!! this should be dividing by how many channels PA thinks it has! if (nframes > m_bufferSize) { - cerr << "WARNING: PulseAudioIO::streamWrite: nframes " << nframes << " > m_bufferSize " << m_bufferSize << endl; + cerr << "WARNING: PulseAudioPlaybackTarget::streamWrite: nframes " << nframes << " > m_bufferSize " << m_bufferSize << endl; } -#ifdef DEBUG_AUDIO_PULSE_AUDIO_IO - cout << "PulseAudioIO::streamWrite: nframes = " << nframes << endl; +#ifdef DEBUG_PULSE_AUDIO_PLAYBACK_TARGET + cerr << "PulseAudioPlaybackTarget::streamWrite: nframes = " << nframes << endl; #endif if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < nframes) { @@ -206,14 +198,14 @@ tmpbuf[i] = new float[tmpbufsz]; } - output = new float[tmpbufsz * tmpbufch]; + output = new float[tmpbufsz * m_paChannels]; } m_source->getSourceSamples(nframes, tmpbuf); float peakLeft = 0.0, peakRight = 0.0; - for (int ch = 0; ch < 2; ++ch) { + for (int ch = 0; ch < m_paChannels; ++ch) { float peak = 0.0; @@ -221,22 +213,22 @@ // PulseAudio samples are interleaved for (int i = 0; i < nframes; ++i) { - output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain; - float sample = fabsf(output[i * 2 + ch]); + output[i * m_paChannels + ch] = tmpbuf[ch][i] * m_outputGain; + float sample = fabsf(output[i * m_paChannels + ch]); if (sample > peak) peak = sample; } } else if (ch == 1 && sourceChannels == 1) { for (int i = 0; i < nframes; ++i) { - output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain; - float sample = fabsf(output[i * 2 + ch]); + output[i * m_paChannels + ch] = tmpbuf[0][i] * m_outputGain; + float sample = fabsf(output[i * m_paChannels + ch]); if (sample > peak) peak = sample; } } else { for (int i = 0; i < nframes; ++i) { - output[i * 2 + ch] = 0; + output[i * m_paChannels + ch] = 0; } } @@ -244,7 +236,7 @@ if (ch > 0 || sourceChannels == 1) peakRight = peak; } -#ifdef DEBUG_AUDIO_PULSE_AUDIO_IO +#ifdef DEBUG_PULSE_AUDIO_PLAYBACK_TARGET cerr << "calling pa_stream_write with " << nframes * tmpbufch * sizeof(float) << " bytes" << endl; #endif @@ -252,136 +244,32 @@ pa_stream_write(m_out, output, nframes * tmpbufch * sizeof(float), 0, 0, PA_SEEK_RELATIVE); +// cerr << "peaks: " << peakLeft << ", " << peakRight << endl; + m_source->setOutputLevels(peakLeft, peakRight); return; } void -PulseAudioIO::streamReadStatic(pa_stream *stream, - size_t length, - void *data) -{ - PulseAudioIO *target = (PulseAudioIO *)data; - - assert(stream == target->m_in); - - target->streamRead(length); -} - -void -PulseAudioIO::streamRead(int available) -{ -#ifdef DEBUG_AUDIO_PULSE_AUDIO_IO - cerr << "PulseAudioIO::streamRead(" << available << ")" << endl; -#endif - - QMutexLocker locker(&m_mutex); - - pa_usec_t latency = 0; - int negative = 0; - if (!pa_stream_get_latency(m_in, &latency, &negative)) { - int latframes = (latency / 1000000.f) * float(m_sampleRate); - if (latframes > 0) m_target->setSystemRecordLatency(latframes); - } - - int sz = pa_stream_readable_size(m_in); - - static const void *input = 0; - static float **tmpbuf = 0; - static int tmpbufch = 0; - static int tmpbufsz = 0; - - int targetChannels = m_target->getApplicationChannelCount(); - - //!!! need to handle mismatches between our and PA's expectations! - - if (targetChannels < 2) targetChannels = 2; - - int nframes = available / (targetChannels * sizeof(float)); //!!! this should be dividing by how many channels PA thinks it has! - - if (nframes > m_bufferSize) { - cerr << "WARNING: PulseAudioIO::streamRead: nframes " << nframes << " > m_bufferSize " << m_bufferSize << endl; - } - -#ifdef DEBUG_AUDIO_PULSE_AUDIO_IO - cout << "PulseAudioIO::streamRead: nframes = " << nframes << endl; -#endif - - if (!tmpbuf || tmpbufch != targetChannels || int(tmpbufsz) < nframes) { - - if (tmpbuf) { - for (int i = 0; i < tmpbufch; ++i) { - delete[] tmpbuf[i]; - } - delete[] tmpbuf; - } - - tmpbufch = targetChannels; - tmpbufsz = nframes; - tmpbuf = new float *[tmpbufch]; - - for (int i = 0; i < tmpbufch; ++i) { - tmpbuf[i] = new float[tmpbufsz]; - } - } - - float peakLeft = 0.0, peakRight = 0.0; - - size_t actual = available; - - pa_stream_peek(m_in, &input, &actual); - - int actualFrames = actual / (targetChannels * sizeof(float)); //!!! this should be dividing by how many channels PA thinks it has! - - if (actualFrames < nframes) { - cerr << "WARNING: streamRead: read " << actualFrames << " frames, expected " << nframes << endl; - } - - const float *finput = (const float *)input; - - for (int ch = 0; ch < targetChannels; ++ch) { - - float peak = 0.0; - - // PulseAudio samples are interleaved - for (int i = 0; i < nframes; ++i) { - tmpbuf[ch][i] = finput[i * 2 + ch]; - float sample = fabsf(finput[i * 2 + ch]); - if (sample > peak) peak = sample; - } - - if (ch == 0) peakLeft = peak; - if (ch > 0 || targetChannels == 1) peakRight = peak; - } - - m_target->putSamples(nframes, tmpbuf); - m_target->setInputLevels(peakLeft, peakRight); - - pa_stream_drop(m_in); - - return; -} - -void -PulseAudioIO::streamStateChangedStatic(pa_stream *stream, +PulseAudioPlaybackTarget::streamStateChangedStatic(pa_stream *stream, void *data) { - PulseAudioIO *target = (PulseAudioIO *)data; + PulseAudioPlaybackTarget *target = (PulseAudioPlaybackTarget *)data; - assert(stream == target->m_in || stream == target->m_out); + assert(stream == target->m_out); target->streamStateChanged(stream); } void -PulseAudioIO::streamStateChanged(pa_stream *stream) +PulseAudioPlaybackTarget::streamStateChanged(pa_stream *stream) { -#ifdef DEBUG_AUDIO_PULSE_AUDIO_IO - cerr << "PulseAudioIO::streamStateChanged" << endl; +#ifdef DEBUG_PULSE_AUDIO_PLAYBACK_TARGET + cerr << "PulseAudioPlaybackTarget::streamStateChanged" << endl; #endif - assert(stream == m_in || stream == m_out); + assert(stream == m_out); QMutexLocker locker(&m_mutex); @@ -393,18 +281,12 @@ case PA_STREAM_READY: { - if (stream == m_in) { - cerr << "PulseAudioIO::streamStateChanged: Capture ready" << endl; - m_captureReady = true; - } else { - cerr << "PulseAudioIO::streamStateChanged: Playback ready" << endl; - m_playbackReady = true; - } + m_playbackReady = true; pa_usec_t latency = 0; int negative = 0; if (pa_stream_get_latency(m_out, &latency, &negative)) { - cerr << "PulseAudioIO::contextStateChanged: Failed to query latency" << endl; + cerr << "PulseAudioPlaybackTarget::contextStateChanged: Failed to query latency" << endl; } cerr << "Latency = " << latency << " usec" << endl; int latframes = (latency / 1000000.f) * float(m_sampleRate); @@ -412,12 +294,12 @@ const pa_buffer_attr *attr; if (!(attr = pa_stream_get_buffer_attr(m_out))) { - cerr << "PulseAudioIO::streamStateChanged: Cannot query stream buffer attributes" << endl; + cerr << "PulseAudioPlaybackTarget::streamStateChanged: Cannot query stream buffer attributes" << endl; m_source->setSystemPlaybackBlockSize(4096); m_source->setSystemPlaybackSampleRate(m_sampleRate); m_source->setSystemPlaybackLatency(latframes); } else { - cerr << "PulseAudioIO::streamStateChanged: stream max length = " << attr->maxlength << endl; + cerr << "PulseAudioPlaybackTarget::streamStateChanged: stream max length = " << attr->maxlength << endl; int latency = attr->tlength; cerr << "latency = " << latency << endl; m_source->setSystemPlaybackBlockSize(attr->maxlength); @@ -425,15 +307,12 @@ m_source->setSystemPlaybackLatency(latframes); } - cerr << "PulseAudioIO: setting system record sample rate to " << m_sampleRate << endl; - m_target->setSystemRecordSampleRate(m_sampleRate); - break; } case PA_STREAM_FAILED: default: - cerr << "PulseAudioIO::streamStateChanged: Error: " + cerr << "PulseAudioPlaybackTarget::streamStateChanged: Error: " << pa_strerror(pa_context_errno(m_context)) << endl; //!!! do something... break; @@ -441,10 +320,10 @@ } void -PulseAudioIO::contextStateChangedStatic(pa_context *context, +PulseAudioPlaybackTarget::contextStateChangedStatic(pa_context *context, void *data) { - PulseAudioIO *target = (PulseAudioIO *)data; + PulseAudioPlaybackTarget *target = (PulseAudioPlaybackTarget *)data; assert(context == target->m_context); @@ -452,10 +331,10 @@ } void -PulseAudioIO::contextStateChanged() +PulseAudioPlaybackTarget::contextStateChanged() { -#ifdef DEBUG_AUDIO_PULSE_AUDIO_IO - cerr << "PulseAudioIO::contextStateChanged" << endl; +#ifdef DEBUG_PULSE_AUDIO_PLAYBACK_TARGET + cerr << "PulseAudioPlaybackTarget::contextStateChanged" << endl; #endif QMutexLocker locker(&m_mutex); @@ -468,24 +347,9 @@ case PA_CONTEXT_READY: { - cerr << "PulseAudioIO::contextStateChanged: Ready" + cerr << "PulseAudioPlaybackTarget::contextStateChanged: Ready" << endl; - m_in = pa_stream_new(m_context, "Turbot capture", &m_spec, 0); - assert(m_in); //!!! - - pa_stream_set_state_callback(m_in, streamStateChangedStatic, this); - pa_stream_set_read_callback(m_in, streamReadStatic, this); - pa_stream_set_overflow_callback(m_in, streamOverflowStatic, this); - pa_stream_set_underflow_callback(m_in, streamUnderflowStatic, this); - - if (pa_stream_connect_record - (m_in, 0, 0, - pa_stream_flags_t(PA_STREAM_INTERPOLATE_TIMING | - PA_STREAM_AUTO_TIMING_UPDATE))) { //??? return value - cerr << "PulseAudioIO: Failed to connect record stream" << endl; - } - m_out = pa_stream_new(m_context, "Turbot playback", &m_spec, 0); assert(m_out); //!!! @@ -499,20 +363,20 @@ pa_stream_flags_t(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE), 0, 0)) { //??? return value - cerr << "PulseAudioIO: Failed to connect playback stream" << endl; + cerr << "PulseAudioPlaybackTarget: Failed to connect playback stream" << endl; } break; } case PA_CONTEXT_TERMINATED: - cerr << "PulseAudioIO::contextStateChanged: Terminated" << endl; + cerr << "PulseAudioPlaybackTarget::contextStateChanged: Terminated" << endl; //!!! do something... break; case PA_CONTEXT_FAILED: default: - cerr << "PulseAudioIO::contextStateChanged: Error: " + cerr << "PulseAudioPlaybackTarget::contextStateChanged: Error: " << pa_strerror(pa_context_errno(m_context)) << endl; //!!! do something... break; @@ -520,15 +384,15 @@ } void -PulseAudioIO::streamOverflowStatic(pa_stream *, void *) +PulseAudioPlaybackTarget::streamOverflowStatic(pa_stream *, void *) { - cerr << "PulseAudioIO::streamOverflowStatic: Overflow!" << endl; + cerr << "PulseAudioPlaybackTarget::streamOverflowStatic: Overflow!" << endl; } void -PulseAudioIO::streamUnderflowStatic(pa_stream *, void *) +PulseAudioPlaybackTarget::streamUnderflowStatic(pa_stream *, void *) { - cerr << "PulseAudioIO::streamUnderflowStatic: Underflow!" << endl; + cerr << "PulseAudioPlaybackTarget::streamUnderflowStatic: Underflow!" << endl; } diff --git a/PulseAudioIO.h b/PulseAudioPlaybackTarget.h copy from PulseAudioIO.h copy to PulseAudioPlaybackTarget.h --- a/PulseAudioIO.h +++ b/PulseAudioPlaybackTarget.h @@ -1,14 +1,14 @@ /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Copyright Chris Cannam - All Rights Reserved */ -#ifndef _AUDIO_PULSE_AUDIO_IO_H_ -#define _AUDIO_PULSE_AUDIO_IO_H_ +#ifndef _AUDIO_PULSE_AUDIO_PLAYBACK_TARGET_H_ +#define _AUDIO_PULSE_AUDIO_PLAYBACK_TARGET_H_ #ifdef HAVE_LIBPULSE #include -#include "SystemAudioIO.h" +#include "SystemPlaybackTarget.h" #include "system/Thread.h" @@ -16,31 +16,24 @@ namespace Turbot { -class ApplicationRecordTarget; class ApplicationPlaybackSource; -class PulseAudioIO : public SystemAudioIO +class PulseAudioPlaybackTarget : public SystemPlaybackTarget { public: - PulseAudioIO(ApplicationRecordTarget *recordTarget, - ApplicationPlaybackSource *playSource); - virtual ~PulseAudioIO(); + PulseAudioPlaybackTarget(ApplicationPlaybackSource *playSource); + virtual ~PulseAudioPlaybackTarget(); - virtual bool isSourceOK() const; - virtual bool isSourceReady() const; virtual bool isTargetOK() const; - virtual bool isTargetReady() const; virtual double getCurrentTime() const; protected: void streamWrite(int); - void streamRead(int); void streamStateChanged(pa_stream *); void contextStateChanged(); static void streamWriteStatic(pa_stream *, size_t, void *); - static void streamReadStatic(pa_stream *, size_t, void *); static void streamStateChangedStatic(pa_stream *, void *); static void streamOverflowStatic(pa_stream *, void *); static void streamUnderflowStatic(pa_stream *, void *); @@ -64,7 +57,6 @@ pa_mainloop *m_loop; pa_mainloop_api *m_api; pa_context *m_context; - pa_stream *m_in; pa_stream *m_out; pa_sample_spec m_spec; @@ -72,9 +64,9 @@ int m_bufferSize; int m_sampleRate; + int m_paChannels; bool m_done; - bool m_captureReady; bool m_playbackReady; };