Misc bug fixes
Alsa trace, NAM legacy flag,
This commit is contained in:
+59
-5
@@ -37,6 +37,8 @@
|
|||||||
#include "DummyAudioDriver.hpp"
|
#include "DummyAudioDriver.hpp"
|
||||||
#include "SchedulerPriority.hpp"
|
#include "SchedulerPriority.hpp"
|
||||||
#include "CrashGuard.hpp"
|
#include "CrashGuard.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
#include "CpuUse.hpp"
|
#include "CpuUse.hpp"
|
||||||
|
|
||||||
@@ -260,6 +262,8 @@ namespace pipedal
|
|||||||
std::vector<BufferTrace> bufferTraces{1000};
|
std::vector<BufferTrace> bufferTraces{1000};
|
||||||
size_t bufferTraceIndex = 0;
|
size_t bufferTraceIndex = 0;
|
||||||
|
|
||||||
|
virtual void DumpBufferTrace(size_t nEntries) override;
|
||||||
|
|
||||||
inline void TraceBufferPositions(size_t framesInBuffer, char code = ' ')
|
inline void TraceBufferPositions(size_t framesInBuffer, char code = ' ')
|
||||||
{
|
{
|
||||||
#if TRACE_BUFFER_POSITIONS
|
#if TRACE_BUFFER_POSITIONS
|
||||||
@@ -1077,7 +1081,7 @@ namespace pipedal
|
|||||||
float *p = (float *)rawPlaybackBuffer.data();
|
float *p = (float *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->playbackBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->playbackChannels;
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
{
|
{
|
||||||
for (int channel = 0; channel < channels; ++channel)
|
for (int channel = 0; channel < channels; ++channel)
|
||||||
@@ -1092,7 +1096,7 @@ namespace pipedal
|
|||||||
float *p = (float *)rawPlaybackBuffer.data();
|
float *p = (float *)rawPlaybackBuffer.data();
|
||||||
|
|
||||||
std::vector<float *> &buffers = this->playbackBuffers;
|
std::vector<float *> &buffers = this->playbackBuffers;
|
||||||
int channels = this->captureChannels;
|
int channels = this->playbackChannels;
|
||||||
for (size_t frame = 0; frame < frames; ++frame)
|
for (size_t frame = 0; frame < frames; ++frame)
|
||||||
{
|
{
|
||||||
for (int channel = 0; channel < channels; ++channel)
|
for (int channel = 0; channel < channels; ++channel)
|
||||||
@@ -1167,7 +1171,6 @@ namespace pipedal
|
|||||||
OpenAudio(this->jackServerSettings, this->channelSelection);
|
OpenAudio(this->jackServerSettings, this->channelSelection);
|
||||||
validate_capture_handle();
|
validate_capture_handle();
|
||||||
FillOutputBuffer();
|
FillOutputBuffer();
|
||||||
audioRunning = true;
|
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
@@ -1180,6 +1183,7 @@ namespace pipedal
|
|||||||
Lv2Log::error(SS("Unable to restart ALSA capture: " << snd_strerror(err)));
|
Lv2Log::error(SS("Unable to restart ALSA capture: " << snd_strerror(err)));
|
||||||
throw PiPedalStateException("Unable to restart ALSA capture.");
|
throw PiPedalStateException("Unable to restart ALSA capture.");
|
||||||
}
|
}
|
||||||
|
audioRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrepareCaptureFunctions(snd_pcm_format_t captureFormat)
|
void PrepareCaptureFunctions(snd_pcm_format_t captureFormat)
|
||||||
@@ -1617,6 +1621,8 @@ namespace pipedal
|
|||||||
auto frame_bytes = this->captureFrameSize;
|
auto frame_bytes = this->captureFrameSize;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
TraceBufferPositions(framesRead,'1');
|
||||||
|
|
||||||
framesRead = snd_pcm_readi(handle, buffer, frames);
|
framesRead = snd_pcm_readi(handle, buffer, frames);
|
||||||
if (framesRead < 0)
|
if (framesRead < 0)
|
||||||
{
|
{
|
||||||
@@ -1629,10 +1635,12 @@ namespace pipedal
|
|||||||
}
|
}
|
||||||
if (framesRead == 0)
|
if (framesRead == 0)
|
||||||
{
|
{
|
||||||
snd_pcm_wait(captureHandle, 1);
|
snd_pcm_wait(handle, frames);
|
||||||
}
|
}
|
||||||
TraceBufferPositions(framesRead);
|
|
||||||
} while (frames > 0);
|
} while (frames > 0);
|
||||||
|
|
||||||
|
TraceBufferPositions(framesRead,'2');
|
||||||
|
|
||||||
return framesRead;
|
return framesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2331,4 +2339,50 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
snd_config_update_free_global(); // to get a clean Valgrind report.
|
snd_config_update_free_global(); // to get a clean Valgrind report.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AlsaDriverImpl::DumpBufferTrace(size_t nEntries)
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
int savedPrecision = cout.precision();
|
||||||
|
auto savedFlags = cout.flags();
|
||||||
|
|
||||||
|
size_t ix = bufferTraceIndex;
|
||||||
|
if (ix < nEntries)
|
||||||
|
{
|
||||||
|
ix = ix + bufferTraces.size()-nEntries;
|
||||||
|
} else {
|
||||||
|
ix -= nEntries;
|
||||||
|
}
|
||||||
|
uint64_t t0;
|
||||||
|
if (bufferTraceIndex == 0) {
|
||||||
|
t0 = bufferTraces[bufferTraces.size()-1].time;
|
||||||
|
} else {
|
||||||
|
t0 = bufferTraces[bufferTraceIndex-1].time;
|
||||||
|
}
|
||||||
|
while (ix != bufferTraceIndex)
|
||||||
|
{
|
||||||
|
auto&bufferTrace = bufferTraces[ix];
|
||||||
|
|
||||||
|
int64_t dt = (int64_t)bufferTrace.time - (int64_t)t0;
|
||||||
|
|
||||||
|
|
||||||
|
cout << bufferTrace.code << " "
|
||||||
|
<< fixed << setprecision(3) << dt*0.001
|
||||||
|
<< " " << "inAvail: " << bufferTrace.inAvail
|
||||||
|
<< " " << "outAvail: " << bufferTrace.outAvail
|
||||||
|
<< " " << "bufferd: " << bufferTrace.buffered
|
||||||
|
<< " " << "total: " << bufferTrace.total
|
||||||
|
<< endl;
|
||||||
|
|
||||||
|
++ix;
|
||||||
|
if (ix == bufferTraces.size())
|
||||||
|
{
|
||||||
|
ix = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cout.precision(savedPrecision);
|
||||||
|
cout.flags(savedFlags);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ namespace pipedal {
|
|||||||
virtual void Close() = 0;
|
virtual void Close() = 0;
|
||||||
|
|
||||||
virtual std::string GetConfigurationDescription() = 0;
|
virtual std::string GetConfigurationDescription() = 0;
|
||||||
|
virtual void DumpBufferTrace(size_t nEntries) {}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -91,10 +91,15 @@ ControlValue* PedalboardItem::GetControlValue(const std::string&symbol)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool PedalboardItem::SetControlValue(const std::string&symbol, float value)
|
bool PedalboardItem::SetControlValue(const std::string&symbol, float value)
|
||||||
{
|
{
|
||||||
ControlValue*controlValue = GetControlValue(symbol);
|
ControlValue*controlValue = GetControlValue(symbol);
|
||||||
if (controlValue == nullptr) return false;
|
if (controlValue == nullptr)
|
||||||
|
{
|
||||||
|
this->controlValues().push_back(ControlValue(symbol.c_str(),value));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (controlValue->value() != value)
|
if (controlValue->value() != value)
|
||||||
{
|
{
|
||||||
controlValue->value(value);
|
controlValue->value(value);
|
||||||
|
|||||||
+1
-1
@@ -108,7 +108,6 @@ public:
|
|||||||
const ControlValue*GetControlValue(const std::string&symbol) const;
|
const ControlValue*GetControlValue(const std::string&symbol) const;
|
||||||
bool SetControlValue(const std::string&key, float value);
|
bool SetControlValue(const std::string&key, float value);
|
||||||
|
|
||||||
|
|
||||||
bool IsStructurallyIdentical(const PedalboardItem&other) const;
|
bool IsStructurallyIdentical(const PedalboardItem&other) const;
|
||||||
|
|
||||||
void ApplySnapshotValue(SnapshotValue*snapshotValue);
|
void ApplySnapshotValue(SnapshotValue*snapshotValue);
|
||||||
@@ -161,6 +160,7 @@ public:
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
DECLARE_JSON_MAP(PedalboardItem);
|
DECLARE_JSON_MAP(PedalboardItem);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SnapshotValue {
|
class SnapshotValue {
|
||||||
|
|||||||
+26
-34
@@ -272,16 +272,17 @@ void PiPedalModel::Load()
|
|||||||
|
|
||||||
this->pedalboard = storage.GetCurrentPreset(); // the current *saved* preset.
|
this->pedalboard = storage.GetCurrentPreset(); // the current *saved* preset.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// the current edited preset, saved only across orderly shutdowns.
|
// the current edited preset, saved only across orderly shutdowns.
|
||||||
|
|
||||||
CrashGuard::SetCrashGuardFileName(storage.GetDataRoot() / "crash_guard.data");
|
CrashGuard::SetCrashGuardFileName(storage.GetDataRoot() / "crash_guard.data");
|
||||||
|
|
||||||
if (CrashGuard::HasCrashed()) {
|
if (CrashGuard::HasCrashed())
|
||||||
|
{
|
||||||
// ignore the current preset, and load a blank pedalboard in order to avoid a potential plugin crash.
|
// ignore the current preset, and load a blank pedalboard in order to avoid a potential plugin crash.
|
||||||
this->pedalboard = Pedalboard::MakeDefault();
|
this->pedalboard = Pedalboard::MakeDefault();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
CurrentPreset currentPreset;
|
CurrentPreset currentPreset;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -289,7 +290,7 @@ void PiPedalModel::Load()
|
|||||||
{
|
{
|
||||||
this->pedalboard = currentPreset.preset_;
|
this->pedalboard = currentPreset.preset_;
|
||||||
this->hasPresetChanged = currentPreset.modified_;
|
this->hasPresetChanged = currentPreset.modified_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
@@ -297,8 +298,6 @@ void PiPedalModel::Load()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UpdateDefaults(&this->pedalboard);
|
UpdateDefaults(&this->pedalboard);
|
||||||
|
|
||||||
std::unique_ptr<AudioHost> p{AudioHost::CreateInstance(pluginHost.asIHost())};
|
std::unique_ptr<AudioHost> p{AudioHost::CreateInstance(pluginHost.asIHost())};
|
||||||
@@ -644,11 +643,9 @@ void PiPedalModel::SetPedalboardItemUseModUi(int64_t clientId, int64_t instanceI
|
|||||||
subscriber->OnItemUseModUiChanged(clientId, instanceId, enabled);
|
subscriber->OnItemUseModUiChanged(clientId, instanceId, enabled);
|
||||||
}
|
}
|
||||||
this->SetPresetChanged(clientId, true);
|
this->SetPresetChanged(clientId, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PiPedalModel::SetPedalboardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled)
|
void PiPedalModel::SetPedalboardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> guard{mutex};
|
std::lock_guard<std::recursive_mutex> guard{mutex};
|
||||||
@@ -1449,7 +1446,8 @@ void PiPedalModel::OnAlsaSequencerDeviceAdded(int client, const std::string &cli
|
|||||||
{
|
{
|
||||||
// reconfigure connections.
|
// reconfigure connections.
|
||||||
std::lock_guard<std::recursive_mutex> lock(this->mutex);
|
std::lock_guard<std::recursive_mutex> lock(this->mutex);
|
||||||
if (this->audioHost) {
|
if (this->audioHost)
|
||||||
|
{
|
||||||
this->audioHost->SetAlsaSequencerConfiguration(this->storage.GetAlsaSequencerConfiguration());
|
this->audioHost->SetAlsaSequencerConfiguration(this->storage.GetAlsaSequencerConfiguration());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1457,7 +1455,7 @@ void PiPedalModel::OnAlsaSequencerDeviceAdded(int client, const std::string &cli
|
|||||||
}
|
}
|
||||||
void PiPedalModel::OnAlsaSequencerDeviceRemoved(int client)
|
void PiPedalModel::OnAlsaSequencerDeviceRemoved(int client)
|
||||||
{
|
{
|
||||||
// no action required.
|
// no action required.
|
||||||
}
|
}
|
||||||
|
|
||||||
void PiPedalModel::SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration)
|
void PiPedalModel::SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration)
|
||||||
@@ -2027,27 +2025,25 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
|
|||||||
pedalboardItem->midiChannelBinding(std::optional<MidiChannelBinding>()); // clear it.
|
pedalboardItem->midiChannelBinding(std::optional<MidiChannelBinding>()); // clear it.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//////// PLUGIN SPECIFIC UPGRADES //////////////////////
|
||||||
|
if (pPlugin->uri() == "http://two-play.com/plugins/toob-nam")
|
||||||
|
{
|
||||||
|
ControlValue *pValue = pedalboardItem->GetControlValue("inputCalibrationMode");
|
||||||
|
if (pValue == nullptr)
|
||||||
|
{
|
||||||
|
// calibration is OFF when upgrading.
|
||||||
|
pedalboardItem->SetControlValue("inputCalibrationMode", 0.0f);
|
||||||
|
}
|
||||||
|
pValue = pedalboardItem->GetControlValue("version");
|
||||||
|
if (pValue == nullptr)
|
||||||
|
{
|
||||||
|
pedalboardItem->SetControlValue("version", 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (size_t i = 0; i < pPlugin->ports().size(); ++i)
|
for (size_t i = 0; i < pPlugin->ports().size(); ++i)
|
||||||
{
|
{
|
||||||
auto port = pPlugin->ports()[i];
|
auto port = pPlugin->ports()[i];
|
||||||
|
|
||||||
//////// PLUGIN SPECIFIC UPGRADES //////////////////////
|
|
||||||
if (pPlugin->uri() == "http://two-play.com/plugins/toob-nam")
|
|
||||||
{
|
|
||||||
ControlValue *pValue = pedalboardItem->GetControlValue("inputCalibrationMode");
|
|
||||||
if (pValue == nullptr)
|
|
||||||
{
|
|
||||||
// calibration is OFF when upgrading.
|
|
||||||
pedalboardItem->SetControlValue("inputCalibrationMode",0.0f);
|
|
||||||
}
|
|
||||||
pValue = pedalboardItem->GetControlValue("version");
|
|
||||||
if (pValue == nullptr)
|
|
||||||
{
|
|
||||||
pedalboardItem->SetControlValue("version",0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (port->is_control_port() && port->is_input())
|
if (port->is_control_port() && port->is_input())
|
||||||
{
|
{
|
||||||
ControlValue *pValue = pedalboardItem->GetControlValue(port->symbol());
|
ControlValue *pValue = pedalboardItem->GetControlValue(port->symbol());
|
||||||
@@ -3107,19 +3103,15 @@ void PiPedalModel::SetTone3000Auth(const std::string &apiKey)
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
storage.SetTone3000Auth(apiKey);
|
storage.SetTone3000Auth(apiKey);
|
||||||
|
|
||||||
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
|
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
|
||||||
bool hasAuth = apiKey != "";
|
bool hasAuth = apiKey != "";
|
||||||
for (auto &subscriber : t)
|
for (auto &subscriber : t)
|
||||||
{
|
{
|
||||||
subscriber->OnTone3000AuthChanged(hasAuth);
|
subscriber->OnTone3000AuthChanged(hasAuth);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
bool PiPedalModel::HasTone3000Auth() const
|
bool PiPedalModel::HasTone3000Auth() const
|
||||||
{
|
{
|
||||||
return storage.GetTone3000Auth() != "";
|
return storage.GetTone3000Auth() != "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -539,7 +539,7 @@ UiFrequencyPlot::UiFrequencyPlot(PluginHost*pHost, const LilvNode*node,
|
|||||||
this->yTop_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__yTop,5);
|
this->yTop_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__yTop,5);
|
||||||
this->yBottom_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__yBottom,-35);
|
this->yBottom_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__yBottom,-35);
|
||||||
this->xLog_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__xLog,-35);
|
this->xLog_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__xLog,-35);
|
||||||
this->width_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__width,60);
|
this->width_ = GetFloat(pWorld,node,pHost->lilvUris->pipedalUI__width,176);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
Check ToOB Cab SIM F/R range.
|
||||||
|
|
||||||
Versioning for legacy NAM presets!
|
|
||||||
Convert CRVB presets!
|
Convert CRVB presets!
|
||||||
|
|
||||||
double select on double-click/ok in file dialog.
|
double select on double-click/ok in file dialog.
|
||||||
|
|||||||
@@ -451,7 +451,7 @@ const ToobParametricEqView =
|
|||||||
if (isLandscape) {
|
if (isLandscape) {
|
||||||
panelStyle = { display: "flex", flexDirection: "row", alignItems: "stretch", width: "100%" };
|
panelStyle = { display: "flex", flexDirection: "row", alignItems: "stretch", width: "100%" };
|
||||||
scrollStyle = { flex: "1 1 auto", overflowX: "auto", overflowY: "hidden", whiteSpace: "nowrap" };
|
scrollStyle = { flex: "1 1 auto", overflowX: "auto", overflowY: "hidden", whiteSpace: "nowrap" };
|
||||||
frameStyle = { display: "flex", flexFlow: "row nowrap", alignItems: "center",paddingTop: 8 };
|
frameStyle = { display: "flex", flexFlow: "row nowrap", alignItems: "center", paddingTop: 8 };
|
||||||
} else {
|
} else {
|
||||||
panelStyle = { display: "flex", flexDirection: "column", alignItems: "stretch", height: "100%" };
|
panelStyle = { display: "flex", flexDirection: "column", alignItems: "stretch", height: "100%" };
|
||||||
scrollStyle = { flex: "1 1 auto", overflowX: "hidden", overflowY: "auto", whiteSpace: "nowrap" };
|
scrollStyle = { flex: "1 1 auto", overflowX: "hidden", overflowY: "auto", whiteSpace: "nowrap" };
|
||||||
@@ -460,17 +460,33 @@ const ToobParametricEqView =
|
|||||||
|
|
||||||
return [(
|
return [(
|
||||||
<div key={"vtc_container"} style={panelStyle}>
|
<div key={"vtc_container"} style={panelStyle}>
|
||||||
<div key={"vtc_panel"} style={{ flex: " 0 0 auto", paddingBottom: 8, display: "flex", alignItems: "center", flexFlow: "row nowrap" }}>
|
{host.isLandscapeGrid() ? (
|
||||||
{controls[0] as React.ReactNode}
|
<div key={"vtc_panel"} style={{ flex: " 0 0 auto", paddingBottom: 8, display: "flex", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||||
<div style={{ alignSelf: "flex-start", marginTop: 8 }}>
|
<div style={{ alignSelf: "flex-start", marginTop: 8 }}>
|
||||||
<IconButton onClick={(e) => {
|
<IconButton
|
||||||
e.stopPropagation();
|
style={{marginTop: 24}}
|
||||||
this.setState({ maximized: true });
|
onClick={(e) => {
|
||||||
}}>
|
e.stopPropagation();
|
||||||
<CircleUpIcon className={classes.upIcon} />
|
this.setState({ maximized: true });
|
||||||
</IconButton>
|
}}>
|
||||||
|
<CircleUpIcon className={classes.upIcon} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
{controls[0] as React.ReactNode}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
<div key={"vtc_panel"} style={{ flex: " 0 0 auto", paddingBottom: 8, display: "flex", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||||
|
{controls[0] as React.ReactNode}
|
||||||
|
<div style={{ alignSelf: "flex-start", marginTop: 8 }}>
|
||||||
|
<IconButton onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.setState({ maximized: true });
|
||||||
|
}}>
|
||||||
|
<CircleUpIcon className={classes.upIcon} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div style={scrollStyle}>
|
<div style={scrollStyle}>
|
||||||
<div style={frameStyle}>
|
<div style={frameStyle}>
|
||||||
{panelControls}
|
{panelControls}
|
||||||
|
|||||||
Reference in New Issue
Block a user