Skip to content

Commit

Permalink
BUGFIX: single thread approach (#153)
Browse files Browse the repository at this point in the history
* BUGFIX: single thread approach

A really old edit pkv made to switch from multithreading to single threading created a few bugs because each thread originally maintained it's own client list and each "device" assumed it had a unique thread.

This should be a quick and dirty fix where each device now maintains its own client list, a bit redundant given the one in the global thread.

Hopefully this also resolves #147, I'm assuming the crash was the result of the error handler originally stopping its own thread (the bug being now it stops the only thread).

* Define _clients after struct definition

* use _clients
  • Loading branch information
Andersama authored May 21, 2024
1 parent 84c4e37 commit 5cfae78
Showing 1 changed file with 64 additions and 5 deletions.
69 changes: 64 additions & 5 deletions src/asio-input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <obs-module.h>
#include <obs-frontend-api.h>
#include <vector>
#include <algorithm>
//#include <JuceHeader.h>
#include <juce_core/juce_core.h>
#include <juce_audio_devices/juce_audio_devices.h>
Expand Down Expand Up @@ -159,12 +160,14 @@ class AudioCB : public juce::AudioIODeviceCallback {
public:
class AudioListener : public TimeSliceClient {
private:
static constexpr unsigned int client_mask = 0x1;
static constexpr unsigned int device_mask = 0x2;
std::vector<short> _route;
std::vector<short> _route_out;
obs_source_audio in;
obs_source_t *source;

bool active;
unsigned int active;
int read_index = 0;
int wait_time = 4;
AudioCB *callback;
Expand Down Expand Up @@ -205,7 +208,7 @@ class AudioCB : public juce::AudioIODeviceCallback {
public:
AudioListener(obs_source_t *source, AudioCB *cb) : source(source), callback(cb)
{
active = true;
active = client_mask | device_mask;
}

~AudioListener()
Expand All @@ -215,12 +218,20 @@ class AudioCB : public juce::AudioIODeviceCallback {

void disconnect()
{
active = false;
active &= device_mask;
}

void reconnect()
{
active = true;
active |= client_mask;
}

void device_disconnect() {
active &= client_mask;
}

void device_reconnect() {
active |= device_mask;
}

void setOutput(obs_source_audio o)
Expand Down Expand Up @@ -257,7 +268,7 @@ class AudioCB : public juce::AudioIODeviceCallback {

int useTimeSlice()
{
if (!active || callback != current_callback)
if ((active & client_mask) == 0 || (active & device_mask) == 0 || callback != current_callback)
return -1;
int write_index = callback->write_index();
if (read_index == write_index)
Expand All @@ -281,6 +292,9 @@ class AudioCB : public juce::AudioIODeviceCallback {
return wait_time;
}
};
private:
std::vector<AudioListener *> _clients;
public:

AudioIODevice *getDevice()
{
Expand Down Expand Up @@ -333,13 +347,27 @@ class AudioCB : public juce::AudioIODeviceCallback {
if (!_thread)
_thread = global_thread;

bool found_client = false;
for (auto known_client : _clients) {
if (known_client == client) {
found_client = true;
break;
}
}

if (!found_client)
_clients.push_back(client);

client->setCurrentCallback(this);
client->setReadIndex(_write_index);
_thread->addTimeSliceClient(client);
}

void remove_client(AudioListener *client)
{
auto it = std::remove(_clients.begin(), _clients.end(), client);
_clients.erase(it, _clients.end());

if (_thread)
_thread->removeTimeSliceClient(client);
}
Expand Down Expand Up @@ -383,17 +411,37 @@ class AudioCB : public juce::AudioIODeviceCallback {
if (!_thread) {
_thread = global_thread;
} else {
/*
// update clients with this device to use this callback
// BUGFIX: when pkv modified the source code to use one global thread
// this became bugged, originally
for (int i = 0; i < _thread->getNumClients(); i++) {
AudioListener *l = static_cast<AudioListener *>(_thread->getClient(i));
// this is one of this device's known clients
l->setCurrentCallback(this);
}
// was ok because _thread was unique to each AudioCB (the this pointer)
// I'm adding a redundant struct to keep track of the clients inside the AudioCB
*/
for (auto known_client : _clients) {
known_client->setCurrentCallback(this);
// if the device for whatever reason stopped, or errored out
// the clients will have been disabled by the device disconnecting
// since we're here, we can mark the device as being ok
known_client->device_reconnect();
}

}
if (!_thread->isThreadRunning())
_thread->startThread(10);
}

void audioDeviceStopped()
{
for (auto known_client : _clients) {
known_client->device_disconnect();
}

blog(LOG_INFO, "Stopped (%s)", _device->getName().toStdString().c_str());

std::string timestamp_string = std::to_string(last_audio_ts);
Expand All @@ -403,8 +451,19 @@ class AudioCB : public juce::AudioIODeviceCallback {

void audioDeviceError(const juce::String &errorMessage)
{
/* BUGFIX: when pkv modified the source code to use one global thread
// this became bugged, originally
if (_thread)
_thread->stopThread(200);
// was ok because _thread was unique to each AudioCB (the this pointer)
// now any device that sends an error effectively nukes all other processing
// which likely will cause problems
*/
// Instead we'll mark all clients as having the device disconnected
for (auto known_client : _clients) {
known_client->device_disconnect();
}

std::string error = errorMessage.toStdString();
blog(LOG_ERROR, "Device Error!\n%s", error.c_str());

Expand Down

0 comments on commit 5cfae78

Please sign in to comment.