Merge pull request #360 from extremesecrecy/SplitIO to rerdavies:splitio_base branch.
Thank you again. This functionality will make a lot of people happy.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
wpa_cli -ip2p-dev-wlan0 GET device_name pipedal
|
wpa_cli -ip2p-dev-wlan0 GET device_name pipedal
|
||||||
wpa_cli -ip2p-dev-wlan0 GET country CA
|
wpa_cli -ip2p-dev-wlan0 GET country CA
|
||||||
wpa_cli -ip2p-dev-wlan0 GET device_type 1-0050F204-1
|
wpa_cli -ip2p-dev-wlan0 GET device_type 1-0050F204-1
|
||||||
|
|||||||
@@ -57,20 +57,21 @@ https://github.com/user-attachments/assets/9a9fd0c6-78fc-4284-8b44-6a1929c00cc6
|
|||||||
### [Command-Line Configuration of PiPedal](https://rerdavies.github.io/pipedal/CommandLine.html)
|
### [Command-Line Configuration of PiPedal](https://rerdavies.github.io/pipedal/CommandLine.html)
|
||||||
### [Changing the Web Server Port](https://rerdavies.github.io/pipedal/ChangingTheWebServerPort.html)
|
### [Changing the Web Server Port](https://rerdavies.github.io/pipedal/ChangingTheWebServerPort.html)
|
||||||
|
|
||||||
%nbsp;
|
|
||||||
|
|
||||||
### [Using LV2 Audio Plugins](https://rerdavies.github.io/pipedal/UsingLv2Plugins.md)
|
### [Using LV2 Audio Plugins](https://rerdavies.github.io/pipedal/UsingLv2Plugins.md)
|
||||||
### [Which LV2 Plugins does PiPedal support?](https://rerdavies.github.io/pipedal/WhichLv2PluginsAreSupported.html)
|
### [Which LV2 Plugins does PiPedal support?](https://rerdavies.github.io/pipedal/WhichLv2PluginsAreSupported.html)
|
||||||
### [Support for LV2 Plugins with MOD User Interfaces](https://rerdavies.github.io/pipedal/ModUiSupport.html)
|
### [Support for LV2 Plugins with MOD User Interfaces](https://rerdavies.github.io/pipedal/ModUiSupport.html)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### [Building PiPedal from Source](https://rerdavies.github.io/pipedal/BuildingPiPedalFromSource.html)
|
### [Building PiPedal from Source](https://rerdavies.github.io/pipedal/BuildingPiPedalFromSource.html)
|
||||||
### [Build Prerequisites](https://rerdavies.github.io/pipedal/BuildPrerequisites.html)
|
### [Build Prerequisites](https://rerdavies.github.io/pipedal/BuildPrerequisites.html)
|
||||||
### [The Build System](https://rerdavies.github.io/pipedal/TheBuildSystem.html)
|
### [The Build System](https://rerdavies.github.io/pipedal/TheBuildSystem.html)
|
||||||
|
|
||||||
### [How to Debug PiPedal](https://rerdavies.github.io/pipedal/Debugging.html)
|
### [How to Debug PiPedal](https://rerdavies.github.io/pipedal/Debugging.html)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### [PiPedal Architecture](https://rerdavies.github.io/pipedal/Architecture.html)
|
#### [PiPedal Architecture](https://rerdavies.github.io/pipedal/Architecture.html)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -6,4 +6,4 @@
|
|||||||
# bundle install
|
# bundle install
|
||||||
#
|
#
|
||||||
cd docs
|
cd docs
|
||||||
bundle exec jekyll serve --host
|
bundle exec jekyll serve --host 0.0.0.0
|
||||||
@@ -21,13 +21,13 @@ Run the following commands to install dependent libraries required by the PiPeda
|
|||||||
|
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt upgrade
|
sudo apt upgrade
|
||||||
|
|
||||||
sudo apt install -y liblilv-dev libboost-dev \
|
sudo apt install -y liblilv-dev libboost-dev \
|
||||||
libsystemd-dev catch libasound2-dev uuid-dev \
|
libsystemd-dev catch libasound2-dev uuid-dev \
|
||||||
authbind libavahi-client-dev libnm-dev libicu-dev \
|
authbind libavahi-client-dev libnm-dev libicu-dev \
|
||||||
libsdbus-c++-dev libzip-dev google-perftools \
|
libsdbus-c++-dev libzip-dev google-perftools \
|
||||||
libgoogle-perftools-dev \
|
libgoogle-perftools-dev \
|
||||||
libpipewire-0.3-dev
|
libpipewire-0.3-dev
|
||||||
|
|
||||||
|
|
||||||
### Installing Sources
|
### Installing Sources
|
||||||
|
|
||||||
|
|||||||
@@ -13,15 +13,16 @@ available through the Code plugins store) has configured itself, build commands
|
|||||||
If you are not using Visual Studio Code, you can configure, build and install PiPedal using CMake build tools. For your convenience,
|
If you are not using Visual Studio Code, you can configure, build and install PiPedal using CMake build tools. For your convenience,
|
||||||
the following shell scripts have been provided in the root of the project.
|
the following shell scripts have been provided in the root of the project.
|
||||||
|
|
||||||
./init.sh # Configure the CMake build for the first time, or if you
|
./init.sh # Configure the CMake build for the first time, or if you
|
||||||
# have changed one of the CMakeList.txt files. (release build)
|
# have changed one of the CMakeList.txt files. (release build)
|
||||||
|
|
||||||
./mk.sh # Build all targets (release build)
|
./mk.sh # Build all targets (release build)
|
||||||
|
|
||||||
sudo ./install.sh # Deploy all targets
|
sudo ./install.sh # Deploy all targets
|
||||||
|
|
||||||
sudo ./makePackage.sh # Build a .deb file for distribution.
|
sudo ./makePackage.sh # Build a .deb file for distribution.
|
||||||
|
|
||||||
|
|
||||||
If you are using a development environment other than Visual Studio Code, it should be fairly straightforward to figure out how
|
If you are using a development environment other than Visual Studio Code, it should be fairly straightforward to figure out how
|
||||||
to incorporate the PiPedal build procedure into your IDE workflow by using the contents of the build shell scripts as a model.
|
to incorporate the PiPedal build procedure into your IDE workflow by using the contents of the build shell scripts as a model.
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,3 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
cd build
|
cd build
|
||||||
cpack -G DEB -C Release -config CPackConfig.cmake
|
cpack -G DEB -C Release -config CPackConfig.cmake
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
${SCRIPT_DIR}/../build/src/profilePlugin -s 100 -w ToobNam_Profile -o /tmp/ToobNam.perf &&\
|
${SCRIPT_DIR}/../build/src/profilePlugin -s 100 -w ToobNam_Profile -o /tmp/ToobNam.perf &&\
|
||||||
google-pprof --text ${SCRIPT_DIR}/../build/src/profilePlugin --add_lib /usr/lib/lv2/ToobAmp.lv2/ToobAmp.so /tmp/ToobNam.perf >./ToobNam.txt
|
google-pprof --text ${SCRIPT_DIR}/../build/src/profilePlugin --add_lib /usr/lib/lv2/ToobAmp.lv2/ToobAmp.so /tmp/ToobNam.perf >./ToobNam.txt
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
${SCRIPT_DIR}/../build/src/profilePlugin -w Nam_Profile -o /tmp/nam.perf &&\
|
${SCRIPT_DIR}/../build/src/profilePlugin -w Nam_Profile -o /tmp/nam.perf &&\
|
||||||
google-pprof --web ${SCRIPT_DIR}/../build/src/profilePlugin --add_lib /usr/lib/lv2/neural_amp_modeler.lv2/neural_amp_modeler.so /tmp/nam.perf >./nam.txt
|
google-pprof --web ${SCRIPT_DIR}/../build/src/profilePlugin --add_lib /usr/lib/lv2/neural_amp_modeler.lv2/neural_amp_modeler.so /tmp/nam.perf >./nam.txt
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
rm -rf ./lv2/aarch64/*
|
rm -rf ./lv2/aarch64/*
|
||||||
|
|
||||||
cp -R /usr/lib/lv2/ToobAmp.lv2/ ./lv2/aarch64/
|
cp -R /usr/lib/lv2/ToobAmp.lv2/ ./lv2/aarch64/
|
||||||
|
|||||||
+5
-1
@@ -11,4 +11,8 @@
|
|||||||
# to NOT do auto-updating (probably best). The build doesn't currently have a procedure for
|
# to NOT do auto-updating (probably best). The build doesn't currently have a procedure for
|
||||||
# knocking out Auto-Update from UpdaterSecurity.hpp. But I would happily accept a pull request
|
# knocking out Auto-Update from UpdaterSecurity.hpp. But I would happily accept a pull request
|
||||||
# to bring such a feature back into mainline if you do it.
|
# to bring such a feature back into mainline if you do it.
|
||||||
build/src/makeRelease
|
|
||||||
|
# Build the release package and create a detached GPG signature. This
|
||||||
|
# command assumes you have already run makePackage.sh so the .deb file
|
||||||
|
# exists in the build directory.
|
||||||
|
./build/src/makeRelease
|
||||||
|
|||||||
+6
-2
@@ -618,6 +618,7 @@ namespace pipedal
|
|||||||
|
|
||||||
if (this->captureHandle)
|
if (this->captureHandle)
|
||||||
{
|
{
|
||||||
|
this->alsa_device_name = this->jackServerSettings.GetAlsaInputDevice();
|
||||||
AlsaConfigureStream(
|
AlsaConfigureStream(
|
||||||
this->alsa_device_name,
|
this->alsa_device_name,
|
||||||
"capture",
|
"capture",
|
||||||
@@ -629,6 +630,7 @@ namespace pipedal
|
|||||||
}
|
}
|
||||||
if (this->playbackHandle)
|
if (this->playbackHandle)
|
||||||
{
|
{
|
||||||
|
this->alsa_device_name = this->jackServerSettings.GetAlsaOutputDevice();
|
||||||
AlsaConfigureStream(
|
AlsaConfigureStream(
|
||||||
this->alsa_device_name,
|
this->alsa_device_name,
|
||||||
"playback",
|
"playback",
|
||||||
@@ -1224,7 +1226,7 @@ namespace pipedal
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
std::string inputName = jackServerSettings.GetAlsaInputDevice();
|
std::string inputName = jackServerSettings.GetAlsaInputDevice();
|
||||||
std::string outputName = jackServerSettings.GetAlsaOutputDevice();
|
std::string outputName = jackServerSettings.GetAlsaOutputDevice();
|
||||||
|
|
||||||
|
|
||||||
this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers();
|
this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers();
|
||||||
@@ -1234,6 +1236,7 @@ namespace pipedal
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
|
this->alsa_device_name = outputName;
|
||||||
err = snd_pcm_open(&playbackHandle, outputName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
err = snd_pcm_open(&playbackHandle, outputName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
{
|
{
|
||||||
@@ -1268,7 +1271,8 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
snd_pcm_nonblock(playbackHandle, 0);
|
snd_pcm_nonblock(playbackHandle, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->alsa_device_name = inputName;
|
||||||
err = snd_pcm_open(&captureHandle, inputName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
err = snd_pcm_open(&captureHandle, inputName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public:
|
|||||||
|
|
||||||
AlsaFormatEncodeDecodeTest(this);
|
AlsaFormatEncodeDecodeTest(this);
|
||||||
|
|
||||||
JackServerSettings serverSettings("hw:M2",48000,32,3);
|
JackServerSettings serverSettings("hw:M2","hw:M2",48000,32,3);
|
||||||
|
|
||||||
JackConfiguration jackConfiguration;
|
JackConfiguration jackConfiguration;
|
||||||
if (useJack)
|
if (useJack)
|
||||||
|
|||||||
+11
-6
@@ -140,14 +140,19 @@ namespace pipedal
|
|||||||
overheadTime = readTime;
|
overheadTime = readTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleT totalTime = writeTime+ readTime + processingTime;
|
SampleT totalTime = writeTime + readTime + processingTime;
|
||||||
SampleT maxTime = waitTime+processingTime;
|
SampleT maxTime = waitTime + processingTime;
|
||||||
|
|
||||||
|
|
||||||
float result = 100.0f*(processingTime)/(maxTime);
|
float result = 0.0f;
|
||||||
float overhead = 100.0F*(overheadTime*2)/totalTime;
|
float overhead = 0.0f;
|
||||||
|
if (maxTime != 0 && totalTime != 0)
|
||||||
{
|
{
|
||||||
std::lock_guard lock { sync};
|
result = 100.0f * (processingTime) / (maxTime);
|
||||||
|
overhead = 100.0f * (overheadTime * 2) / totalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock{sync};
|
||||||
currentCpuUse = result;
|
currentCpuUse = result;
|
||||||
currentOverhead = overhead;
|
currentOverhead = overhead;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ namespace pipedal
|
|||||||
result.sampleRates_.push_back(48000);
|
result.sampleRates_.push_back(48000);
|
||||||
result.minBufferSize_ = 16;
|
result.minBufferSize_ = 16;
|
||||||
result.maxBufferSize_ = 1024;
|
result.maxBufferSize_ = 1024;
|
||||||
result.supportsCapture_ = true;
|
result.supportsCapture_ = true;
|
||||||
result.supportsPlayback_ = true;
|
result.supportsPlayback_ = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ void JackServerSettings::ReadJackDaemonConfiguration()
|
|||||||
this->bufferSize_ = GetJackArg(argv, "-p", "--period");
|
this->bufferSize_ = GetJackArg(argv, "-p", "--period");
|
||||||
this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "--nperiods");
|
this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "--nperiods");
|
||||||
this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "--rate");
|
this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "--rate");
|
||||||
// read new dual device flags, fallback on old -d/--device
|
// read new dual device flags, fallback on old -d/--device
|
||||||
std::string capDev = GetJackStringArg(argv, "-C", "--capture");
|
std::string capDev = GetJackStringArg(argv, "-C", "--capture");
|
||||||
std::string playDev = GetJackStringArg(argv, "-P", "--playback");
|
std::string playDev = GetJackStringArg(argv, "-P", "--playback");
|
||||||
std::string dev = "";
|
std::string dev = "";
|
||||||
@@ -254,6 +254,7 @@ JSON_MAP_REFERENCE(JackServerSettings, valid)
|
|||||||
JSON_MAP_REFERENCE(JackServerSettings, isOnboarding)
|
JSON_MAP_REFERENCE(JackServerSettings, isOnboarding)
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, rebootRequired)
|
JSON_MAP_REFERENCE(JackServerSettings, rebootRequired)
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, isJackAudio)
|
JSON_MAP_REFERENCE(JackServerSettings, isJackAudio)
|
||||||
|
JSON_MAP_REFERENCE(JackServerSettings, alsaDevice) // legacy field
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDevice)
|
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDevice)
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDevice)
|
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDevice)
|
||||||
JSON_MAP_REFERENCE(JackServerSettings, sampleRate)
|
JSON_MAP_REFERENCE(JackServerSettings, sampleRate)
|
||||||
|
|||||||
+18
-16
@@ -31,8 +31,9 @@ namespace pipedal
|
|||||||
bool isOnboarding_ = true;
|
bool isOnboarding_ = true;
|
||||||
bool isJackAudio_ = JACK_HOST ? true : false;
|
bool isJackAudio_ = JACK_HOST ? true : false;
|
||||||
bool rebootRequired_ = false;
|
bool rebootRequired_ = false;
|
||||||
std::string alsaInputDevice_;
|
std::string alsaInputDevice_;
|
||||||
std::string alsaOutputDevice_;
|
std::string alsaOutputDevice_;
|
||||||
|
std::string alsaDevice_; // legacy
|
||||||
uint64_t sampleRate_ = 0;
|
uint64_t sampleRate_ = 0;
|
||||||
uint32_t bufferSize_ = 64;
|
uint32_t bufferSize_ = 64;
|
||||||
uint32_t numberOfBuffers_ = 3;
|
uint32_t numberOfBuffers_ = 3;
|
||||||
@@ -41,14 +42,15 @@ namespace pipedal
|
|||||||
void SetAlsaInputDevice(const std::string &d){ alsaInputDevice_ = d; }
|
void SetAlsaInputDevice(const std::string &d){ alsaInputDevice_ = d; }
|
||||||
void SetAlsaOutputDevice(const std::string &d){ alsaOutputDevice_ = d; }
|
void SetAlsaOutputDevice(const std::string &d){ alsaOutputDevice_ = d; }
|
||||||
JackServerSettings();
|
JackServerSettings();
|
||||||
JackServerSettings(
|
JackServerSettings(
|
||||||
const std::string alsaInputDevice,
|
const std::string &alsaInputDevice,
|
||||||
|
const std::string &alsaOutputDevice,
|
||||||
uint64_t sampleRate,
|
uint64_t sampleRate,
|
||||||
uint32_t bufferSize,
|
uint32_t bufferSize,
|
||||||
uint32_t numberOfBuffers)
|
uint32_t numberOfBuffers)
|
||||||
: valid_(true),
|
: valid_(true),
|
||||||
alsaInputDevice_(alsaInputDevice),
|
alsaInputDevice_(alsaInputDevice),
|
||||||
alsaOutputDevice_(alsaInputDevice), // default same
|
alsaOutputDevice_(alsaOutputDevice),
|
||||||
sampleRate_(sampleRate),
|
sampleRate_(sampleRate),
|
||||||
bufferSize_(bufferSize),
|
bufferSize_(bufferSize),
|
||||||
numberOfBuffers_(numberOfBuffers),
|
numberOfBuffers_(numberOfBuffers),
|
||||||
@@ -57,10 +59,13 @@ namespace pipedal
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint64_t GetSampleRate() const { return sampleRate_; }
|
uint64_t GetSampleRate() const { return sampleRate_; }
|
||||||
|
|
||||||
uint32_t GetBufferSize() const { return bufferSize_; }
|
uint32_t GetBufferSize() const { return bufferSize_; }
|
||||||
uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; }
|
uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; }
|
||||||
const std::string &GetAlsaInputDevice() const { return alsaInputDevice_; }
|
const std::string &GetAlsaInputDevice() const { return alsaInputDevice_; }
|
||||||
const std::string &GetAlsaOutputDevice() const { return alsaOutputDevice_; }
|
const std::string &GetAlsaOutputDevice() const { return alsaOutputDevice_; }
|
||||||
|
const std::string &GetLegacyAlsaDevice() const { return alsaDevice_; } //legacy
|
||||||
|
void SetLegacyAlsaDevice(const std::string &d) { alsaDevice_ = d; }
|
||||||
void UseDummyAudioDevice() {
|
void UseDummyAudioDevice() {
|
||||||
this->valid_ = true;
|
this->valid_ = true;
|
||||||
if (sampleRate_ == 0) sampleRate_ = 48000;
|
if (sampleRate_ == 0) sampleRate_ = 48000;
|
||||||
@@ -76,14 +81,6 @@ namespace pipedal
|
|||||||
|
|
||||||
bool IsValid() const { return valid_; }
|
bool IsValid() const { return valid_; }
|
||||||
|
|
||||||
// JackServerSettings(uint64_t sampleRate, uint32_t bufferSize, uint32_t numberOfBuffers)
|
|
||||||
// {
|
|
||||||
// this->valid_ = true;
|
|
||||||
// this->rebootRequired_ = true;
|
|
||||||
// this->sampleRate_ = sampleRate;
|
|
||||||
// this->bufferSize_ = bufferSize;
|
|
||||||
// this->numberOfBuffers_ = numberOfBuffers;
|
|
||||||
// }
|
|
||||||
void WriteDaemonConfig(); // requires root perms.
|
void WriteDaemonConfig(); // requires root perms.
|
||||||
void SetRebootRequired(bool value)
|
void SetRebootRequired(bool value)
|
||||||
{
|
{
|
||||||
@@ -94,9 +91,14 @@ namespace pipedal
|
|||||||
isOnboarding_ = value;
|
isOnboarding_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Equals(const JackServerSettings &other)
|
bool Equals(const JackServerSettings &other) const
|
||||||
{
|
{
|
||||||
return this->alsaInputDevice_ == other.alsaInputDevice_ && this->alsaOutputDevice_ == other.alsaOutputDevice_ && this->sampleRate_ == other.sampleRate_ && this->bufferSize_ == other.bufferSize_ && this->numberOfBuffers_ == other.numberOfBuffers_;
|
return this->alsaInputDevice_ == other.alsaInputDevice_ &&
|
||||||
|
this->alsaOutputDevice_ == other.alsaOutputDevice_ &&
|
||||||
|
this->alsaDevice_ == other.alsaDevice_ &&
|
||||||
|
this->sampleRate_ == other.sampleRate_ &&
|
||||||
|
this->bufferSize_ == other.bufferSize_ &&
|
||||||
|
this->numberOfBuffers_ == other.numberOfBuffers_;
|
||||||
}
|
}
|
||||||
|
|
||||||
DECLARE_JSON_MAP(JackServerSettings);
|
DECLARE_JSON_MAP(JackServerSettings);
|
||||||
|
|||||||
+25
-16
@@ -57,9 +57,9 @@ void PrintHelp()
|
|||||||
pp << "Copyright (c) 2022 Robin Davies\n";
|
pp << "Copyright (c) 2022 Robin Davies\n";
|
||||||
pp << "\n";
|
pp << "\n";
|
||||||
pp << Indent(0) << "Syntax\n\n";
|
pp << Indent(0) << "Syntax\n\n";
|
||||||
pp << Indent(2) << "pipedal_latency_test [<options>] <device-name>\n\n";
|
pp << Indent(2) << "pipedal_latency_test [<options>] <input-device> [<output-device>]\n\n";
|
||||||
pp << "where <device-name> is the name of an ALSA device. Typically this should be the name of a hardware "
|
pp << "where <input-device> is the name of an ALSA capture device and <output-device> is the name of a playback device. "
|
||||||
"device (a device name starting with 'hw:').\n\n";
|
"If <output-device> is omitted, the input device will be used for both capture and playback. Typically the device names start with 'hw:'.\n\n";
|
||||||
pp << Indent(0) << "Options\n\n";
|
pp << Indent(0) << "Options\n\n";
|
||||||
pp << Indent(15);
|
pp << Indent(15);
|
||||||
|
|
||||||
@@ -95,7 +95,8 @@ void PrintHelp()
|
|||||||
|
|
||||||
pp << Indent(0) << "Examples\n\n";
|
pp << Indent(0) << "Examples\n\n";
|
||||||
pp << Indent(2) << "pipedal_latency_test --list\n\n";
|
pp << Indent(2) << "pipedal_latency_test --list\n\n";
|
||||||
pp << Indent(2) << "pipedal_latency_test hw:M2\n\n";
|
pp << Indent(2) << "pipedal_latency_test hw:M2 hw:M2\n";
|
||||||
|
pp << Indent(2) << "pipedal_latency_test hw:M2 hw:Device2\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListDevices()
|
void ListDevices()
|
||||||
@@ -138,7 +139,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
AudioDriver *audioDriver = nullptr;
|
AudioDriver *audioDriver = nullptr;
|
||||||
|
|
||||||
const std::string &deviceId;
|
const std::string &inputDeviceId;
|
||||||
|
const std::string &outputDeviceId;
|
||||||
ChannelsT inputChannels;
|
ChannelsT inputChannels;
|
||||||
ChannelsT outputChannels;
|
ChannelsT outputChannels;
|
||||||
uint32_t sampleRate;
|
uint32_t sampleRate;
|
||||||
@@ -147,11 +149,13 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
AlsaTester(
|
AlsaTester(
|
||||||
const std::string &deviceId,
|
const std::string &inputDeviceId,
|
||||||
|
const std::string &outputDeviceId,
|
||||||
const ChannelsT &inputChannels,
|
const ChannelsT &inputChannels,
|
||||||
const ChannelsT &outputChannels,
|
const ChannelsT &outputChannels,
|
||||||
uint32_t sampleRate, int bufferSize, int buffers)
|
uint32_t sampleRate, int bufferSize, int buffers)
|
||||||
: deviceId(deviceId),
|
: inputDeviceId(inputDeviceId),
|
||||||
|
outputDeviceId(outputDeviceId),
|
||||||
sampleRate(sampleRate),
|
sampleRate(sampleRate),
|
||||||
inputChannels(inputChannels),
|
inputChannels(inputChannels),
|
||||||
outputChannels(outputChannels),
|
outputChannels(outputChannels),
|
||||||
@@ -186,7 +190,7 @@ public:
|
|||||||
TestResult result;
|
TestResult result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
JackServerSettings serverSettings(deviceId, sampleRate, bufferSize, buffers);
|
JackServerSettings serverSettings(inputDeviceId, outputDeviceId, sampleRate, bufferSize, buffers);
|
||||||
|
|
||||||
JackConfiguration jackConfiguration;
|
JackConfiguration jackConfiguration;
|
||||||
jackConfiguration.AlsaInitialize(serverSettings);
|
jackConfiguration.AlsaInitialize(serverSettings);
|
||||||
@@ -403,12 +407,13 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
TestResult RunLatencyTest(
|
TestResult RunLatencyTest(
|
||||||
const std::string deviceId,
|
const std::string inputDeviceId,
|
||||||
|
const std::string outputDeviceId,
|
||||||
const ChannelsT &inputChannels,
|
const ChannelsT &inputChannels,
|
||||||
const ChannelsT &outputChannels,
|
const ChannelsT &outputChannels,
|
||||||
uint32_t sampleRate, int bufferSize, int buffers)
|
uint32_t sampleRate, int bufferSize, int buffers)
|
||||||
{
|
{
|
||||||
AlsaTester tester(deviceId, inputChannels, outputChannels, sampleRate, bufferSize, buffers);
|
AlsaTester tester(inputDeviceId, outputDeviceId, inputChannels, outputChannels, sampleRate, bufferSize, buffers);
|
||||||
return tester.Test();
|
return tester.Test();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,13 +433,14 @@ static std::string overheadDisplay(float value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RunLatencyTest(
|
void RunLatencyTest(
|
||||||
const std::string &deviceId,
|
const std::string &inputDeviceId,
|
||||||
|
const std::string &outputDeviceId,
|
||||||
const ChannelsT &inputChannels,
|
const ChannelsT &inputChannels,
|
||||||
const ChannelsT &outputChannels,
|
const ChannelsT &outputChannels,
|
||||||
uint32_t sampleRate)
|
uint32_t sampleRate)
|
||||||
{
|
{
|
||||||
PrettyPrinter pp;
|
PrettyPrinter pp;
|
||||||
pp << "Device: " << deviceId << " Rate: " << sampleRate << "\n\n";
|
pp << "Input: " << inputDeviceId << " Output: " << outputDeviceId << " Rate: " << sampleRate << "\n\n";
|
||||||
|
|
||||||
const int SIZE_COLUMN_WIDTH = 8;
|
const int SIZE_COLUMN_WIDTH = 8;
|
||||||
const int BUFFERS_COLUMN_WIDTH = 20;
|
const int BUFFERS_COLUMN_WIDTH = 20;
|
||||||
@@ -461,7 +467,7 @@ void RunLatencyTest(
|
|||||||
|
|
||||||
for (auto bufferCount : bufferCounts)
|
for (auto bufferCount : bufferCounts)
|
||||||
{
|
{
|
||||||
auto result = RunLatencyTest(deviceId, inputChannels,outputChannels, sampleRate, bufferSize, bufferCount);
|
auto result = RunLatencyTest(inputDeviceId, outputDeviceId, inputChannels,outputChannels, sampleRate, bufferSize, bufferCount);
|
||||||
|
|
||||||
pp.Column(column);
|
pp.Column(column);
|
||||||
column += BUFFERS_COLUMN_WIDTH;
|
column += BUFFERS_COLUMN_WIDTH;
|
||||||
@@ -556,11 +562,14 @@ std:
|
|||||||
{
|
{
|
||||||
ListDevices();
|
ListDevices();
|
||||||
}
|
}
|
||||||
else if (parser.Arguments().size() == 1)
|
else if (parser.Arguments().size() >= 1 && parser.Arguments().size() <= 2)
|
||||||
{
|
{
|
||||||
inputChannels = ParseChannels(strInputChannels);
|
inputChannels = ParseChannels(strInputChannels);
|
||||||
outputChannels = ParseChannels(strInputChannels);
|
outputChannels = ParseChannels(strOutputChannels);
|
||||||
RunLatencyTest(parser.Arguments()[0], inputChannels, outputChannels, sampleRate);
|
|
||||||
|
std::string inDev = parser.Arguments()[0];
|
||||||
|
std::string outDev = parser.Arguments().size() == 2 ? parser.Arguments()[1] : inDev;
|
||||||
|
RunLatencyTest(inDev, outDev, inputChannels, outputChannels, sampleRate);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
+43
-17
@@ -22,6 +22,7 @@
|
|||||||
#include "alsa/asoundlib.h"
|
#include "alsa/asoundlib.h"
|
||||||
#include "Lv2Log.hpp"
|
#include "Lv2Log.hpp"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
@@ -106,7 +107,7 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
|||||||
|
|
||||||
if (captureOk || playbackOk)
|
if (captureOk || playbackOk)
|
||||||
{
|
{
|
||||||
snd_pcm_t *hDevice = captureOk ? captureDevice : playbackDevice;
|
snd_pcm_t *hDevice = captureOk ? captureDevice : playbackDevice;
|
||||||
snd_pcm_hw_params_t *params = nullptr;
|
snd_pcm_hw_params_t *params = nullptr;
|
||||||
err = snd_pcm_hw_params_malloc(¶ms);
|
err = snd_pcm_hw_params_malloc(¶ms);
|
||||||
if (err == 0)
|
if (err == 0)
|
||||||
@@ -117,29 +118,42 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
|||||||
unsigned int minRate = 0, maxRate = 0;
|
unsigned int minRate = 0, maxRate = 0;
|
||||||
snd_pcm_uframes_t minBufferSize = 0, maxBufferSize = 0;
|
snd_pcm_uframes_t minBufferSize = 0, maxBufferSize = 0;
|
||||||
int dir;
|
int dir;
|
||||||
|
|
||||||
err = snd_pcm_hw_params_get_rate_min(params, &minRate, &dir);
|
err = snd_pcm_hw_params_get_rate_min(params, &minRate, &dir);
|
||||||
if (err == 0)
|
if (err == 0)
|
||||||
{
|
{
|
||||||
err = snd_pcm_hw_params_get_rate_max(params, &maxRate, &dir);
|
err = snd_pcm_hw_params_get_rate_max(params, &maxRate, &dir);
|
||||||
|
if (err == 0)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof(RATES) / sizeof(RATES[0]); ++i)
|
||||||
|
{
|
||||||
|
uint32_t rate = RATES[i];
|
||||||
|
if (rate >= minRate && rate <= maxRate)
|
||||||
|
{
|
||||||
|
info.sampleRates_.push_back(rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Lv2Log::warning(SS("Failed to get maximum sample rate for device '" << info.name_ << "'."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Lv2Log::warning(SS("Failed to get minimum sample rate for device '" << info.name_ << "'."));
|
||||||
|
}
|
||||||
|
|
||||||
if (err == 0)
|
if (err == 0)
|
||||||
{
|
{
|
||||||
err = snd_pcm_hw_params_get_buffer_size_min(params, &minBufferSize);
|
err = snd_pcm_hw_params_get_buffer_size_min(params, &minBufferSize);
|
||||||
}
|
if (err == 0)
|
||||||
if (err == 0)
|
|
||||||
{
|
|
||||||
err = snd_pcm_hw_params_get_buffer_size_max(params, &maxBufferSize);
|
|
||||||
}
|
|
||||||
if (err == 0)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < sizeof(RATES) / sizeof(RATES[0]); ++i)
|
|
||||||
{
|
{
|
||||||
uint32_t rate = RATES[i];
|
err = snd_pcm_hw_params_get_buffer_size_max(params, &maxBufferSize);
|
||||||
if (rate >= minRate && rate <= maxRate)
|
|
||||||
{
|
|
||||||
info.sampleRates_.push_back(rate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (err == 0)
|
||||||
|
{
|
||||||
if (minBufferSize < 16)
|
if (minBufferSize < 16)
|
||||||
{
|
{
|
||||||
minBufferSize = 16;
|
minBufferSize = 16;
|
||||||
@@ -165,7 +179,7 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
|||||||
{
|
{
|
||||||
result.push_back(info);
|
result.push_back(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (getCachedDevice(info.name_, &info))
|
if (getCachedDevice(info.name_, &info))
|
||||||
@@ -180,12 +194,24 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
|||||||
}
|
}
|
||||||
snd_config_update_free_global();
|
snd_config_update_free_global();
|
||||||
|
|
||||||
|
auto isFiltered = [](const AlsaDeviceInfo &d) {
|
||||||
|
std::string name = d.name_ + " " + d.longName_;
|
||||||
|
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c){return std::tolower(c);});
|
||||||
|
return name.find("hdmi") != std::string::npos || name.find("bcm2835") != std::string::npos;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<AlsaDeviceInfo> filtered;
|
||||||
|
for (auto &d : result)
|
||||||
|
{
|
||||||
|
if (!isFiltered(d)) filtered.push_back(d);
|
||||||
|
}
|
||||||
|
|
||||||
Lv2Log::debug("GetAlsaDevices --");
|
Lv2Log::debug("GetAlsaDevices --");
|
||||||
for (auto &device : result)
|
for (auto &device : filtered)
|
||||||
{
|
{
|
||||||
Lv2Log::debug(SS(" " << device.name_ << " " << device.longName_ << " " << device.cardId_));
|
Lv2Log::debug(SS(" " << device.name_ << " " << device.longName_ << " " << device.cardId_));
|
||||||
}
|
}
|
||||||
return result;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AddMidiCardDevicesToList(snd_ctl_t *ctl, int card, int device, AlsaMidiDeviceInfo::Direction direction, std::vector<AlsaMidiDeviceInfo> *result)
|
static void AddMidiCardDevicesToList(snd_ctl_t *ctl, int card, int device, AlsaMidiDeviceInfo::Direction direction, std::vector<AlsaMidiDeviceInfo> *result)
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ namespace pipedal {
|
|||||||
std::string longName_;
|
std::string longName_;
|
||||||
std::vector<uint32_t> sampleRates_;
|
std::vector<uint32_t> sampleRates_;
|
||||||
uint32_t minBufferSize_ = 0,maxBufferSize_ = 0;
|
uint32_t minBufferSize_ = 0,maxBufferSize_ = 0;
|
||||||
bool supportsCapture_ = false;
|
bool supportsCapture_ = false;
|
||||||
bool supportsPlayback_ = false;
|
bool supportsPlayback_ = false;
|
||||||
|
|
||||||
bool isDummyDevice() const {
|
bool isDummyDevice() const {
|
||||||
|
|||||||
@@ -1598,6 +1598,14 @@ pipedal::JackServerSettings Storage::GetJackServerSettings()
|
|||||||
{
|
{
|
||||||
json_reader reader(f);
|
json_reader reader(f);
|
||||||
reader.read(&result);
|
reader.read(&result);
|
||||||
|
if (!result.GetLegacyAlsaDevice().empty() &&
|
||||||
|
result.GetAlsaInputDevice().empty() &&
|
||||||
|
result.GetAlsaOutputDevice().empty())
|
||||||
|
{
|
||||||
|
result.SetAlsaInputDevice(result.GetLegacyAlsaDevice());
|
||||||
|
result.SetAlsaOutputDevice(result.GetLegacyAlsaDevice());
|
||||||
|
result.SetLegacyAlsaDevice("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#if JACK_HOST
|
#if JACK_HOST
|
||||||
result.Initialize();
|
result.Initialize();
|
||||||
|
|||||||
+1
-1
@@ -457,7 +457,7 @@ std::string uri_builder::str() const {
|
|||||||
s << '?';
|
s << '?';
|
||||||
for (size_t i = 0; i < queries_.size(); ++i)
|
for (size_t i = 0; i < queries_.size(); ++i)
|
||||||
{
|
{
|
||||||
if (i != 0) s << '&';
|
if (i != 0) s << '&';
|
||||||
s << queries_[i].key << "=";
|
s << queries_[i].key << "=";
|
||||||
HtmlHelper::encode_url_segment(s,queries_[i].value,true);
|
HtmlHelper::encode_url_segment(s,queries_[i].value,true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#/usr/bin/sudo /usr/bin/bash
|
|
||||||
# CMake doesn't have an uninstall option, so remove files manually.
|
# CMake doesn't have an uninstall option, so remove files manually.
|
||||||
export PREFIX=/usr
|
export PREFIX=/usr
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
valgrind --leak-check=full --log-file=vg.output ./build/src/pipedald /etc/pipedal/config ./vite/dist -port 0.0.0.0:8080 -log-level debug
|
valgrind --leak-check=full --log-file=vg.output ./build/src/pipedald /etc/pipedal/config ./vite/dist -port 0.0.0.0:8080 -log-level debug
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const AboutDialog = class extends Component<AboutDialogProps, AboutDialogState>
|
|||||||
.then(jackStatus => {
|
.then(jackStatus => {
|
||||||
this.setState({ jackStatus: jackStatus });
|
this.setState({ jackStatus: jackStatus });
|
||||||
})
|
})
|
||||||
.catch(_error => { /* ignore*/ });
|
.catch(_error => { /* ignore*/ });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,12 +116,12 @@ const AboutDialog = class extends Component<AboutDialogProps, AboutDialogState>
|
|||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
this.updateNotifications();
|
this.updateNotifications();
|
||||||
}
|
}
|
||||||
componentDidUpdate(prevProps: Readonly<AboutDialogProps>, prevState: Readonly<AboutDialogState>, snapshot: any): void {
|
componentDidUpdate(prevProps: Readonly<AboutDialogProps>, prevState: Readonly<AboutDialogState>, snapshot: any): void {
|
||||||
super.componentDidUpdate?.(prevProps, prevState, snapshot);
|
super.componentDidUpdate?.(prevProps, prevState, snapshot);
|
||||||
this.updateNotifications();
|
this.updateNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDialogClose(_e: SyntheticEvent) {
|
handleDialogClose(_e: SyntheticEvent) {
|
||||||
this.props.onClose();
|
this.props.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default class AlsaDeviceInfo {
|
|||||||
this.sampleRates = input.sampleRates as number[];
|
this.sampleRates = input.sampleRates as number[];
|
||||||
this.minBufferSize = input.minBufferSize;
|
this.minBufferSize = input.minBufferSize;
|
||||||
this.maxBufferSize = input.maxBufferSize;
|
this.maxBufferSize = input.maxBufferSize;
|
||||||
this.supportsCapture = input.supportsCapture ? true : false;
|
this.supportsCapture = input.supportsCapture ? true : false;
|
||||||
this.supportsPlayback = input.supportsPlayback ? true : false;
|
this.supportsPlayback = input.supportsPlayback ? true : false;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export class FakeAndroidHost implements AndroidHostInterface
|
|||||||
|
|
||||||
launchExternalUrl(_url:string): boolean
|
launchExternalUrl(_url:string): boolean
|
||||||
{
|
{
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
private theme = 1;
|
private theme = 1;
|
||||||
@@ -71,6 +72,7 @@ export class FakeAndroidHost implements AndroidHostInterface
|
|||||||
}
|
}
|
||||||
setServerVersion(_serverVersion: string): void {
|
setServerVersion(_serverVersion: string): void {
|
||||||
// No-op for fake host
|
// No-op for fake host
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private keepScreenOn = false;
|
private keepScreenOn = false;
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ const theme = createTheme(
|
|||||||
/* make the selection state for MuiListItemButtons a smidgen darker (light theme only) */
|
/* make the selection state for MuiListItemButtons a smidgen darker (light theme only) */
|
||||||
MuiListItemButton: {
|
MuiListItemButton: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: ({ theme }) => ({
|
root: ({ theme }) => ({
|
||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.2)', // Adjust for desired darkness
|
backgroundColor: 'rgba(0, 0, 0, 0.2)', // Adjust for desired darkness
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
|
|||||||
@@ -189,4 +189,46 @@ export default class JackHostStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getDisplayViewNoCpu(label: string, status?: JackHostStatus): React.ReactNode {
|
||||||
|
if (!status) {
|
||||||
|
return (<div style={{ whiteSpace: "nowrap" }}>
|
||||||
|
<Typography variant="caption" color="inherit">{label}</Typography>
|
||||||
|
<Typography variant="caption"> </Typography>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
if (status.restarting) {
|
||||||
|
return (
|
||||||
|
<div style={{ whiteSpace: "nowrap" }}>
|
||||||
|
<Typography variant="caption" color="inherit">{label}</Typography>
|
||||||
|
<span style={{ color: RED_COLOR }}>
|
||||||
|
<Typography variant="caption" color="inherit">Restarting </Typography>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
} else if (!status.active) {
|
||||||
|
return (
|
||||||
|
<div style={{ whiteSpace: "nowrap" }}>
|
||||||
|
<Typography variant="caption" color="inherit">{label}</Typography>
|
||||||
|
|
||||||
|
<span style={{ color: RED_COLOR }}>
|
||||||
|
<Typography variant="caption" color="inherit">{status.errorMessage === "" ? "Audio\u00A0Stopped" : status.errorMessage} </Typography>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let underrunError = status.msSinceLastUnderrun < 15 * 1000;
|
||||||
|
return (
|
||||||
|
<div style={{ whiteSpace: "nowrap" }}>
|
||||||
|
<Typography variant="caption" color="inherit">{label}</Typography>
|
||||||
|
<span style={{ color: underrunError ? RED_COLOR : GREEN_COLOR }}>
|
||||||
|
<Typography variant="caption" color="inherit">
|
||||||
|
XRuns: {status.underruns + ""}
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -49,20 +49,44 @@ export default class JackServerSettings {
|
|||||||
isOnboarding: boolean = true;
|
isOnboarding: boolean = true;
|
||||||
rebootRequired = false;
|
rebootRequired = false;
|
||||||
isJackAudio = false;
|
isJackAudio = false;
|
||||||
alsaInputDevice: string = "";
|
alsaInputDevice: string = "";
|
||||||
alsaOutputDevice: string = "";
|
alsaOutputDevice: string = "";
|
||||||
sampleRate = 48000;
|
sampleRate = 48000;
|
||||||
bufferSize = 64;
|
bufferSize = 64;
|
||||||
numberOfBuffers = 3;
|
numberOfBuffers = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure this instance to use the dummy audio device. This mirrors the
|
||||||
|
* behaviour of the backend JackServerSettings::UseDummyAudioDevice method
|
||||||
|
* and is used when temporarily releasing ALSA devices.
|
||||||
|
* Needed when changing devices and when testing new settings with apply button.
|
||||||
|
*/
|
||||||
|
useDummyAudioDevice() {
|
||||||
|
if (this.sampleRate === 0) {
|
||||||
|
this.sampleRate = 48000;
|
||||||
|
}
|
||||||
|
this.valid = true;
|
||||||
|
this.alsaInputDevice = "__DUMMY_AUDIO__dummy:channels_2";
|
||||||
|
this.alsaOutputDevice = "__DUMMY_AUDIO__dummy:channels_2";
|
||||||
|
}
|
||||||
|
|
||||||
getSummaryText() {
|
getSummaryText() {
|
||||||
if (this.valid) {
|
if (!this.valid || !this.alsaInputDevice || !this.alsaOutputDevice) {
|
||||||
let inDev = this.alsaInputDevice.startsWith("hw:") ? this.alsaInputDevice .substring(3) : this.alsaInputDevice;
|
return "Not selected";
|
||||||
let outDev = this.alsaOutputDevice.startsWith("hw:") ? this.alsaOutputDevice.substring(3) : this.alsaOutputDevice;
|
}
|
||||||
return "In: "+inDev+" Out: "+outDev+" — Rate "+this.sampleRate+", "+this.bufferSize+"×"+this.numberOfBuffers;
|
|
||||||
|
let inDev = this.alsaInputDevice.startsWith("hw:")
|
||||||
|
? this.alsaInputDevice.substring(3)
|
||||||
|
: this.alsaInputDevice;
|
||||||
|
let outDev = this.alsaOutputDevice.startsWith("hw:")
|
||||||
|
? this.alsaOutputDevice.substring(3)
|
||||||
|
: this.alsaOutputDevice;
|
||||||
|
|
||||||
|
if (inDev === outDev) {
|
||||||
|
return inDev;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return "Not configured";
|
return inDev+" → "+outDev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,9 @@
|
|||||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
import { Component } from 'react';
|
|
||||||
|
|
||||||
import { Theme } from '@mui/material/styles';
|
import { Theme } from '@mui/material/styles';
|
||||||
|
|
||||||
import WithStyles from './WithStyles';
|
import WithStyles from './WithStyles';
|
||||||
@@ -40,10 +38,36 @@ import DialogContent from '@mui/material/DialogContent';
|
|||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||||
import IconButtonEx from './IconButtonEx';
|
import IconButtonEx from './IconButtonEx';
|
||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
|
|
||||||
import AlsaDeviceInfo from './AlsaDeviceInfo';
|
import AlsaDeviceInfo from './AlsaDeviceInfo';
|
||||||
|
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||||
|
|
||||||
|
function filterDevices(devices: AlsaDeviceInfo[]): AlsaDeviceInfo[] {
|
||||||
|
return devices.filter(d => {
|
||||||
|
const name = (d.name + ' ' + d.longName).toLowerCase();
|
||||||
|
return !(name.includes('hdmi') || name.includes('bcm2835'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortDevices(devices: AlsaDeviceInfo[]): AlsaDeviceInfo[] {
|
||||||
|
function category(d: AlsaDeviceInfo): number {
|
||||||
|
if (d.supportsCapture && d.supportsPlayback) return 0;
|
||||||
|
if (d.longName.toLowerCase().includes("usb")) return 1;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
let copy = [...devices];
|
||||||
|
copy.sort((a,b) => {
|
||||||
|
let ca = category(a);
|
||||||
|
let cb = category(b);
|
||||||
|
if (ca !== cb) return ca - cb;
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
const MIN_BUFFER_SIZE = 16;
|
const MIN_BUFFER_SIZE = 16;
|
||||||
const MAX_BUFFER_SIZE = 2048;
|
const MAX_BUFFER_SIZE = 2048;
|
||||||
@@ -54,12 +78,17 @@ interface BufferSetting {
|
|||||||
numberOfBuffers: number;
|
numberOfBuffers: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const INVALID_DEVICE_ID = "_invalid_";
|
// empty string used when no valid device is selected - the default.
|
||||||
|
const INVALID_DEVICE_ID = "";
|
||||||
interface JackServerSettingsDialogState {
|
interface JackServerSettingsDialogState {
|
||||||
latencyText: string;
|
latencyText: string;
|
||||||
jackServerSettings: JackServerSettings;
|
jackServerSettings: JackServerSettings;
|
||||||
alsaDevices?: AlsaDeviceInfo[];
|
alsaDevices?: AlsaDeviceInfo[];
|
||||||
okEnabled: boolean;
|
okEnabled: boolean;
|
||||||
|
fullScreen: boolean;
|
||||||
|
compactWidth: boolean;
|
||||||
|
showDeviceWarning: boolean;
|
||||||
|
dontShowWarningAgain: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
@@ -71,6 +100,9 @@ const styles = (theme: Theme) =>
|
|||||||
selectEmpty: {
|
selectEmpty: {
|
||||||
marginTop: theme.spacing(2),
|
marginTop: theme.spacing(2),
|
||||||
},
|
},
|
||||||
|
inputLabel: {
|
||||||
|
whiteSpace: "nowrap"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
export interface JackServerSettingsDialogProps extends WithStyles<typeof styles> {
|
export interface JackServerSettingsDialogProps extends WithStyles<typeof styles> {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -80,13 +112,15 @@ export interface JackServerSettingsDialogProps extends WithStyles<typeof styles>
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getLatencyText(settings?: JackServerSettings ): string {
|
function getLatencyText(settings?: JackServerSettings ): string {
|
||||||
if (!settings)
|
if (!settings) {
|
||||||
{
|
|
||||||
return "\u00A0";
|
return "\u00A0";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.valid) return "\u00A0";
|
if (!settings.sampleRate || !settings.bufferSize || !settings.numberOfBuffers) {
|
||||||
let ms = settings.bufferSize * settings.numberOfBuffers / settings.sampleRate * 1000;
|
return "\u00A0";
|
||||||
|
}
|
||||||
|
|
||||||
|
let ms = (settings.bufferSize * settings.numberOfBuffers) / settings.sampleRate * 1000;
|
||||||
return ms.toFixed(1) + "ms";
|
return ms.toFixed(1) + "ms";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,11 +193,6 @@ function getBestBuffers(
|
|||||||
{
|
{
|
||||||
return { bufferSize:bufferSize, numberOfBuffers: numberOfBuffers};
|
return { bufferSize:bufferSize, numberOfBuffers: numberOfBuffers};
|
||||||
}
|
}
|
||||||
const device = inDevice || outDevice;
|
|
||||||
if (!device)
|
|
||||||
{
|
|
||||||
return { bufferSize:bufferSize, numberOfBuffers:numberOfBuffers};
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the numberOfbuffers is fine as is, don't change the number of Buffers.
|
// If the numberOfbuffers is fine as is, don't change the number of Buffers.
|
||||||
// set default values. Otherwise, choose the best buffer count from available buffer counts.
|
// set default values. Otherwise, choose the best buffer count from available buffer counts.
|
||||||
@@ -197,33 +226,57 @@ function getBestBuffers(
|
|||||||
return {bufferSize: bufferSize, numberOfBuffers:numberOfBuffers};
|
return {bufferSize: bufferSize, numberOfBuffers:numberOfBuffers};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// otherwise select a sensible starting value.
|
// otherwise select a sensible starting value.
|
||||||
|
|
||||||
// favored default: 64x3.
|
// Build candidate sizes with 32 first if present.
|
||||||
if (64*3 >= minSize && 64*3 <= maxSize)
|
let validBufferSizes = getValidBufferSizesMultiple(inDevice, outDevice);
|
||||||
{
|
validBufferSizes.sort((a,b)=>a-b);
|
||||||
return { bufferSize: 64, numberOfBuffers: 3};
|
if (validBufferSizes.indexOf(32) !== -1) {
|
||||||
|
validBufferSizes = [32, ...validBufferSizes.filter(v => v !== 32)];
|
||||||
}
|
}
|
||||||
// if that isn't possible then minBufferSize/2 x 4.
|
|
||||||
bufferSize = minSize/2;
|
function tryPick(count: number): BufferSetting | undefined {
|
||||||
|
for (let bs of validBufferSizes) {
|
||||||
|
const counts = getValidBufferCountsMultiple(bs, inDevice, outDevice);
|
||||||
|
if (counts.indexOf(count) !== -1) {
|
||||||
|
if (bs * count >= minSize && bs * count <= maxSize) {
|
||||||
|
return { bufferSize: bs, numberOfBuffers: count };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = tryPick(4);
|
||||||
|
if (!result) result = tryPick(3);
|
||||||
|
if (!result) result = tryPick(2);
|
||||||
|
if (result) return result;
|
||||||
|
|
||||||
|
// Fallback to previous behaviour.
|
||||||
|
if (64*3 >= minSize && 64*3 <= maxSize) {
|
||||||
|
return { bufferSize: 64, numberOfBuffers: 3 };
|
||||||
|
}
|
||||||
|
bufferSize = minSize/2;
|
||||||
numberOfBuffers = 4;
|
numberOfBuffers = 4;
|
||||||
// otherwise, minBufferSize/2 x 2.
|
if (bufferSize * numberOfBuffers > maxSize) {
|
||||||
if (bufferSize*numberOfBuffers > maxSize)
|
|
||||||
{
|
|
||||||
numberOfBuffers = 2;
|
numberOfBuffers = 2;
|
||||||
}
|
}
|
||||||
return { bufferSize: bufferSize, numberOfBuffers: numberOfBuffers};
|
return { bufferSize: bufferSize, numberOfBuffers: numberOfBuffers };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaDeviceInfo[])
|
function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaDeviceInfo[])
|
||||||
{
|
{
|
||||||
if (!jackServerSettings.valid) return false;
|
|
||||||
if (!alsaDevices) return false;
|
if (!alsaDevices) return false;
|
||||||
|
if (!jackServerSettings.alsaInputDevice || !jackServerSettings.alsaOutputDevice) return false;
|
||||||
|
|
||||||
const inDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaInputDevice && d.supportsCapture);
|
const inDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaInputDevice && d.supportsCapture);
|
||||||
const outDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaOutputDevice && d.supportsPlayback);
|
const outDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaOutputDevice && d.supportsPlayback);
|
||||||
if (!inDevice || !outDevice) return false;
|
if (!inDevice || !outDevice) return false;
|
||||||
|
|
||||||
|
const sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates);
|
||||||
|
if (sampleRates.indexOf(jackServerSettings.sampleRate) === -1) return false;
|
||||||
|
|
||||||
const deviceBufferSize = jackServerSettings.bufferSize * jackServerSettings.numberOfBuffers;
|
const deviceBufferSize = jackServerSettings.bufferSize * jackServerSettings.numberOfBuffers;
|
||||||
const minSize = Math.max(inDevice.minBufferSize, outDevice.minBufferSize);
|
const minSize = Math.max(inDevice.minBufferSize, outDevice.minBufferSize);
|
||||||
const maxSize = Math.min(inDevice.maxBufferSize, outDevice.maxBufferSize);
|
const maxSize = Math.min(inDevice.maxBufferSize, outDevice.maxBufferSize);
|
||||||
@@ -232,53 +285,130 @@ function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaD
|
|||||||
const validBufferCounts = getValidBufferCountsMultiple(jackServerSettings.bufferSize, inDevice, outDevice);
|
const validBufferCounts = getValidBufferCountsMultiple(jackServerSettings.bufferSize, inDevice, outDevice);
|
||||||
if (validBufferCounts.indexOf(jackServerSettings.numberOfBuffers) === -1) return false;
|
if (validBufferCounts.indexOf(jackServerSettings.numberOfBuffers) === -1) return false;
|
||||||
|
|
||||||
const sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates);
|
|
||||||
if (sampleRates.indexOf(jackServerSettings.sampleRate) === -1) return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const JackServerSettingsDialog = withStyles(
|
const JackServerSettingsDialog = withStyles(
|
||||||
class extends Component<JackServerSettingsDialogProps, JackServerSettingsDialogState> {
|
class extends ResizeResponsiveComponent<JackServerSettingsDialogProps, JackServerSettingsDialogState> {
|
||||||
|
|
||||||
model: PiPedalModel;
|
model: PiPedalModel;
|
||||||
|
ignoreClose: boolean = false;
|
||||||
|
|
||||||
constructor(props: JackServerSettingsDialogProps) {
|
constructor(props: JackServerSettingsDialogProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.model = PiPedalModelFactory.getInstance();
|
this.model = PiPedalModelFactory.getInstance();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.suppressDeviceWarning = localStorage.getItem("suppressSeparateDeviceWarning") === "1";
|
||||||
this.state = {
|
this.state = {
|
||||||
latencyText: getLatencyText(props.jackServerSettings),
|
latencyText: getLatencyText(props.jackServerSettings),
|
||||||
jackServerSettings: props.jackServerSettings.clone(), // invalid, but not nullish
|
jackServerSettings: props.jackServerSettings.clone(), // invalid, but not nullish
|
||||||
alsaDevices: undefined,
|
alsaDevices: undefined,
|
||||||
okEnabled: false
|
okEnabled: false,
|
||||||
|
fullScreen: this.getFullScreen(),
|
||||||
|
compactWidth: document.documentElement.clientWidth < 600,
|
||||||
|
showDeviceWarning: false,
|
||||||
|
dontShowWarningAgain: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
mounted: boolean = false;
|
mounted: boolean = false;
|
||||||
|
|
||||||
|
suppressDeviceWarning: boolean;
|
||||||
|
/**
|
||||||
|
* Copy of the settings when the dialog is opened. Pressing Apply only tests these settings temporarily. Closing the dialog without OK re-applies the saved copy so the audio driver returns to its previous configuration.
|
||||||
|
*/
|
||||||
|
originalJackServerSettings?: JackServerSettings;
|
||||||
|
|
||||||
|
getFullScreen() {
|
||||||
|
return document.documentElement.clientWidth < 420 ||
|
||||||
|
document.documentElement.clientHeight < 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
onWindowSizeChanged(width: number, height: number): void {
|
||||||
|
super.onWindowSizeChanged(width, height);
|
||||||
|
const fullScreen = this.getFullScreen();
|
||||||
|
const compactWidth = width < 600;
|
||||||
|
if (fullScreen !== this.state.fullScreen || compactWidth !== this.state.compactWidth) {
|
||||||
|
this.setState({ fullScreen, compactWidth });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
requestAlsaInfo() {
|
requestAlsaInfo() {
|
||||||
this.model.getAlsaDevices()
|
this.model.getAlsaDevices()
|
||||||
.then((devices) => {
|
.then((devices) => {
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
if (this.props.open) {
|
if (this.props.open) {
|
||||||
let settings = this.applyAlsaDevices(this.props.jackServerSettings, devices);
|
let f = filterDevices(devices);
|
||||||
|
let settings = this.applyAlsaDevices(this.state.jackServerSettings, f);
|
||||||
this.setState({
|
this.setState({
|
||||||
alsaDevices: devices,
|
alsaDevices: f,
|
||||||
jackServerSettings: settings,
|
jackServerSettings: settings,
|
||||||
latencyText: getLatencyText(settings),
|
latencyText: getLatencyText(settings),
|
||||||
okEnabled: isOkEnabled(settings,devices)
|
okEnabled: isOkEnabled(settings, f)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({ alsaDevices: devices });
|
this.setState({ alsaDevices: filterDevices(devices) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
// Error requesting ALSA info.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Persist the current settings to permanent storage. */
|
||||||
|
saveSettings(settings?: JackServerSettings): void {
|
||||||
|
const s = (settings ?? this.state.jackServerSettings).clone();
|
||||||
|
// Fire and forget. Errors will be handled by PiPedalModel's internal error handling (e.g., showAlert).
|
||||||
|
this.model.setJackServerSettings(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the current settings temporarily so they can be restored if the
|
||||||
|
* dialog is cancelled.
|
||||||
|
*/
|
||||||
|
saveSettingsTemporary(settings?: JackServerSettings) {
|
||||||
|
this.originalJackServerSettings = (settings ?? this.state.jackServerSettings).clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the provided settings to the audio system without persisting
|
||||||
|
* them. Falls back to the regular setter if the temporary apply API is
|
||||||
|
* unavailable.
|
||||||
|
*/
|
||||||
|
applySettings(settings?: JackServerSettings): void {
|
||||||
|
const s = (settings ?? this.state.jackServerSettings).clone();
|
||||||
|
s.valid = true;
|
||||||
|
|
||||||
|
const ws = this.model.webSocket;
|
||||||
|
if (ws) {
|
||||||
|
ws.request<void>("applyJackServerSettings", s)
|
||||||
|
.catch(() => { }); // Fire and forget
|
||||||
|
} else {
|
||||||
|
this.model.setJackServerSettings(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revert the dialog state to the persisted settings. The reverted
|
||||||
|
* settings are applied immediately as well.
|
||||||
|
*/
|
||||||
|
revertSettings() {
|
||||||
|
this.model.getJackServerSettings()
|
||||||
|
.then((s) => {
|
||||||
|
const applied = this.applyAlsaDevices(s.clone(), this.state.alsaDevices);
|
||||||
|
this.setState({
|
||||||
|
jackServerSettings: applied,
|
||||||
|
latencyText: getLatencyText(applied),
|
||||||
|
okEnabled: isOkEnabled(applied, this.state.alsaDevices)
|
||||||
|
});
|
||||||
|
this.applySettings(s);
|
||||||
|
})
|
||||||
|
.catch(() => { });
|
||||||
|
}
|
||||||
|
|
||||||
applyAlsaDevices(jackServerSettings: JackServerSettings, alsaDevices?: AlsaDeviceInfo[]) {
|
applyAlsaDevices(jackServerSettings: JackServerSettings, alsaDevices?: AlsaDeviceInfo[]) {
|
||||||
let result = jackServerSettings.clone();
|
let result = jackServerSettings.clone();
|
||||||
@@ -289,70 +419,94 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inDevice = alsaDevices.find(d => d.id === result.alsaInputDevice && d.supportsCapture);
|
let devices = filterDevices(alsaDevices);
|
||||||
let outDevice = alsaDevices.find(d => d.id === result.alsaOutputDevice && d.supportsPlayback);
|
let inDevice = devices.find(d => d.id === result.alsaInputDevice && d.supportsCapture);
|
||||||
if (!inDevice) {
|
let outDevice = devices.find(d => d.id === result.alsaOutputDevice && d.supportsPlayback);
|
||||||
inDevice = alsaDevices.find(d => d.supportsCapture);
|
|
||||||
if (inDevice) {
|
if (!inDevice || !outDevice) {
|
||||||
|
const capture = devices.filter(d => d.supportsCapture);
|
||||||
|
const playback = devices.filter(d => d.supportsPlayback);
|
||||||
|
if (capture.length === 1 && playback.length === 1) {
|
||||||
|
inDevice = capture[0];
|
||||||
|
outDevice = playback[0];
|
||||||
|
result.alsaInputDevice = inDevice.id;
|
||||||
|
result.alsaOutputDevice = outDevice.id;
|
||||||
|
} else if (capture.length === 1 && capture[0].supportsPlayback && playback.length === 0) {
|
||||||
|
inDevice = capture[0];
|
||||||
|
outDevice = capture[0];
|
||||||
result.alsaInputDevice = inDevice.id;
|
result.alsaInputDevice = inDevice.id;
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!outDevice) {
|
|
||||||
outDevice = alsaDevices.find(d => d.supportsPlayback);
|
|
||||||
if (outDevice) {
|
|
||||||
result.alsaOutputDevice = outDevice.id;
|
result.alsaOutputDevice = outDevice.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inDevice || !outDevice) {
|
if (!inDevice || !outDevice) {
|
||||||
result.valid = false;
|
result.valid = false;
|
||||||
|
if (!inDevice) result.alsaInputDevice = INVALID_DEVICE_ID;
|
||||||
|
if (!outDevice) result.alsaOutputDevice = INVALID_DEVICE_ID;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.sampleRate === 0) result.sampleRate = 48000;
|
let sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates);
|
||||||
let sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates);
|
if (sampleRates.length !== 0 && sampleRates.indexOf(result.sampleRate) === -1) {
|
||||||
if (sampleRates.length === 0) sampleRates = inDevice.sampleRates;
|
let bestSr = sampleRates[0];
|
||||||
let bestSr = sampleRates[0];
|
let bestErr = 1e36;
|
||||||
let bestErr = 1e36;
|
for (let sr of sampleRates) {
|
||||||
for (let sr of sampleRates) {
|
let err = (sr - result.sampleRate) * (sr - result.sampleRate);
|
||||||
let err = (sr - result.sampleRate) * (sr - result.sampleRate);
|
if (err < bestErr) { bestErr = err; bestSr = sr; }
|
||||||
if (err < bestErr) { bestErr = err; bestSr = sr; }
|
}
|
||||||
|
result.sampleRate = bestSr;
|
||||||
}
|
}
|
||||||
result.sampleRate = bestSr;
|
|
||||||
|
|
||||||
let bestBuffers = getBestBuffers(inDevice, outDevice, result.bufferSize, result.numberOfBuffers);
|
let bestBuffers = getBestBuffers(inDevice, outDevice, result.bufferSize, result.numberOfBuffers);
|
||||||
result.bufferSize = bestBuffers.bufferSize;
|
result.bufferSize = bestBuffers.bufferSize;
|
||||||
result.numberOfBuffers = bestBuffers.numberOfBuffers;
|
result.numberOfBuffers = bestBuffers.numberOfBuffers;
|
||||||
|
|
||||||
result.valid = !!result.alsaInputDevice && !!result.alsaOutputDevice;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
super.componentDidMount();
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
|
this.ignoreClose = false;
|
||||||
if (this.props.open) {
|
if (this.props.open) {
|
||||||
this.requestAlsaInfo();
|
this.requestAlsaInfo();
|
||||||
|
this.saveSettingsTemporary(this.props.jackServerSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(oldProps: JackServerSettingsDialogProps) {
|
componentDidUpdate(oldProps: JackServerSettingsDialogProps) {
|
||||||
if ((this.props.open && !oldProps.open) && this.mounted) {
|
if ((this.props.open && !oldProps.open) && this.mounted) {
|
||||||
let settings = this.applyAlsaDevices(this.props.jackServerSettings.clone(), this.state.alsaDevices);
|
this.ignoreClose = false;
|
||||||
this.setState({
|
this.saveSettingsTemporary(this.props.jackServerSettings);
|
||||||
|
// When opening, preserve the current settings until ALSA device
|
||||||
|
// information is loaded. If we don't have device info yet, just
|
||||||
|
// clone the provided settings without applying device defaults.
|
||||||
|
let settings = this.state.alsaDevices
|
||||||
|
? this.applyAlsaDevices(this.props.jackServerSettings.clone(), this.state.alsaDevices)
|
||||||
|
: this.props.jackServerSettings.clone();
|
||||||
|
|
||||||
|
this.setState({
|
||||||
jackServerSettings: settings,
|
jackServerSettings: settings,
|
||||||
latencyText: getLatencyText(settings),
|
latencyText: getLatencyText(settings),
|
||||||
okEnabled: isOkEnabled(settings,this.state.alsaDevices)
|
okEnabled: isOkEnabled(settings, this.state.alsaDevices)
|
||||||
});
|
});
|
||||||
if (!this.state.alsaDevices) {
|
if (!this.state.alsaDevices) {
|
||||||
this.requestAlsaInfo();
|
this.requestAlsaInfo();
|
||||||
}
|
}
|
||||||
}
|
} else if (!this.props.open && oldProps.open) {
|
||||||
|
this.originalJackServerSettings = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
super.componentWillUnmount();
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
|
if (this.originalJackServerSettings) {
|
||||||
|
// Revert any unapplied changes when the dialog is unmounted
|
||||||
|
this.applySettings(this.originalJackServerSettings);
|
||||||
|
this.originalJackServerSettings = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedDevice(deviceId: string): AlsaDeviceInfo | undefined {
|
getSelectedDevice(deviceId: string): AlsaDeviceInfo | undefined {
|
||||||
if (!this.state.alsaDevices) return undefined;
|
if (!this.state.alsaDevices) return undefined;
|
||||||
for (let i = 0; i < this.state.alsaDevices.length; ++i) {
|
for (let i = 0; i < this.state.alsaDevices.length; ++i) {
|
||||||
@@ -371,6 +525,7 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
if (!inDev && !outDev) return;
|
if (!inDev && !outDev) return;
|
||||||
let settings = this.state.jackServerSettings.clone();
|
let settings = this.state.jackServerSettings.clone();
|
||||||
settings.sampleRate = rate;
|
settings.sampleRate = rate;
|
||||||
|
settings.valid = false;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
jackServerSettings: settings,
|
jackServerSettings: settings,
|
||||||
@@ -386,7 +541,8 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
if (!inDev && !outDev) return;
|
if (!inDev && !outDev) return;
|
||||||
let settings = this.state.jackServerSettings.clone();
|
let settings = this.state.jackServerSettings.clone();
|
||||||
settings.bufferSize = size;
|
settings.bufferSize = size;
|
||||||
let bestBufferSetting = getBestBuffers(inDev,outDev,settings.bufferSize,settings.numberOfBuffers,true);
|
settings.valid = false;
|
||||||
|
let bestBufferSetting = getBestBuffers(inDev,outDev,settings.bufferSize,settings.numberOfBuffers,true);
|
||||||
settings.bufferSize = bestBufferSetting.bufferSize;
|
settings.bufferSize = bestBufferSetting.bufferSize;
|
||||||
settings.numberOfBuffers = bestBufferSetting.numberOfBuffers;
|
settings.numberOfBuffers = bestBufferSetting.numberOfBuffers;
|
||||||
|
|
||||||
@@ -404,6 +560,7 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
if (!inDev && !outDev) return;
|
if (!inDev && !outDev) return;
|
||||||
let settings = this.state.jackServerSettings.clone();
|
let settings = this.state.jackServerSettings.clone();
|
||||||
settings.numberOfBuffers = bufferCount;
|
settings.numberOfBuffers = bufferCount;
|
||||||
|
settings.valid = false;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
jackServerSettings: settings,
|
jackServerSettings: settings,
|
||||||
@@ -413,96 +570,177 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleApply() {
|
handleApply() {
|
||||||
if (this.state.okEnabled)
|
if (this.state.okEnabled) {
|
||||||
{
|
this.applySettings(); // Fire and forget
|
||||||
this.props.onApply(this.state.jackServerSettings.clone());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
handleInputDeviceChanged(e: any) {
|
|
||||||
const d = e.target.value as string;
|
handleOk() {
|
||||||
let s = this.state.jackServerSettings.clone();
|
if (!this.state.okEnabled) return;
|
||||||
s.alsaInputDevice = d;
|
|
||||||
this.setState({
|
const proceedWithOk = () => {
|
||||||
jackServerSettings: s,
|
const settings = this.state.jackServerSettings.clone();
|
||||||
okEnabled: isOkEnabled(s, this.state.alsaDevices)
|
settings.valid = true;
|
||||||
});
|
this.ignoreClose = true; // Indicate that the closing is intentional
|
||||||
}
|
this.applySettings(settings); // Fire and forget
|
||||||
|
this.saveSettings(settings); // Fire and forget
|
||||||
handleOutputDeviceChanged(e: any) {
|
this.originalJackServerSettings = undefined;
|
||||||
const d = e.target.value as string;
|
if (this.props.onApply) {
|
||||||
let s = this.state.jackServerSettings.clone();
|
this.props.onApply(settings.clone());
|
||||||
s.alsaOutputDevice = d;
|
}
|
||||||
this.setState({
|
this.props.onClose(); // Close the dialog
|
||||||
jackServerSettings: s,
|
};
|
||||||
okEnabled: isOkEnabled(s, this.state.alsaDevices)
|
|
||||||
});
|
if (this.state.jackServerSettings.alsaInputDevice !== this.state.jackServerSettings.alsaOutputDevice
|
||||||
}
|
&& !this.suppressDeviceWarning) {
|
||||||
render() {
|
this.setState({ showDeviceWarning: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
proceedWithOk();
|
||||||
|
};
|
||||||
|
|
||||||
|
handleWarningProceed() {
|
||||||
|
if (this.state.dontShowWarningAgain) {
|
||||||
|
localStorage.setItem("suppressSeparateDeviceWarning", "1");
|
||||||
|
this.suppressDeviceWarning = true;
|
||||||
|
}
|
||||||
|
this.setState({ showDeviceWarning: false, dontShowWarningAgain: false }, () => {
|
||||||
|
const settings = this.state.jackServerSettings.clone();
|
||||||
|
settings.valid = true;
|
||||||
|
this.ignoreClose = true;
|
||||||
|
this.applySettings(settings); // Fire and forget
|
||||||
|
this.saveSettings(settings); // Fire and forget
|
||||||
|
this.originalJackServerSettings = undefined;
|
||||||
|
if (this.props.onApply) {
|
||||||
|
this.props.onApply(settings.clone());
|
||||||
|
}
|
||||||
|
this.props.onClose(); // Close the dialog after the warning
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleWarningCancel() {
|
||||||
|
this.setState({ showDeviceWarning: false, dontShowWarningAgain: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleWarningCheck(e: any, checked: boolean) {
|
||||||
|
this.setState({ dontShowWarningAgain: checked });
|
||||||
|
}
|
||||||
|
handleInputDeviceChanged(e: any) {
|
||||||
|
const d = e.target.value as string;
|
||||||
|
let s = this.state.jackServerSettings.clone();
|
||||||
|
s.alsaInputDevice = d;
|
||||||
|
s.valid = false;
|
||||||
|
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
|
||||||
|
this.setState({
|
||||||
|
jackServerSettings: settings,
|
||||||
|
latencyText: getLatencyText(settings),
|
||||||
|
okEnabled: isOkEnabled(settings, this.state.alsaDevices)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOutputDeviceChanged(e: any) {
|
||||||
|
const d = e.target.value as string;
|
||||||
|
let s = this.state.jackServerSettings.clone();
|
||||||
|
s.alsaOutputDevice = d;
|
||||||
|
s.valid = false;
|
||||||
|
let settings = this.applyAlsaDevices(s, this.state.alsaDevices);
|
||||||
|
this.setState({
|
||||||
|
jackServerSettings: settings,
|
||||||
|
latencyText: getLatencyText(settings),
|
||||||
|
okEnabled: isOkEnabled(settings, this.state.alsaDevices)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
const classes = withStyles.getClasses(this.props);
|
const classes = withStyles.getClasses(this.props);
|
||||||
|
|
||||||
const { onClose, open } = this.props;
|
const { onClose, open } = this.props;
|
||||||
|
|
||||||
|
//Ignore close rutine if the ignoreclose is true. (After OK or Proceed or Multi Device Warning)
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
if (this.ignoreClose) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (this.originalJackServerSettings) {
|
||||||
|
// Revert any applied settings
|
||||||
|
this.applySettings(this.originalJackServerSettings); // Fire and forget
|
||||||
|
this.originalJackServerSettings = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
let selectedInputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice);
|
const sortedDevices = sortDevices(this.state.alsaDevices ?? []);
|
||||||
|
|
||||||
|
let selectedInputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice);
|
||||||
let selectedOutputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice);
|
let selectedOutputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice);
|
||||||
|
|
||||||
let bufferSizes: number[] = getValidBufferSizesMultiple(selectedInputDevice, selectedOutputDevice);
|
const devicesSelected = (selectedInputDevice && selectedOutputDevice);
|
||||||
let bufferCounts = getValidBufferCountsMultiple(this.state.jackServerSettings.bufferSize, selectedInputDevice, selectedOutputDevice);
|
|
||||||
let bufferSizeDisabled = !(selectedInputDevice || selectedOutputDevice);
|
let bufferSizes: number[] = devicesSelected ?
|
||||||
let bufferCountDisabled = !(selectedInputDevice || selectedOutputDevice);
|
getValidBufferSizesMultiple(selectedInputDevice, selectedOutputDevice) : [];
|
||||||
let sampleRates = selectedInputDevice && selectedOutputDevice ?
|
let bufferCounts = devicesSelected ?
|
||||||
|
getValidBufferCountsMultiple(this.state.jackServerSettings.bufferSize, selectedInputDevice, selectedOutputDevice) : [];
|
||||||
|
let bufferSizeDisabled = !devicesSelected;
|
||||||
|
let bufferCountDisabled = !devicesSelected;
|
||||||
|
let sampleRates = devicesSelected && selectedInputDevice && selectedOutputDevice ?
|
||||||
intersectArrays(selectedInputDevice.sampleRates, selectedOutputDevice.sampleRates)
|
intersectArrays(selectedInputDevice.sampleRates, selectedOutputDevice.sampleRates)
|
||||||
: (selectedInputDevice ? selectedInputDevice.sampleRates : (selectedOutputDevice ? selectedOutputDevice.sampleRates : []));
|
: [];
|
||||||
|
let sampleRateOptions = sampleRates.length === 0 && this.state.jackServerSettings.sampleRate ? [this.state.jackServerSettings.sampleRate] : sampleRates;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<DialogEx tag="jack" onClose={handleClose} aria-labelledby="select-channels-title" open={open}
|
<DialogEx tag="jack" onClose={handleClose} aria-labelledby="select-channels-title" open={open}
|
||||||
onEnterKey={() => {
|
onEnterKey={() => {
|
||||||
this.handleApply();
|
this.handleOk();
|
||||||
}}
|
}}
|
||||||
|
fullScreen={this.state.fullScreen}
|
||||||
|
|
||||||
>
|
>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<div style={{ display: "flex", alignItems: "center" }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
||||||
{/* Audio Input Device */}
|
<div style={{ display: "flex", flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||||
<FormControl className={classes.formControl}>
|
<div style={{ display: "flex", flexDirection: this.state.compactWidth ? "column" : "row", gap: 8 }}>
|
||||||
<InputLabel htmlFor="jsd_inputDevice">Input Device</InputLabel>
|
{/* Audio Input Device */}
|
||||||
<Select
|
<FormControl variant="standard" className={classes.formControl}>
|
||||||
id="jsd_inputDevice"
|
<InputLabel shrink className={classes.inputLabel} htmlFor="jsd_inputDevice">Input Device</InputLabel>
|
||||||
value={this.state.jackServerSettings.alsaInputDevice}
|
<Select variant="standard"
|
||||||
onChange={e => this.handleInputDeviceChanged(e)}
|
id="jsd_inputDevice"
|
||||||
disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0}
|
value={this.state.jackServerSettings.alsaInputDevice}
|
||||||
style={{ width: 220 }}
|
onChange={e => this.handleInputDeviceChanged(e)}
|
||||||
>
|
disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0}
|
||||||
{this.state.alsaDevices?.filter(d => d.supportsCapture).map(d => (
|
style={{ width: 220 }}
|
||||||
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
|
>
|
||||||
|
{sortedDevices.filter(d => d.supportsCapture).map(d => (
|
||||||
|
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
|
||||||
)) || <MenuItem value="" disabled>Loading...</MenuItem>}
|
)) || <MenuItem value="" disabled>Loading...</MenuItem>}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
{/* Audio Output Device */}
|
{/* Audio Output Device */}
|
||||||
<FormControl className={classes.formControl}>
|
<FormControl variant="standard" className={classes.formControl}>
|
||||||
<InputLabel htmlFor="jsd_outputDevice">Output Device</InputLabel>
|
<InputLabel shrink className={classes.inputLabel} htmlFor="jsd_outputDevice">Output Device</InputLabel>
|
||||||
<Select
|
<Select variant="standard"
|
||||||
id="jsd_outputDevice"
|
id="jsd_outputDevice"
|
||||||
value={this.state.jackServerSettings.alsaOutputDevice}
|
value={this.state.jackServerSettings.alsaOutputDevice}
|
||||||
onChange={e => this.handleOutputDeviceChanged(e)}
|
onChange={e => this.handleOutputDeviceChanged(e)}
|
||||||
disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0}
|
disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0}
|
||||||
style={{ width: 220 }}
|
style={{ width: 220 }}
|
||||||
>
|
>
|
||||||
{this.state.alsaDevices?.filter(d => d.supportsPlayback).map(d => (
|
{sortedDevices.filter(d => d.supportsPlayback).map(d => (
|
||||||
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
|
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
|
||||||
)) || <MenuItem value="" disabled>Loading...</MenuItem>}
|
)) || <MenuItem value="" disabled>Loading...</MenuItem>}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<IconButtonEx tooltip="Refresh devices" onClick={() => this.requestAlsaInfo()} aria-label="refresh-audio-devices">
|
</div>
|
||||||
<RefreshIcon />
|
<IconButtonEx tooltip="Refresh devices" onClick={() => this.requestAlsaInfo()} aria-label="refresh-audio-devices">
|
||||||
</IconButtonEx>
|
<RefreshIcon />
|
||||||
|
</IconButtonEx>
|
||||||
|
</div>
|
||||||
</div><div>
|
</div><div>
|
||||||
<FormControl className={classes.formControl}>
|
<FormControl variant="standard" className={classes.formControl}>
|
||||||
<InputLabel htmlFor="jsd_sampleRate">Sample rate</InputLabel>
|
<InputLabel shrink className={classes.inputLabel} htmlFor="jsd_sampleRate">Sample rate</InputLabel>
|
||||||
<Select variant="standard"
|
<Select variant="standard"
|
||||||
onChange={(e) => this.handleRateChanged(e)}
|
onChange={(e) => this.handleRateChanged(e)}
|
||||||
value={this.state.jackServerSettings.sampleRate}
|
value={this.state.jackServerSettings.sampleRate}
|
||||||
@@ -514,14 +752,14 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{sampleRates.map((sr) => {
|
{sampleRateOptions.map((sr) => {
|
||||||
return (<MenuItem value={sr}>{sr}</MenuItem> );
|
return (<MenuItem value={sr}>{sr}</MenuItem> );
|
||||||
})}
|
})}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<div style={{ display: "inline", whiteSpace: "nowrap" }}>
|
<div style={{ display: "inline", whiteSpace: "nowrap" }}>
|
||||||
<FormControl className={classes.formControl}>
|
<FormControl variant="standard" className={classes.formControl}>
|
||||||
<InputLabel htmlFor="bufferSize">Buffer size</InputLabel>
|
<InputLabel shrink className={classes.inputLabel} htmlFor="bufferSize">Buffer size</InputLabel>
|
||||||
<Select variant="standard"
|
<Select variant="standard"
|
||||||
onChange={(e) => this.handleSizeChanged(e)}
|
onChange={(e) => this.handleSizeChanged(e)}
|
||||||
value={this.state.jackServerSettings.bufferSize}
|
value={this.state.jackServerSettings.bufferSize}
|
||||||
@@ -539,8 +777,8 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
)}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl className={classes.formControl}>
|
<FormControl variant="standard" className={classes.formControl}>
|
||||||
<InputLabel htmlFor="numberofBuffers">Buffers</InputLabel>
|
<InputLabel shrink className={classes.inputLabel} htmlFor="numberofBuffers">Buffers</InputLabel>
|
||||||
<Select variant="standard"
|
<Select variant="standard"
|
||||||
onChange={(e) => this.handleNumberOfBuffersChanged(e)}
|
onChange={(e) => this.handleNumberOfBuffersChanged(e)}
|
||||||
value={this.state.jackServerSettings.numberOfBuffers}
|
value={this.state.jackServerSettings.numberOfBuffers}
|
||||||
@@ -570,16 +808,39 @@ const JackServerSettingsDialog = withStyles(
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button variant="dialogSecondary" onClick={handleClose} >
|
<Button variant="dialogSecondary" onClick={handleClose}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="dialogPrimary" onClick={() => this.handleApply()}
|
<Button variant="dialogSecondary" onClick={() => this.handleApply()} disabled={!this.state.okEnabled}>Apply</Button>
|
||||||
disabled={!this.state.okEnabled}>
|
<Button variant="dialogPrimary" onClick={() => this.handleOk()} disabled={!this.state.okEnabled}>
|
||||||
OK
|
OK
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
|
||||||
</DialogEx>
|
</DialogEx>
|
||||||
|
{this.state.showDeviceWarning && (
|
||||||
|
<DialogEx open={this.state.showDeviceWarning} tag="ioWarning"
|
||||||
|
onEnterKey={() => this.handleWarningProceed()}
|
||||||
|
onClose={() => this.handleWarningCancel()}
|
||||||
|
style={{ userSelect: "none" }}
|
||||||
|
>
|
||||||
|
<DialogContent>
|
||||||
|
<Typography variant="body2" color="textPrimary" gutterBottom>
|
||||||
|
Using different input and output devices may cause issues when streaming audio.
|
||||||
|
Are you sure you want to continue?
|
||||||
|
</Typography>
|
||||||
|
<FormControlLabel
|
||||||
|
control={<Checkbox checked={this.state.dontShowWarningAgain} onChange={(e,c)=>this.handleWarningCheck(e,c)} />}
|
||||||
|
label="Don't show me this message again"
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => this.handleWarningCancel()} variant="dialogSecondary" style={{ width: 120 }}>Cancel</Button>
|
||||||
|
<Button onClick={() => this.handleWarningProceed()} variant="dialogPrimary" style={{ width: 120 }}>PROCEED</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</DialogEx>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ const ZoomedDial = withStyles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onBodyPointerDownCapture(e_: any): any {
|
onBodyPointerDownCapture(e_: any): any {
|
||||||
let e = e_ as PointerEvent;
|
let e = e_ as PointerEvent;
|
||||||
if (this.isExtraTouch(e)) {
|
if (this.isExtraTouch(e)) {
|
||||||
this.captureElement!.setPointerCapture(e.pointerId);
|
this.captureElement!.setPointerCapture(e.pointerId);
|
||||||
|
|||||||
Reference in New Issue
Block a user