Skip to content

Commit

Permalink
bug: invalid mono audio device channel selects
Browse files Browse the repository at this point in the history
  • Loading branch information
rerdavies committed Oct 12, 2024
1 parent 21f5c9e commit b0107d4
Show file tree
Hide file tree
Showing 17 changed files with 163 additions and 68 deletions.
25 changes: 21 additions & 4 deletions react/src/Jack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
import {PiPedalArgumentError} from './PiPedalError';
import {AlsaMidiDeviceInfo} from './AlsaMidiDeviceInfo';

function getChannelNumber(channelId: string): number {
let pos = channelId.indexOf("_");
let i = Number(channelId.substring(pos+1));
return i;
}

export class JackChannelSelection {
deserialize(input: any): JackChannelSelection
{
Expand Down Expand Up @@ -57,7 +63,8 @@ export class JackChannelSelection {
return "Stereo";
}
if (selectedChannels.length === 1) return "Mono";
return "Invalid selection";

return "\u00A0"; // nbsp

}
if (selectedChannels.length === 0) return "Invalid selection";
Expand All @@ -69,12 +76,22 @@ export class JackChannelSelection {
} else if (selectedChannels[0] === availableChannels[1]) {
return "Mono (right channel only)";
} else {
return "\u00A0"; // nbsp
throw new PiPedalArgumentError("Invalid channel selection."); // should be subset of jackConfiguration.
}
} else {
if (selectedChannels.length === 2) return "Stereo (custom selection)";
if (selectedChannels.length === 1) return "Mono (custom selection)";
throw new PiPedalArgumentError("Invalid channel selection."); // should be subset of jackConfiguration.
if (selectedChannels.length === 2)
{
let result: string = "Stereo (" + (getChannelNumber(selectedChannels[0])+1) + "," + (getChannelNumber(selectedChannels[1])+1) + ")";
return result;
}
if (selectedChannels.length === 1)
{
let result: string = "Mono (" + (getChannelNumber(selectedChannels[0])+1) +")";
return result;

}
return "\u00A0"; // nbsp
}
}
getAudioInputDisplayValue(jackConfiguration: JackConfiguration): string
Expand Down
26 changes: 19 additions & 7 deletions react/src/SelectChannelsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,22 @@ function removePort(selectedChannels: string[], channel: string) {
return result;
}

function makeChannelName(id: string)
{
let pos = id.lastIndexOf("_");
if (pos === -1)
{
return id;
}
let i = Number(id.substring(pos+1));
return "Channel " + (i+1);
}
function SelectChannelsDialog(props: SelectChannelsDialogProps) {
//const classes = useStyles();
const { onClose, selectedChannels, availableChannels, open } = props;
const [currentSelection, setCurrentSelection] = useState(selectedChannels);

let showCheckboxes = availableChannels.length > 2;
let showCheckboxes = availableChannels.length !== 2;
if (showCheckboxes) {

let toggleSelect = (value: string) => {
Expand All @@ -85,23 +95,25 @@ function SelectChannelsDialog(props: SelectChannelsDialogProps) {
onClose(null);
};
const handleOk = (): void => {
onClose(currentSelection);
if (currentSelection.length >= 1 && currentSelection.length <= 2)
{
onClose(currentSelection);
}
};


return (
<DialogEx tag="audioChannels" onClose={handleClose} aria-labelledby="select-channels-title" open={open}
<DialogEx tag="audioChannels" onClose={handleClose} aria-labelledby="select-channels-title" open={open} fullWidth maxWidth="xs"
onEnterKey={handleOk}
>
<DialogTitle id="simple-dialog-title">Select Channels</DialogTitle>
<List>
{availableChannels.map((channel) => (
<ListItem button >
<ListItem button key={channel}>
<FormControlLabel
control={
<Checkbox checked={isChecked(currentSelection, channel)} onClick={() => toggleSelect(channel)} key={channel} />
<Checkbox checked={isChecked(currentSelection, channel)} onClick={() => toggleSelect(channel)} style={{marginLeft: 24}} />
}
label={channel}
label={makeChannelName(channel)}
/>
</ListItem>
)
Expand Down
6 changes: 4 additions & 2 deletions react/src/SettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(



<ButtonBase className={classes.setting} onClick={() => this.handleInputSelection()} disabled={!isConfigValid}
<ButtonBase className={classes.setting} onClick={() => this.handleInputSelection()}
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}

>
Expand All @@ -661,7 +662,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
<Typography display="block" variant="caption" color="textSecondary" noWrap>{this.state.jackSettings.getAudioInputDisplayValue(this.state.jackConfiguration)}</Typography>
</div>
</ButtonBase>
<ButtonBase className={classes.setting} onClick={() => this.handleOutputSelection()} disabled={!isConfigValid}
<ButtonBase className={classes.setting} onClick={() => this.handleOutputSelection()}
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}
>
<SelectHoverBackground selected={false} showHover={true} />
Expand Down
13 changes: 8 additions & 5 deletions src/AlsaDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <thread>
#include "RtInversionGuard.hpp"
#include "PiPedalException.hpp"
#include "DummyAudioDriver.hpp"

#include "CpuUse.hpp"

Expand Down Expand Up @@ -2022,13 +2023,15 @@ namespace pipedal
{
if (jackServerSettings.IsDummyAudioDevice())
{
auto nChannels = GetDummyAudioChannels(jackServerSettings.GetAlsaInputDevice());

inputAudioPorts.clear();
inputAudioPorts.push_back(std::string("system::capture_0"));
inputAudioPorts.push_back(std::string("system::capture_1"));
outputAudioPorts.clear();
outputAudioPorts.push_back(std::string("system::playback_0"));
outputAudioPorts.push_back(std::string("system::playback_1"));

for (uint32_t i = 0; i < nChannels; ++i)
{
inputAudioPorts.push_back(std::string(SS("system::capture_" << i)));
outputAudioPorts.push_back(std::string(SS("system::playback_" << i)));
}
return true;
}

Expand Down
6 changes: 3 additions & 3 deletions src/AlsaDriverTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ class AlsaTester: private AudioDriverHost {
}

JackChannelSelection channelSelection(
jackConfiguration.GetInputAudioPorts(),
jackConfiguration.GetOutputAudioPorts(),
jackConfiguration.GetInputMidiDevices());
jackConfiguration.inputAudioPorts(),
jackConfiguration.outputAudioPorts(),
jackConfiguration.inputMidiDevices());

#if JACK_HOST
if (useJack)
Expand Down
2 changes: 1 addition & 1 deletion src/AudioHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1636,7 +1636,7 @@ class AudioHostImpl : public AudioHost, private AudioDriverHost, private IPatchW
if (jackServerSettings.IsDummyAudioDevice())
{
this->isDummyAudioDriver = true;
this->audioDriver = std::unique_ptr<AudioDriver>(CreateDummyAudioDriver(this));
this->audioDriver = std::unique_ptr<AudioDriver>(CreateDummyAudioDriver(this,jackServerSettings.GetAlsaInputDevice()));
}
else
{
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ add_executable(pipedal_latency_test
PiPedalAlsa.hpp PiPedalAlsa.cpp
asan_options.cpp
AlsaDriver.cpp AlsaDriver.hpp
DummyAudioDriver.cpp DummyAudioDriver.hpp
JackConfiguration.hpp JackConfiguration.cpp
JackServerSettings.hpp JackServerSettings.cpp
CpuUse.hpp
Expand Down
56 changes: 48 additions & 8 deletions src/DummyAudioDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#include <atomic>
#include <chrono>
#include <thread>
#include <stdexcept>
#include "ss.hpp"

#include "CpuUse.hpp"

Expand Down Expand Up @@ -91,11 +93,16 @@ namespace pipedal
uint8_t *rawPlaybackBuffer = nullptr;

AudioDriverHost *driverHost = nullptr;
uint32_t channels = 2;

public:
DummyDriverImpl(AudioDriverHost *driverHost)
DummyDriverImpl(AudioDriverHost *driverHost,const std::string&deviceName)
: driverHost(driverHost)
, channels(GetDummyAudioChannels(deviceName))
{
captureChannels = channels;
playbackChannels = channels;

}
virtual ~DummyDriverImpl()
{
Expand Down Expand Up @@ -201,8 +208,8 @@ namespace pipedal

this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers();
this->bufferSize = jackServerSettings.GetBufferSize();
AllocateBuffers(captureBuffers, 2);
AllocateBuffers(playbackBuffers, 2);
AllocateBuffers(captureBuffers, channels);
AllocateBuffers(playbackBuffers, channels);

}

Expand Down Expand Up @@ -299,7 +306,7 @@ namespace pipedal

this->activeCaptureBuffers.resize(channelSelection.GetInputAudioPorts().size());

playbackBuffers.resize(2);
playbackBuffers.resize(channels);

int ix = 0;
for (auto &x : channelSelection.GetInputAudioPorts())
Expand Down Expand Up @@ -419,22 +426,23 @@ namespace pipedal
}
};

AudioDriver *CreateDummyAudioDriver(AudioDriverHost *driverHost)
AudioDriver *CreateDummyAudioDriver(AudioDriverHost *driverHost,const std::string&deviceName)
{
return new DummyDriverImpl(driverHost);
return new DummyDriverImpl(driverHost,deviceName);
}

bool GetDummyChannels(const JackServerSettings &jackServerSettings,
std::vector<std::string> &inputAudioPorts,
std::vector<std::string> &outputAudioPorts)
std::vector<std::string> &outputAudioPorts,
uint32_t channels)
{

bool result = false;

try
{

unsigned int playbackChannels = 2, captureChannels = 2;
uint32_t playbackChannels = channels, captureChannels = channels;

inputAudioPorts.clear();
for (unsigned int i = 0; i < captureChannels; ++i)
Expand All @@ -457,4 +465,36 @@ namespace pipedal
return result;
}



AlsaDeviceInfo MakeDummyDeviceInfo(uint32_t channels)
{
AlsaDeviceInfo result;
constexpr int DUMMY_DEVICE_ID_OFFSET = 100974;
result.cardId_ = DUMMY_DEVICE_ID_OFFSET+channels;
result.id_ = SS("dummy:channels_" << channels);
result.name_ = SS("Dummy Device (" << channels << " channels)");
result.longName_ = result.name_;
result.sampleRates_.push_back(44100);
result.sampleRates_.push_back(48000);
result.minBufferSize_ = 16;
result.maxBufferSize_ = 1024;
return result;
}

} // namespace

uint32_t pipedal::GetDummyAudioChannels(const std::string &deviceName)
{
uint32_t channels;
int pos = deviceName.find_last_of('_');
if (pos == std::string::npos)
{
throw std::runtime_error("Invalid dummy device name");
}
std::istringstream ss(deviceName.substr(pos+1));
ss >> channels;
return channels;

}

5 changes: 4 additions & 1 deletion src/DummyAudioDriver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@

namespace pipedal {

AudioDriver* CreateDummyAudioDriver(AudioDriverHost*driverHost);
AlsaDeviceInfo MakeDummyDeviceInfo(uint32_t channels);

uint32_t GetDummyAudioChannels(const std::string &deviceName);
AudioDriver* CreateDummyAudioDriver(AudioDriverHost*driverHost,const std::string&deviceId);

}

24 changes: 17 additions & 7 deletions src/JackConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,14 @@ void JackConfiguration::JackInitialize()

JackChannelSelection JackChannelSelection::MakeDefault(const JackConfiguration&config){
JackChannelSelection result;
for (size_t i = 0; i < std::min((size_t)2,config.GetInputAudioPorts().size()); ++i)
for (size_t i = 0; i < std::min((size_t)2,config.inputAudioPorts().size()); ++i)
{
result.inputAudioPorts_.push_back(config.GetInputAudioPorts()[i]);
result.inputAudioPorts_.push_back(config.inputAudioPorts()[i]);

}
for (size_t i = 0; i < std::min((size_t)2,config.GetOutputAudioPorts().size()); ++i)
for (size_t i = 0; i < std::min((size_t)2,config.outputAudioPorts().size()); ++i)
{
result.outputAudioPorts_.push_back(config.GetOutputAudioPorts()[i]);
result.outputAudioPorts_.push_back(config.outputAudioPorts()[i]);
}
return result;

Expand All @@ -232,6 +232,16 @@ static std::vector<std::string> makeValid(const std::vector<std::string> & selec
result.push_back(t);
}
}

// if the result is empty, generate a default selection.
if (result.size() == 0)
{
size_t n = std::min(available.size(),(size_t)2);
for (size_t i = 0; i < n; ++i)
{
result.push_back(available[i]);
}
}
return result;
}
static std::vector<AlsaMidiDeviceInfo> makeValid(const std::vector<AlsaMidiDeviceInfo> & selected, const std::vector<AlsaMidiDeviceInfo> &available)
Expand Down Expand Up @@ -284,9 +294,9 @@ static std::vector<AlsaMidiDeviceInfo> makeValid(const std::vector<AlsaMidiDevic
JackChannelSelection JackChannelSelection::RemoveInvalidChannels(const JackConfiguration&config) const
{
JackChannelSelection result;
result.inputAudioPorts_ = makeValid(this->inputAudioPorts_,config.GetInputAudioPorts());
result.outputAudioPorts_ = makeValid(this->outputAudioPorts_,config.GetOutputAudioPorts());
result.inputMidiDevices_ = makeValid(this->inputMidiDevices_, config.GetInputMidiDevices());
result.inputAudioPorts_ = makeValid(this->inputAudioPorts_,config.inputAudioPorts());
result.outputAudioPorts_ = makeValid(this->outputAudioPorts_,config.outputAudioPorts());
result.inputMidiDevices_ = makeValid(this->inputMidiDevices_, config.inputMidiDevices());
if (!result.isValid())
{
return this->MakeDefault(config);
Expand Down
10 changes: 5 additions & 5 deletions src/JackConfiguration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ namespace pipedal
public:
JackConfiguration();

void AlsaInitialize(const JackServerSettings &jackServerSettings); // from also config settings.

void AlsaInitialize(const JackServerSettings &jackServerSettings); // from alsa config settings.
void JackInitialize(); // from jack server instance.
~JackConfiguration();
bool isValid() const { return isValid_;}
Expand All @@ -67,9 +67,9 @@ namespace pipedal
double maxAllowedMidiDelta() const { return maxAllowedMidiDelta_; }
void setErrorStatus(const std::string&message) { this->errorStatus_ = message; }

const std::vector<std::string> &GetInputAudioPorts() const { return inputAudioPorts_; }
const std::vector<std::string> &GetOutputAudioPorts() const { return outputAudioPorts_; }
const std::vector<AlsaMidiDeviceInfo> &GetInputMidiDevices() const { return inputMidiDevices_; }
const std::vector<std::string> &inputAudioPorts() const { return inputAudioPorts_; }
const std::vector<std::string> &outputAudioPorts() const { return outputAudioPorts_; }
const std::vector<AlsaMidiDeviceInfo> &inputMidiDevices() const { return inputMidiDevices_; }

DECLARE_JSON_MAP(JackConfiguration);

Expand Down
6 changes: 4 additions & 2 deletions src/JackServerSettings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ namespace pipedal
void UseDummyAudioDevice() {
this->valid_ = true;
if (sampleRate_ == 0) sampleRate_ = 48000;
this->alsaDevice_ = "__DUMMY_AUDIO__";
this->alsaDevice_ = "dummy:channels_2";
}
bool IsDummyAudioDevice() const {
return this->alsaDevice_ == "__DUMMY_AUDIO__";
return
this->alsaDevice_.starts_with("__DUMMY_AUDIO__")
|| this->alsaDevice_.starts_with("dummy:");
}

void ReadJackDaemonConfiguration();
Expand Down
Loading

0 comments on commit b0107d4

Please sign in to comment.