+8
-4
@@ -8,9 +8,13 @@ icon_float: left
|
||||
|
||||
Before using PiPedal, you will need to configure settings for the audio device that PiPedal will use.
|
||||
|
||||
PiPedal works with either external USB audio interfaces or Raspberry Pi audio HATs. You can now
|
||||
select a distinct ALSA input device and output device, so recording and playback can use different
|
||||
hardware if desired.
|
||||
|
||||
{% include pageIconL.html %}
|
||||
|
||||
You will also want to configure PiPedal to provide a Wi-Fi auto-hotspot so that you can connect to using your using your Android phone or tablet. It's fine to use your home Wi-Fi network to connect to PiPedal when you're at home; but don't forget that when you take PiPedal out to a gig, you will need to ensure that PiPedal's Wi-Fi auto-hotspot is enabled before you do.
|
||||
You will also want to configure PiPedal to provide a Wi-Fi auto-hotspot so that you can connect to it using your Android phone or tablet. It's fine to use your home Wi-Fi network to connect to PiPedal when you're at home; but don't forget that when you take PiPedal out to a gig, you will need to ensure that PiPedal's Wi-Fi auto-hotspot is enabled before you do.
|
||||
|
||||
PiPedal uses LV2 audio plugins. There are literally thousands of freely available high-quality LV2 plugins that are suitable for use as guitar effects.
|
||||
|
||||
@@ -21,9 +25,9 @@ with PiPedal.
|
||||
|
||||
### Installing the PiPedal Remote Android app
|
||||
|
||||
If you are using an Android phone or table to connect to PiPedal, you should download and install the [PiPedal Android Client](https://play.google.com/store/apps/details?id=com.twoplay.pipedal)
|
||||
If you are using an Android phone or tablet to connect to PiPedal, you should download and install the [PiPedal Android Client](https://play.google.com/store/apps/details?id=com.twoplay.pipedal)
|
||||
|
||||
The Android PiPedal client provides three feature:
|
||||
The Android PiPedal client provides three features:
|
||||
|
||||
1. It automatically locates running instances of PiPedal on the local Wi-Fi network using dns/Bonjour discovery. So you don't need to keep track of which IP address to connect with.
|
||||
|
||||
@@ -72,7 +76,7 @@ an audio device. Many external USB audio devices that have two inputs provide th
|
||||
|
||||
#### Selecting Audio Buffer Sizes
|
||||
|
||||
You may have to experiment to find the buffer size, and buffer count that works best for your. The actual round-trip latency and frequency of audio overruns and underruns depends the the operating system system version, the audio hardware being used, and upon CPU use of the audio patches you are using. Selecting larger buffer sizes or larger buffer counts increase the amount of computing power available for your audio effects, and will reduce the number of dropouts caused by audio overruns/underruns (xruns); smaller buffer sizes and buffer counts improve the overall round-trip latency but will increase the likelihood of xruns.
|
||||
You may have to experiment to find the buffer size and buffer count that works best for you. The actual round-trip latency and frequency of audio overruns and underruns depends on the operating system version, the audio hardware being used, and upon CPU use of the audio patches you are using. Selecting larger buffer sizes or larger buffer counts increases the amount of computing power available for your audio effects and will reduce the number of dropouts caused by audio overruns/underruns (xruns); smaller buffer sizes and buffer counts improve the overall round-trip latency but will increase the likelihood of xruns.
|
||||
|
||||
Please note that the Raspberry Pi OS is not completely robust with respect to realtime scheduling. Significant display activity, SD-CARD activity or heavy CPU use by programs other than PiPedal may cause audio xruns. For best results, run PiPedal without a display attached, and with no other programs running. Connecting remotely to the PiPedal web interface should not cause problems.
|
||||
|
||||
|
||||
+17
-14
@@ -20,7 +20,7 @@
|
||||
* LIABILITY, WHETHER 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.
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
#include "util.hpp"
|
||||
@@ -1223,7 +1223,9 @@ namespace pipedal
|
||||
{
|
||||
int err;
|
||||
|
||||
alsa_device_name = jackServerSettings.GetAlsaInputDevice();
|
||||
std::string inputName = jackServerSettings.GetAlsaInputDevice();
|
||||
std::string outputName = jackServerSettings.GetAlsaOutputDevice();
|
||||
|
||||
|
||||
this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers();
|
||||
this->bufferSize = jackServerSettings.GetBufferSize();
|
||||
@@ -1232,7 +1234,7 @@ namespace pipedal
|
||||
try
|
||||
{
|
||||
|
||||
err = snd_pcm_open(&playbackHandle, alsa_device_name.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)
|
||||
{
|
||||
switch (errno)
|
||||
@@ -1267,7 +1269,7 @@ namespace pipedal
|
||||
snd_pcm_nonblock(playbackHandle, 0);
|
||||
}
|
||||
|
||||
err = snd_pcm_open(&captureHandle, alsa_device_name.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)
|
||||
{
|
||||
@@ -1934,7 +1936,8 @@ namespace pipedal
|
||||
}
|
||||
}};
|
||||
|
||||
std::string alsaDeviceName = jackServerSettings.GetAlsaInputDevice();
|
||||
std::string inDev = jackServerSettings.GetAlsaInputDevice();
|
||||
std::string outDev = jackServerSettings.GetAlsaOutputDevice();
|
||||
bool result = false;
|
||||
|
||||
try
|
||||
@@ -1942,7 +1945,7 @@ namespace pipedal
|
||||
int err;
|
||||
for (int retry = 0; retry < 2; ++retry)
|
||||
{
|
||||
err = snd_pcm_open(&playbackHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
err = snd_pcm_open(&playbackHandle, outDev.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
if (err < 0) // field report of a device that is present, but won't immediately open.
|
||||
{
|
||||
sleep(1);
|
||||
@@ -1952,13 +1955,13 @@ namespace pipedal
|
||||
}
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " playback device not found. "
|
||||
<< "(" << snd_strerror(err) << ")"));
|
||||
throw PiPedalStateException(SS(outDev << " playback device not found. "
|
||||
<< "(" << snd_strerror(err) << ")"));
|
||||
}
|
||||
|
||||
for (int retry = 0; retry < 15; ++retry)
|
||||
{
|
||||
err = snd_pcm_open(&captureHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||
err = snd_pcm_open(&captureHandle, inDev.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||
if (err == -EBUSY)
|
||||
{
|
||||
sleep(1);
|
||||
@@ -1967,7 +1970,7 @@ namespace pipedal
|
||||
break;
|
||||
}
|
||||
if (err < 0)
|
||||
throw PiPedalStateException(SS(alsaDeviceName << " capture device not found."));
|
||||
throw PiPedalStateException(SS(inDev << " capture device not found."));
|
||||
|
||||
if (snd_pcm_hw_params_malloc(&playbackHwParams) < 0)
|
||||
{
|
||||
@@ -1981,8 +1984,8 @@ namespace pipedal
|
||||
snd_pcm_hw_params_any(playbackHandle, playbackHwParams);
|
||||
snd_pcm_hw_params_any(captureHandle, captureHwParams);
|
||||
|
||||
SetPreferredAlsaFormat(alsaDeviceName, "capture", captureHandle, captureHwParams);
|
||||
SetPreferredAlsaFormat(alsaDeviceName, "output", playbackHandle, playbackHwParams);
|
||||
SetPreferredAlsaFormat(inDev, "capture", captureHandle, captureHwParams);
|
||||
SetPreferredAlsaFormat(outDev, "output", playbackHandle, playbackHwParams);
|
||||
|
||||
unsigned int sampleRate = jackServerSettings.GetSampleRate();
|
||||
err = snd_pcm_hw_params_set_rate_near(playbackHandle, playbackHwParams, &sampleRate, 0);
|
||||
@@ -2002,13 +2005,13 @@ namespace pipedal
|
||||
err = snd_pcm_hw_params_get_channels_max(playbackHwParams, &playbackChannels);
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalLogicException("No outut channels.");
|
||||
throw PiPedalLogicException("No output channels.");
|
||||
}
|
||||
unsigned int channelsMin;
|
||||
err = snd_pcm_hw_params_get_channels_min(playbackHwParams, &channelsMin);
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalLogicException("No outut channels.");
|
||||
throw PiPedalLogicException("No output channels.");
|
||||
}
|
||||
if (ShouldForceStereoChannels(playbackHandle, playbackHwParams, channelsMin, playbackChannels))
|
||||
{
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
* LIABILITY, WHETHER 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.
|
||||
*/
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -517,6 +517,8 @@ namespace pipedal
|
||||
result.sampleRates_.push_back(48000);
|
||||
result.minBufferSize_ = 16;
|
||||
result.maxBufferSize_ = 1024;
|
||||
result.supportsCapture_ = true;
|
||||
result.supportsPlayback_ = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace pipedal
|
||||
return outputAudioPorts_;
|
||||
}
|
||||
|
||||
// replaced with AlsaSequencerConfiguration
|
||||
// replaced with AlsaSequencerConfiguration
|
||||
const std::vector<AlsaMidiDeviceInfo>& LegacyGetInputMidiDevices() const
|
||||
{
|
||||
return inputMidiDevices_;
|
||||
|
||||
@@ -148,7 +148,13 @@ void JackServerSettings::ReadJackDaemonConfiguration()
|
||||
this->bufferSize_ = GetJackArg(argv, "-p", "--period");
|
||||
this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "--nperiods");
|
||||
this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "--rate");
|
||||
this->alsaDevice_ = GetJackStringArg(argv,"-d", "--device");
|
||||
// read new dual device flags, fallback on old -d/--device
|
||||
std::string capDev = GetJackStringArg(argv, "-C", "--capture");
|
||||
std::string playDev = GetJackStringArg(argv, "-P", "--playback");
|
||||
std::string dev = "";
|
||||
try { dev = GetJackStringArg(argv, "-d", "--device"); } catch(...) {}
|
||||
this->alsaInputDevice_ = capDev.empty() ? dev : capDev;
|
||||
this->alsaOutputDevice_ = playDev.empty() ? dev : playDev;
|
||||
this->valid_ = true;
|
||||
}
|
||||
catch (std::exception &)
|
||||
@@ -219,11 +225,13 @@ void JackServerSettings::WriteDaemonConfig()
|
||||
{
|
||||
output << line << endl;
|
||||
}
|
||||
// the style used by qjackctl. :-/
|
||||
// the style used by qjackctl. :-/
|
||||
// Lower to -P70 in order to allow the USB soft-irq to run at higher priority than JACK (it runs at 80).
|
||||
output << "/usr/bin/jackd "
|
||||
<< "-R -P70 --silent"
|
||||
<< " -dalsa -d" << this->alsaDevice_
|
||||
<< " -dalsa"
|
||||
<< " -C" << this->GetAlsaInputDevice()
|
||||
<< " -P" << this->GetAlsaOutputDevice()
|
||||
<< " -r" << this->sampleRate_
|
||||
<< " -p" << this->bufferSize_
|
||||
<< " -n" << this->numberOfBuffers_ << " -Xseq"
|
||||
@@ -246,7 +254,8 @@ JSON_MAP_REFERENCE(JackServerSettings, valid)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, isOnboarding)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, rebootRequired)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, isJackAudio)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, alsaDevice)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, alsaInputDevice)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDevice)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, sampleRate)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, bufferSize)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, numberOfBuffers)
|
||||
|
||||
+18
-11
@@ -31,18 +31,24 @@ namespace pipedal
|
||||
bool isOnboarding_ = true;
|
||||
bool isJackAudio_ = JACK_HOST ? true : false;
|
||||
bool rebootRequired_ = false;
|
||||
std::string alsaDevice_;
|
||||
std::string alsaInputDevice_;
|
||||
std::string alsaOutputDevice_;
|
||||
uint64_t sampleRate_ = 0;
|
||||
uint32_t bufferSize_ = 64;
|
||||
uint32_t numberOfBuffers_ = 3;
|
||||
|
||||
public:
|
||||
void SetAlsaInputDevice(const std::string &d){ alsaInputDevice_ = d; }
|
||||
void SetAlsaOutputDevice(const std::string &d){ alsaOutputDevice_ = d; }
|
||||
JackServerSettings();
|
||||
JackServerSettings(
|
||||
JackServerSettings(
|
||||
const std::string alsaInputDevice,
|
||||
uint64_t sampleRate, uint32_t bufferSize, uint32_t numberOfBuffers)
|
||||
uint64_t sampleRate,
|
||||
uint32_t bufferSize,
|
||||
uint32_t numberOfBuffers)
|
||||
: valid_(true),
|
||||
alsaDevice_(alsaInputDevice),
|
||||
alsaInputDevice_(alsaInputDevice),
|
||||
alsaOutputDevice_(alsaInputDevice), // default same
|
||||
sampleRate_(sampleRate),
|
||||
bufferSize_(bufferSize),
|
||||
numberOfBuffers_(numberOfBuffers),
|
||||
@@ -53,16 +59,17 @@ namespace pipedal
|
||||
uint64_t GetSampleRate() const { return sampleRate_; }
|
||||
uint32_t GetBufferSize() const { return bufferSize_; }
|
||||
uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; }
|
||||
const std::string &GetAlsaInputDevice() const { return alsaDevice_; }
|
||||
const std::string &GetAlsaInputDevice() const { return alsaInputDevice_; }
|
||||
const std::string &GetAlsaOutputDevice() const { return alsaOutputDevice_; }
|
||||
void UseDummyAudioDevice() {
|
||||
this->valid_ = true;
|
||||
if (sampleRate_ == 0) sampleRate_ = 48000;
|
||||
this->alsaDevice_ = "dummy:channels_2";
|
||||
this->alsaInputDevice_ = "__DUMMY_AUDIO__dummy:channels_2";
|
||||
this->alsaOutputDevice_ = "__DUMMY_AUDIO__dummy:channels_2";
|
||||
}
|
||||
bool IsDummyAudioDevice() const {
|
||||
return
|
||||
this->alsaDevice_.starts_with("__DUMMY_AUDIO__")
|
||||
|| this->alsaDevice_.starts_with("dummy:");
|
||||
return this->alsaInputDevice_.starts_with("__DUMMY_AUDIO__")
|
||||
|| this->alsaOutputDevice_.starts_with("__DUMMY_AUDIO__");
|
||||
}
|
||||
|
||||
void ReadJackDaemonConfiguration();
|
||||
@@ -75,7 +82,7 @@ namespace pipedal
|
||||
// this->rebootRequired_ = true;
|
||||
// this->sampleRate_ = sampleRate;
|
||||
// this->bufferSize_ = bufferSize;
|
||||
// this->numberOfBuffers_ = numberOfBuffers;
|
||||
// this->numberOfBuffers_ = numberOfBuffers;
|
||||
// }
|
||||
void WriteDaemonConfig(); // requires root perms.
|
||||
void SetRebootRequired(bool value)
|
||||
@@ -89,7 +96,7 @@ namespace pipedal
|
||||
|
||||
bool Equals(const JackServerSettings &other)
|
||||
{
|
||||
return this->alsaDevice_ == other.alsaDevice_ && this->sampleRate_ == other.sampleRate_ && this->bufferSize_ == other.bufferSize_ && this->numberOfBuffers_ == other.numberOfBuffers_;
|
||||
return this->alsaInputDevice_ == other.alsaInputDevice_ && this->alsaOutputDevice_ == other.alsaOutputDevice_ && this->sampleRate_ == other.sampleRate_ && this->bufferSize_ == other.bufferSize_ && this->numberOfBuffers_ == other.numberOfBuffers_;
|
||||
}
|
||||
|
||||
DECLARE_JSON_MAP(JackServerSettings);
|
||||
|
||||
+24
-16
@@ -97,20 +97,16 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
||||
info.name_ = snd_ctl_card_info_get_name(alsaInfo);
|
||||
info.longName_ = snd_ctl_card_info_get_longname(alsaInfo);
|
||||
|
||||
snd_pcm_t *hDevice = nullptr;
|
||||
snd_pcm_t *captureDevice = nullptr;
|
||||
snd_pcm_t *playbackDevice = nullptr;
|
||||
bool captureOk = snd_pcm_open(&captureDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, 0) == 0;
|
||||
bool playbackOk = snd_pcm_open(&playbackDevice, cardId.c_str(), SND_PCM_STREAM_PLAYBACK, 0) == 0;
|
||||
info.supportsCapture_ = captureOk;
|
||||
info.supportsPlayback_ = playbackOk;
|
||||
|
||||
// must support capture AND playback
|
||||
err = snd_pcm_open(&hDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, 0);
|
||||
if (err == 0)
|
||||
{
|
||||
snd_pcm_close(hDevice);
|
||||
}
|
||||
if (err == 0)
|
||||
{
|
||||
err = snd_pcm_open(&hDevice, cardId.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
}
|
||||
if (err == 0)
|
||||
if (captureOk || playbackOk)
|
||||
{
|
||||
snd_pcm_t *hDevice = captureOk ? captureDevice : playbackDevice;
|
||||
snd_pcm_hw_params_t *params = nullptr;
|
||||
err = snd_pcm_hw_params_malloc(¶ms);
|
||||
if (err == 0)
|
||||
@@ -151,15 +147,25 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
|
||||
|
||||
info.minBufferSize_ = (uint32_t)minBufferSize;
|
||||
info.maxBufferSize_ = (uint32_t)maxBufferSize;
|
||||
cacheDevice(info.name_, info);
|
||||
result.push_back(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (params != nullptr)
|
||||
snd_pcm_hw_params_free(params);
|
||||
snd_pcm_close(hDevice);
|
||||
}
|
||||
if (captureOk)
|
||||
snd_pcm_close(captureDevice);
|
||||
if (playbackOk && playbackDevice != captureDevice)
|
||||
snd_pcm_close(playbackDevice);
|
||||
if (err == 0)
|
||||
{
|
||||
cacheDevice(info.name_, info);
|
||||
result.push_back(info);
|
||||
}
|
||||
else if (getCachedDevice(info.name_, &info))
|
||||
{
|
||||
result.push_back(info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getCachedDevice(info.name_, &info))
|
||||
@@ -480,6 +486,8 @@ JSON_MAP_REFERENCE(AlsaDeviceInfo, longName)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, sampleRates)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, minBufferSize)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, maxBufferSize)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, supportsCapture)
|
||||
JSON_MAP_REFERENCE(AlsaDeviceInfo, supportsPlayback)
|
||||
JSON_MAP_END()
|
||||
|
||||
JSON_MAP_BEGIN(AlsaMidiDeviceInfo)
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace pipedal {
|
||||
std::string longName_;
|
||||
std::vector<uint32_t> sampleRates_;
|
||||
uint32_t minBufferSize_ = 0,maxBufferSize_ = 0;
|
||||
bool supportsCapture_ = false;
|
||||
bool supportsPlayback_ = false;
|
||||
|
||||
bool isDummyDevice() const {
|
||||
return id_.starts_with("dummy:");
|
||||
|
||||
@@ -457,6 +457,7 @@ std::string uri_builder::str() const {
|
||||
s << '?';
|
||||
for (size_t i = 0; i < queries_.size(); ++i)
|
||||
{
|
||||
if (i != 0) s << '&';
|
||||
s << queries_[i].key << "=";
|
||||
HtmlHelper::encode_url_segment(s,queries_[i].value,true);
|
||||
}
|
||||
|
||||
+1
-1
@@ -204,7 +204,7 @@ public:
|
||||
bool has_query(const std::string &key) const {
|
||||
for (size_t i = 0; i < queries_.size(); ++i)
|
||||
{
|
||||
if (queries_[i].key == key) true;
|
||||
if (queries_[i].key == key) return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
|
||||
|
||||
|
||||
// forward declaration.
|
||||
// forward declaration.
|
||||
|
||||
class websocket_session;
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ export default class AlsaDeviceInfo {
|
||||
this.sampleRates = input.sampleRates as number[];
|
||||
this.minBufferSize = input.minBufferSize;
|
||||
this.maxBufferSize = input.maxBufferSize;
|
||||
this.supportsCapture = input.supportsCapture ? true : false;
|
||||
this.supportsPlayback = input.supportsPlayback ? true : false;
|
||||
return this;
|
||||
}
|
||||
static deserialize_array(input: any): AlsaDeviceInfo[]
|
||||
@@ -62,5 +64,7 @@ export default class AlsaDeviceInfo {
|
||||
longName: string = "";
|
||||
sampleRates: number[] = [];
|
||||
minBufferSize: number = 0;
|
||||
maxBufferSize: number = 0;
|
||||
maxBufferSize: number = 0;
|
||||
supportsCapture: boolean = false;
|
||||
supportsPlayback: boolean = false;
|
||||
};
|
||||
@@ -25,7 +25,8 @@ export default class JackServerSettings {
|
||||
this.isOnboarding = input.isOnboarding;
|
||||
this.isJackAudio = input.isJackAudio;
|
||||
this.rebootRequired = input.rebootRequired;
|
||||
this.alsaDevice = input.alsaDevice?? "";
|
||||
this.alsaInputDevice = input.alsaInputDevice ?? "";
|
||||
this.alsaOutputDevice = input.alsaOutputDevice ?? "";
|
||||
this.sampleRate = input.sampleRate;
|
||||
this.bufferSize = input.bufferSize;
|
||||
this.numberOfBuffers = input.numberOfBuffers;
|
||||
@@ -39,7 +40,7 @@ export default class JackServerSettings {
|
||||
// if (numberOfBuffers) {
|
||||
// this.valid = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
clone(): JackServerSettings
|
||||
{
|
||||
return new JackServerSettings().deserialize(this);
|
||||
@@ -48,16 +49,18 @@ export default class JackServerSettings {
|
||||
isOnboarding: boolean = true;
|
||||
rebootRequired = false;
|
||||
isJackAudio = false;
|
||||
alsaDevice: string = "";
|
||||
alsaInputDevice: string = "";
|
||||
alsaOutputDevice: string = "";
|
||||
sampleRate = 48000;
|
||||
bufferSize = 64;
|
||||
numberOfBuffers = 3;
|
||||
|
||||
getSummaryText() {
|
||||
if (this.valid) {
|
||||
let device = this.alsaDevice;
|
||||
if (device.startsWith("hw:")) device = device.substring(3);
|
||||
return device + " - Rate: " + this.sampleRate + " BufferSize: " + this.bufferSize + " Buffers: " + 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;
|
||||
return "In: "+inDev+" Out: "+outDev+" — Rate "+this.sampleRate+", "+this.bufferSize+"×"+this.numberOfBuffers;
|
||||
|
||||
} else {
|
||||
return "Not configured";
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// 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
|
||||
// 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';
|
||||
@@ -40,6 +40,8 @@ import DialogContent from '@mui/material/DialogContent';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
|
||||
import AlsaDeviceInfo from './AlsaDeviceInfo';
|
||||
|
||||
@@ -114,6 +116,22 @@ function getValidBufferCounts(bufferSize: number, alsaDeviceInfo?: AlsaDeviceInf
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function intersectArrays(a: number[], b: number[]): number[] {
|
||||
return a.filter((v) => b.indexOf(v) !== -1);
|
||||
}
|
||||
|
||||
function getValidBufferCountsMultiple(bufferSize: number, inDevice?: AlsaDeviceInfo, outDevice?: AlsaDeviceInfo): number[] {
|
||||
let c1 = getValidBufferCounts(bufferSize, inDevice);
|
||||
if (!outDevice) return c1;
|
||||
return intersectArrays(c1, getValidBufferCounts(bufferSize, outDevice));
|
||||
}
|
||||
|
||||
function getValidBufferSizesMultiple(inDevice?: AlsaDeviceInfo, outDevice?: AlsaDeviceInfo): number[] {
|
||||
let s1 = getValidBufferSizes(inDevice);
|
||||
if (!outDevice) return s1;
|
||||
return intersectArrays(s1, getValidBufferSizes(outDevice));
|
||||
}
|
||||
function getValidBufferSizes(alsaDeviceInfo? : AlsaDeviceInfo): number[]
|
||||
{
|
||||
if (!alsaDeviceInfo )
|
||||
@@ -131,23 +149,31 @@ function getValidBufferSizes(alsaDeviceInfo? : AlsaDeviceInfo): number[]
|
||||
return result;
|
||||
}
|
||||
function getBestBuffers(
|
||||
alsaDeviceInfo: AlsaDeviceInfo | undefined,
|
||||
bufferSize: number,
|
||||
inDevice: AlsaDeviceInfo | undefined,
|
||||
outDevice: AlsaDeviceInfo | undefined,
|
||||
bufferSize: number,
|
||||
numberOfBuffers: number,
|
||||
bufferSizeChanging: boolean = false
|
||||
) : BufferSetting {
|
||||
if (!alsaDeviceInfo)
|
||||
if (!inDevice && !outDevice)
|
||||
{
|
||||
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.
|
||||
// set default values. Otherwise, choose the best buffer count from available buffer counts.
|
||||
let validBuffercounts = getValidBufferCounts(bufferSize,alsaDeviceInfo);
|
||||
let validBuffercounts = getValidBufferCountsMultiple(bufferSize,inDevice,outDevice);
|
||||
const minSize = Math.max(inDevice?.minBufferSize ?? MIN_BUFFER_SIZE, outDevice?.minBufferSize ?? MIN_BUFFER_SIZE);
|
||||
const maxSize = Math.min(inDevice?.maxBufferSize ?? MAX_BUFFER_SIZE, outDevice?.maxBufferSize ?? MAX_BUFFER_SIZE);
|
||||
let ix = validBuffercounts.indexOf(numberOfBuffers);
|
||||
if (ix !== -1) {
|
||||
if (bufferSize*numberOfBuffers <= alsaDeviceInfo.maxBufferSize
|
||||
&& bufferSize*numberOfBuffers >= alsaDeviceInfo.minBufferSize)
|
||||
if (bufferSize*numberOfBuffers <= maxSize
|
||||
&& bufferSize*numberOfBuffers >= minSize)
|
||||
{
|
||||
return {bufferSize: bufferSize, numberOfBuffers: numberOfBuffers};
|
||||
}
|
||||
@@ -174,15 +200,15 @@ function getBestBuffers(
|
||||
// otherwise select a sensible starting value.
|
||||
|
||||
// favored default: 64x3.
|
||||
if (64*3 >= alsaDeviceInfo.minBufferSize && 64*3 <= alsaDeviceInfo.maxBufferSize)
|
||||
if (64*3 >= minSize && 64*3 <= maxSize)
|
||||
{
|
||||
return { bufferSize: 64, numberOfBuffers: 3};
|
||||
}
|
||||
// if that isn't possible then minBufferSize/2 x 4.
|
||||
bufferSize = alsaDeviceInfo.minBufferSize/2;
|
||||
bufferSize = minSize/2;
|
||||
numberOfBuffers = 4;
|
||||
// otherwise, minBufferSize/2 x 2.
|
||||
if (bufferSize*numberOfBuffers > alsaDeviceInfo.maxBufferSize)
|
||||
if (bufferSize*numberOfBuffers > maxSize)
|
||||
{
|
||||
numberOfBuffers = 2;
|
||||
}
|
||||
@@ -194,29 +220,21 @@ function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaD
|
||||
{
|
||||
if (!jackServerSettings.valid) return false;
|
||||
if (!alsaDevices) return false;
|
||||
let alsaDevice: AlsaDeviceInfo | undefined = undefined;
|
||||
for (let i = 0; i < alsaDevices.length; ++i)
|
||||
{
|
||||
if (alsaDevices[i].id === jackServerSettings.alsaDevice)
|
||||
{
|
||||
alsaDevice = alsaDevices[i];
|
||||
}
|
||||
}
|
||||
if (!alsaDevice)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let deviceBufferSize = jackServerSettings.bufferSize * jackServerSettings.numberOfBuffers;
|
||||
if (deviceBufferSize < alsaDevice.minBufferSize || deviceBufferSize > alsaDevice.maxBufferSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let validBufferCounts = getValidBufferCounts(jackServerSettings.bufferSize, alsaDevice);
|
||||
let ix = validBufferCounts.indexOf(jackServerSettings.numberOfBuffers);
|
||||
if (ix === -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const inDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaInputDevice && d.supportsCapture);
|
||||
const outDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaOutputDevice && d.supportsPlayback);
|
||||
if (!inDevice || !outDevice) return false;
|
||||
|
||||
const deviceBufferSize = jackServerSettings.bufferSize * jackServerSettings.numberOfBuffers;
|
||||
const minSize = Math.max(inDevice.minBufferSize, outDevice.minBufferSize);
|
||||
const maxSize = Math.min(inDevice.maxBufferSize, outDevice.maxBufferSize);
|
||||
if (deviceBufferSize < minSize || deviceBufferSize > maxSize) return false;
|
||||
|
||||
const validBufferCounts = getValidBufferCountsMultiple(jackServerSettings.bufferSize, inDevice, outDevice);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -264,35 +282,49 @@ const JackServerSettingsDialog = withStyles(
|
||||
|
||||
applyAlsaDevices(jackServerSettings: JackServerSettings, alsaDevices?: AlsaDeviceInfo[]) {
|
||||
let result = jackServerSettings.clone();
|
||||
if (!alsaDevices) {
|
||||
return result;
|
||||
}
|
||||
result.valid = false;
|
||||
if (alsaDevices.length === 0) {
|
||||
result.alsaDevice = INVALID_DEVICE_ID;
|
||||
if (!alsaDevices || alsaDevices.length === 0) {
|
||||
result.valid = false;
|
||||
result.alsaInputDevice = INVALID_DEVICE_ID;
|
||||
result.alsaOutputDevice = INVALID_DEVICE_ID;
|
||||
return result;
|
||||
}
|
||||
|
||||
let selectedDevice: AlsaDeviceInfo | undefined = undefined;
|
||||
for (let i = 0; i < alsaDevices.length; ++i) {
|
||||
if (alsaDevices[i].id === result.alsaDevice) {
|
||||
selectedDevice = alsaDevices[i]
|
||||
break;
|
||||
let inDevice = alsaDevices.find(d => d.id === result.alsaInputDevice && d.supportsCapture);
|
||||
let outDevice = alsaDevices.find(d => d.id === result.alsaOutputDevice && d.supportsPlayback);
|
||||
if (!inDevice) {
|
||||
inDevice = alsaDevices.find(d => d.supportsCapture);
|
||||
if (inDevice) {
|
||||
result.alsaInputDevice = inDevice.id;
|
||||
}
|
||||
}
|
||||
if (!selectedDevice) {
|
||||
selectedDevice = alsaDevices[0];
|
||||
result.alsaDevice = selectedDevice.id;
|
||||
|
||||
if (!outDevice) {
|
||||
outDevice = alsaDevices.find(d => d.supportsPlayback);
|
||||
if (outDevice) {
|
||||
result.alsaOutputDevice = outDevice.id;
|
||||
}
|
||||
}
|
||||
if (result.sampleRate === 0) result.sampleRate = 48000;
|
||||
result.sampleRate = selectedDevice.closestSampleRate(result.sampleRate);
|
||||
|
||||
let bestBuffers = getBestBuffers(selectedDevice,result.bufferSize,result.numberOfBuffers);
|
||||
if (!inDevice || !outDevice) {
|
||||
result.valid = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result.sampleRate === 0) result.sampleRate = 48000;
|
||||
let sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates);
|
||||
if (sampleRates.length === 0) sampleRates = inDevice.sampleRates;
|
||||
let bestSr = sampleRates[0];
|
||||
let bestErr = 1e36;
|
||||
for (let sr of sampleRates) {
|
||||
let err = (sr - result.sampleRate) * (sr - result.sampleRate);
|
||||
if (err < bestErr) { bestErr = err; bestSr = sr; }
|
||||
}
|
||||
result.sampleRate = bestSr;
|
||||
|
||||
let bestBuffers = getBestBuffers(inDevice, outDevice, result.bufferSize, result.numberOfBuffers);
|
||||
result.bufferSize = bestBuffers.bufferSize;
|
||||
result.numberOfBuffers = bestBuffers.numberOfBuffers;
|
||||
|
||||
result.valid = true;
|
||||
result.valid = !!result.alsaInputDevice && !!result.alsaOutputDevice;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -321,37 +353,22 @@ const JackServerSettingsDialog = withStyles(
|
||||
this.mounted = false;
|
||||
|
||||
}
|
||||
getSelectedDevice(deviceId: string): AlsaDeviceInfo | null {
|
||||
if (!this.state.alsaDevices) return null;
|
||||
getSelectedDevice(deviceId: string): AlsaDeviceInfo | undefined {
|
||||
if (!this.state.alsaDevices) return undefined;
|
||||
for (let i = 0; i < this.state.alsaDevices.length; ++i) {
|
||||
if (this.state.alsaDevices[i].id === deviceId) {
|
||||
return this.state.alsaDevices[i];
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
handleDeviceChanged(e: any) {
|
||||
let device = e.target.value as string;
|
||||
|
||||
let selectedDevice = this.getSelectedDevice(device);
|
||||
if (!selectedDevice) return;
|
||||
let settings = this.state.jackServerSettings.clone();
|
||||
settings.alsaDevice = device;
|
||||
settings = this.applyAlsaDevices(settings,this.state.alsaDevices);
|
||||
|
||||
this.setState({
|
||||
jackServerSettings: settings,
|
||||
latencyText: getLatencyText(settings),
|
||||
okEnabled: isOkEnabled(settings,this.state.alsaDevices)
|
||||
});
|
||||
}
|
||||
handleRateChanged(e: any) {
|
||||
let rate = e.target.value as number;
|
||||
console.log("New rate: " + rate);
|
||||
let selectedDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaDevice);
|
||||
if (!selectedDevice) return;
|
||||
let inDev = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice);
|
||||
let outDev = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice);
|
||||
if (!inDev && !outDev) return;
|
||||
let settings = this.state.jackServerSettings.clone();
|
||||
settings.sampleRate = rate;
|
||||
|
||||
@@ -364,11 +381,12 @@ const JackServerSettingsDialog = withStyles(
|
||||
handleSizeChanged(e: any) {
|
||||
let size = e.target.value as number;
|
||||
|
||||
let selectedDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaDevice);
|
||||
if (!selectedDevice) return;
|
||||
let inDev = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice);
|
||||
let outDev = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice);
|
||||
if (!inDev && !outDev) return;
|
||||
let settings = this.state.jackServerSettings.clone();
|
||||
settings.bufferSize = size;
|
||||
let bestBufferSetting = getBestBuffers(selectedDevice,settings.bufferSize,settings.numberOfBuffers,true);
|
||||
let bestBufferSetting = getBestBuffers(inDev,outDev,settings.bufferSize,settings.numberOfBuffers,true);
|
||||
settings.bufferSize = bestBufferSetting.bufferSize;
|
||||
settings.numberOfBuffers = bestBufferSetting.numberOfBuffers;
|
||||
|
||||
@@ -381,8 +399,9 @@ const JackServerSettingsDialog = withStyles(
|
||||
handleNumberOfBuffersChanged(e: any) {
|
||||
let bufferCount = e.target.value as number;
|
||||
|
||||
let selectedDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaDevice);
|
||||
if (!selectedDevice) return;
|
||||
let inDev = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice);
|
||||
let outDev = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice);
|
||||
if (!inDev && !outDev) return;
|
||||
let settings = this.state.jackServerSettings.clone();
|
||||
settings.numberOfBuffers = bufferCount;
|
||||
|
||||
@@ -399,7 +418,25 @@ const JackServerSettingsDialog = withStyles(
|
||||
this.props.onApply(this.state.jackServerSettings.clone());
|
||||
}
|
||||
};
|
||||
|
||||
handleInputDeviceChanged(e: any) {
|
||||
const d = e.target.value as string;
|
||||
let s = this.state.jackServerSettings.clone();
|
||||
s.alsaInputDevice = d;
|
||||
this.setState({
|
||||
jackServerSettings: s,
|
||||
okEnabled: isOkEnabled(s, this.state.alsaDevices)
|
||||
});
|
||||
}
|
||||
|
||||
handleOutputDeviceChanged(e: any) {
|
||||
const d = e.target.value as string;
|
||||
let s = this.state.jackServerSettings.clone();
|
||||
s.alsaOutputDevice = d;
|
||||
this.setState({
|
||||
jackServerSettings: s,
|
||||
okEnabled: isOkEnabled(s, this.state.alsaDevices)
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
|
||||
@@ -408,21 +445,17 @@ const JackServerSettingsDialog = withStyles(
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
};
|
||||
let waitingForDevices = !this.state.alsaDevices
|
||||
let noDevices = this.state.alsaDevices && this.state.alsaDevices.length === 0;
|
||||
let selectedDevice: AlsaDeviceInfo | undefined = undefined;
|
||||
if (this.state.alsaDevices) {
|
||||
for (let device of this.state.alsaDevices) {
|
||||
if (device.id === this.state.jackServerSettings.alsaDevice) {
|
||||
selectedDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let bufferSizes: number[] = getValidBufferSizes(selectedDevice);
|
||||
let bufferCounts = getValidBufferCounts(this.state.jackServerSettings.bufferSize,selectedDevice);
|
||||
let bufferSizeDisabled = !selectedDevice;
|
||||
let bufferCountDisabled = !selectedDevice;
|
||||
|
||||
let selectedInputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice);
|
||||
let selectedOutputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice);
|
||||
|
||||
let bufferSizes: number[] = getValidBufferSizesMultiple(selectedInputDevice, selectedOutputDevice);
|
||||
let bufferCounts = getValidBufferCountsMultiple(this.state.jackServerSettings.bufferSize, selectedInputDevice, selectedOutputDevice);
|
||||
let bufferSizeDisabled = !(selectedInputDevice || selectedOutputDevice);
|
||||
let bufferCountDisabled = !(selectedInputDevice || selectedOutputDevice);
|
||||
let sampleRates = selectedInputDevice && selectedOutputDevice ?
|
||||
intersectArrays(selectedInputDevice.sampleRates, selectedOutputDevice.sampleRates)
|
||||
: (selectedInputDevice ? selectedInputDevice.sampleRates : (selectedOutputDevice ? selectedOutputDevice.sampleRates : []));
|
||||
|
||||
return (
|
||||
<DialogEx tag="jack" onClose={handleClose} aria-labelledby="select-channels-title" open={open}
|
||||
@@ -432,39 +465,48 @@ const JackServerSettingsDialog = withStyles(
|
||||
|
||||
>
|
||||
<DialogContent>
|
||||
<div>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
{/* Audio Input Device */}
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel htmlFor="jsd_device">Device</InputLabel>
|
||||
<Select variant="standard" onChange={(e) => this.handleDeviceChanged(e)}
|
||||
value={this.state.jackServerSettings.alsaDevice}
|
||||
style={{width: 220}}
|
||||
inputProps={{
|
||||
name: "Device",
|
||||
id: "jsd_device",
|
||||
}}
|
||||
>
|
||||
{(noDevices && !waitingForDevices) &&
|
||||
(
|
||||
<MenuItem value={INVALID_DEVICE_ID}>No suitable devices.</MenuItem>
|
||||
)
|
||||
}
|
||||
{((!noDevices) && !waitingForDevices) && (
|
||||
this.state.alsaDevices!.map((device) =>
|
||||
(
|
||||
<MenuItem key={device.id} value={device.id}>{device.name}</MenuItem>
|
||||
)
|
||||
<InputLabel htmlFor="jsd_inputDevice">Input Device</InputLabel>
|
||||
<Select
|
||||
id="jsd_inputDevice"
|
||||
value={this.state.jackServerSettings.alsaInputDevice}
|
||||
onChange={e => this.handleInputDeviceChanged(e)}
|
||||
disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0}
|
||||
style={{ width: 220 }}
|
||||
>
|
||||
{this.state.alsaDevices?.filter(d => d.supportsCapture).map(d => (
|
||||
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
|
||||
)) || <MenuItem value="" disabled>Loading...</MenuItem>}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
)
|
||||
)}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{/* Audio Output Device */}
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel htmlFor="jsd_outputDevice">Output Device</InputLabel>
|
||||
<Select
|
||||
id="jsd_outputDevice"
|
||||
value={this.state.jackServerSettings.alsaOutputDevice}
|
||||
onChange={e => this.handleOutputDeviceChanged(e)}
|
||||
disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0}
|
||||
style={{ width: 220 }}
|
||||
>
|
||||
{this.state.alsaDevices?.filter(d => d.supportsPlayback).map(d => (
|
||||
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
|
||||
)) || <MenuItem value="" disabled>Loading...</MenuItem>}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<IconButtonEx tooltip="Refresh devices" onClick={() => this.requestAlsaInfo()} aria-label="refresh-audio-devices">
|
||||
<RefreshIcon />
|
||||
</IconButtonEx>
|
||||
</div><div>
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel htmlFor="jsd_sampleRate">Sample rate</InputLabel>
|
||||
<Select variant="standard"
|
||||
onChange={(e) => this.handleRateChanged(e)}
|
||||
value={this.state.jackServerSettings.sampleRate}
|
||||
disabled={!selectedDevice}
|
||||
disabled={sampleRates.length === 0}
|
||||
inputProps={{
|
||||
id: 'jsd_sampleRate',
|
||||
style: {
|
||||
@@ -472,11 +514,9 @@ const JackServerSettingsDialog = withStyles(
|
||||
}
|
||||
}}
|
||||
>
|
||||
{selectedDevice &&
|
||||
selectedDevice.sampleRates.map((sr) => {
|
||||
return ( <MenuItem value={sr}>{sr}</MenuItem> );
|
||||
})
|
||||
}
|
||||
{sampleRates.map((sr) => {
|
||||
return (<MenuItem value={sr}>{sr}</MenuItem> );
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<div style={{ display: "inline", whiteSpace: "nowrap" }}>
|
||||
|
||||
Reference in New Issue
Block a user