# HG changeset patch # User Chris Cannam # Date 1730282804 0 # Wed Oct 30 10:06:44 2024 +0000 # Node ID 017ab3ed3a33e0d71259a4ab8da3b265cde28ffb # Parent 88520ab5b8e5d18053123cc82798ae60f4634c55 # Parent 9bdee315ebcdcfd3d42874fa5ebf2a896daa123d Merge from branch toggle-record-in-io diff --git a/bqaudioio/SystemAudioIO.h b/bqaudioio/SystemAudioIO.h --- a/bqaudioio/SystemAudioIO.h +++ b/bqaudioio/SystemAudioIO.h @@ -71,6 +71,17 @@ */ bool isReady() const { return isSourceReady() && isTargetReady(); } + /** + * Suppress or re-enable recording. By default both recording and + * playback are enabled, and an active stream will be duplex. A + * call to suppressRecordSide(true) may be used to hint that the + * record side should remain closed until any following call to + * suppressRecordSide(false). These calls should be made while the + * stream is suspended. This is only a hint; not all + * implementations will implement it. + */ + virtual void suppressRecordSide(bool suppress) = 0; + protected: SystemAudioIO(ApplicationRecordTarget *target, ApplicationPlaybackSource *source) : diff --git a/src/DynamicJACK.h b/src/DynamicJACK.h --- a/src/DynamicJACK.h +++ b/src/DynamicJACK.h @@ -208,6 +208,15 @@ return f(client, port); } +static void dynamic_jack_free(void *ptr) +{ + typedef void(*func)(void *); + void *s = symbol("jack_free"); + if (!s) return; + func f = (func)s; + f(ptr); +} + #define dynamic1(rv, name, argtype, failval) \ static rv dynamic_##name(argtype arg) { \ typedef rv (*func) (argtype); \ @@ -245,6 +254,7 @@ #define jack_port_get_buffer dynamic_jack_port_get_buffer #define jack_port_get_latency_range dynamic_jack_port_get_latency_range #define jack_frame_time dynamic_jack_frame_time +#define jack_free dynamic_jack_free } diff --git a/src/JACKAudioIO.cpp b/src/JACKAudioIO.cpp --- a/src/JACKAudioIO.cpp +++ b/src/JACKAudioIO.cpp @@ -336,6 +336,13 @@ if (m_target) { m_target->setSystemRecordChannelCount(channelsRec); } + + if (playPorts) { + jack_free(playPorts); + } + if (capPorts) { + jack_free(capPorts); + } } int diff --git a/src/JACKAudioIO.h b/src/JACKAudioIO.h --- a/src/JACKAudioIO.h +++ b/src/JACKAudioIO.h @@ -66,6 +66,8 @@ void suspend() override {} void resume() override {} + void suppressRecordSide(bool) override {} + double getCurrentTime() const override; std::string getStartupErrorString() const { return m_startupError; } diff --git a/src/PortAudioIO.cpp b/src/PortAudioIO.cpp --- a/src/PortAudioIO.cpp +++ b/src/PortAudioIO.cpp @@ -241,7 +241,9 @@ string recordDevice, string playbackDevice) : SystemAudioIO(target, source), - m_stream(0), + m_stream(nullptr), + m_recordDevice(0), + m_playbackDevice(0), m_mode(mode), m_bufferSize(0), m_sampleRate(0), @@ -249,7 +251,8 @@ m_outputLatency(0), m_prioritySet(false), m_suspended(false), - m_buffers(0), + m_recordEnabled(true), + m_buffers(nullptr), m_bufferChannels(0) { log("starting"); @@ -267,23 +270,18 @@ m_source = 0; } - PaError err; - - m_bufferSize = 0; - - PaStreamParameters ip, op; - ip.device = getDeviceIndex(recordDevice, true); - op.device = getDeviceIndex(playbackDevice, false); + m_recordDevice = getDeviceIndex(recordDevice, true); + m_playbackDevice = getDeviceIndex(playbackDevice, false); { ostringstream os; - os << "Obtained playback device index " << op.device - << " and record device index " << ip.device; + os << "Obtained playback device index " << m_playbackDevice + << " and record device index " << m_recordDevice; log(os.str()); } - const PaDeviceInfo *inInfo = Pa_GetDeviceInfo(ip.device); - const PaDeviceInfo *outInfo = Pa_GetDeviceInfo(op.device); + const PaDeviceInfo *inInfo = Pa_GetDeviceInfo(m_recordDevice); + const PaDeviceInfo *outInfo = Pa_GetDeviceInfo(m_playbackDevice); if (outInfo) { m_sampleRate = outInfo->defaultSampleRate; } @@ -335,54 +333,14 @@ outInfo->maxOutputChannels > 0) { m_outputChannels = outInfo->maxOutputChannels; } - - ip.channelCount = m_inputChannels; - op.channelCount = m_outputChannels; - ip.sampleFormat = paFloat32; - op.sampleFormat = paFloat32; - ip.suggestedLatency = 0.2; - op.suggestedLatency = 0.2; - ip.hostApiSpecificStreamInfo = 0; - op.hostApiSpecificStreamInfo = 0; - m_bufferSize = 0; - err = openStream(m_mode, &m_stream, &ip, &op, m_sampleRate, - paFramesPerBufferUnspecified, this); - - if (err != paNoError) { - m_bufferSize = 1024; - err = openStream(m_mode, &m_stream, &ip, &op, m_sampleRate, - 1024, this); - } - - if (err != paNoError) { - if (m_inputChannels != 2 || m_outputChannels != 2) { - - log(string("WARNING: Failed to open PortAudio stream: ") + - Pa_GetErrorText(err) + ": trying again with 2x2 configuration"); - - m_inputChannels = 2; - m_outputChannels = 2; - ip.channelCount = m_inputChannels; - op.channelCount = m_outputChannels; - - m_bufferSize = 0; - err = openStream(m_mode, &m_stream, &ip, &op, m_sampleRate, - paFramesPerBufferUnspecified, this); - - m_bufferSize = 1024; - if (err != paNoError) { - err = openStream(m_mode, &m_stream, &ip, &op, m_sampleRate, - 1024, this); - } - } - } + PaError err = openStream(); if (err != paNoError) { m_startupError = "Failed to open PortAudio stream: "; m_startupError += Pa_GetErrorText(err); log("ERROR: " + m_startupError); - m_stream = 0; + m_stream = nullptr; deinitialise(); return; } @@ -431,7 +389,7 @@ m_startupError += Pa_GetErrorText(err); log("ERROR: " + m_startupError); Pa_CloseStream(m_stream); - m_stream = 0; + m_stream = nullptr; deinitialise(); return; } @@ -451,8 +409,81 @@ PortAudioIO::~PortAudioIO() { + auto err = closeStream(); + if (err != paNoError) { + log("ERROR: Failed to close PortAudio stream"); + } + + deallocate_channels(m_buffers, m_bufferChannels); + deinitialise(); + log("closed"); +} + +PaError +PortAudioIO::openStream() +{ + PaError err = paNoError; + PaStreamParameters ip, op; + + ip.device = m_recordDevice; + op.device = m_playbackDevice; + ip.channelCount = m_inputChannels; + op.channelCount = m_outputChannels; + ip.sampleFormat = paFloat32; + op.sampleFormat = paFloat32; + ip.suggestedLatency = 0.2; + op.suggestedLatency = 0.2; + ip.hostApiSpecificStreamInfo = 0; + op.hostApiSpecificStreamInfo = 0; + + Mode activeMode = m_mode; + if (m_mode == Mode::Duplex && !m_recordEnabled) { + activeMode = Mode::Playback; + } + + m_bufferSize = 0; + err = openStreamStatic + (activeMode, &m_stream, &ip, &op, m_sampleRate, + paFramesPerBufferUnspecified, this); + + if (err != paNoError) { + m_bufferSize = 1024; + err = openStreamStatic + (activeMode, &m_stream, &ip, &op, m_sampleRate, 1024, this); + } + + if (err != paNoError) { + if (m_inputChannels != 2 || m_outputChannels != 2) { + + log(string("WARNING: Failed to open PortAudio stream: ") + + Pa_GetErrorText(err) + ": trying again with 2x2 configuration"); + + m_inputChannels = 2; + m_outputChannels = 2; + ip.channelCount = m_inputChannels; + op.channelCount = m_outputChannels; + + m_bufferSize = 0; + err = openStreamStatic + (activeMode, &m_stream, &ip, &op, m_sampleRate, + paFramesPerBufferUnspecified, this); + + m_bufferSize = 1024; + if (err != paNoError) { + err = openStreamStatic + (activeMode, &m_stream, &ip, &op, m_sampleRate, 1024, this); + } + } + } + + return err; +} + +PaError +PortAudioIO::closeStream() +{ + PaError err = paNoError; if (m_stream) { - PaError err; if (!m_suspended) { err = Pa_StopStream(m_stream); if (err != paNoError) { @@ -467,20 +498,44 @@ if (err != paNoError) { log("ERROR: Failed to close PortAudio stream"); } - deallocate_channels(m_buffers, m_bufferChannels); - deinitialise(); - log("closed"); + m_stream = nullptr; + } + return err; +} + +void +PortAudioIO::suppressRecordSide(bool suppress) +{ + bool enabled = !suppress; + if (enabled == m_recordEnabled) { + return; } + bool wasSuspended = m_suspended; + PaError err = paNoError; + if (m_stream) { + err = closeStream(); + if (err != paNoError) { + log("ERROR: Failed to close PortAudio stream in order to change record mode"); + } + m_recordEnabled = enabled; + err = openStream(); + if (err != paNoError) { + log("ERROR: Failed to reopen PortAudio stream in order to change record mode"); + } + if (!wasSuspended) { + resume(); + } + } } PaError -PortAudioIO::openStream(Mode mode, - PaStream **stream, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - void *data) +PortAudioIO::openStreamStatic(Mode mode, + PaStream **stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + void *data) { switch (mode) { case Mode::Playback: diff --git a/src/PortAudioIO.h b/src/PortAudioIO.h --- a/src/PortAudioIO.h +++ b/src/PortAudioIO.h @@ -70,6 +70,8 @@ virtual void suspend() override; virtual void resume() override; + virtual void suppressRecordSide(bool) override; + std::string getStartupErrorString() const { return m_startupError; } protected: @@ -77,10 +79,13 @@ const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags); - static PaError openStream(Mode, PaStream **, - const PaStreamParameters *, - const PaStreamParameters *, - double, unsigned long, void *); + PaError openStream(); + PaError closeStream(); + + static PaError openStreamStatic(Mode, PaStream **, + const PaStreamParameters *, + const PaStreamParameters *, + double, unsigned long, void *); static int processStatic(const void *, void *, unsigned long, const PaStreamCallbackTimeInfo *, @@ -88,6 +93,9 @@ PaStream *m_stream; + PaDeviceIndex m_recordDevice; + PaDeviceIndex m_playbackDevice; + Mode m_mode; int m_bufferSize; double m_sampleRate; @@ -99,6 +107,7 @@ int m_outputLatency; bool m_prioritySet; bool m_suspended; + bool m_recordEnabled; float **m_buffers; int m_bufferChannels; std::string m_startupError; diff --git a/src/PulseAudioIO.h b/src/PulseAudioIO.h --- a/src/PulseAudioIO.h +++ b/src/PulseAudioIO.h @@ -76,6 +76,8 @@ void suspend() override; void resume() override; + void suppressRecordSide(bool) override {} + std::string getStartupErrorString() const { return m_startupError; } protected: