Roll back TONE3000 direct download (not ready for prime time); add direct link to TONE3000 downloads from File Property dialog.
This commit is contained in:
@@ -3038,3 +3038,22 @@ void PiPedalModel::SetPedalboardItemTitle(int64_t instanceId, const std::string
|
|||||||
this->SetPresetChanged(-1, true);
|
this->SetPresetChanged(-1, true);
|
||||||
this->FirePedalboardChanged(-1, false);
|
this->FirePedalboardChanged(-1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PiPedalModel::SetTone3000Auth(const std::string &apiKey)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
|
storage.SetTone3000Auth(apiKey);
|
||||||
|
|
||||||
|
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
|
||||||
|
bool hasAuth = apiKey != "";
|
||||||
|
for (auto &subscriber : t)
|
||||||
|
{
|
||||||
|
subscriber->OnTone3000AuthChanged(hasAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
bool PiPedalModel::HasTone3000Auth() const
|
||||||
|
{
|
||||||
|
return storage.GetTone3000Auth() != "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ namespace pipedal
|
|||||||
virtual void OnHasWifiChanged(bool hasWifi) = 0;
|
virtual void OnHasWifiChanged(bool hasWifi) = 0;
|
||||||
virtual void OnAlsaSequencerConfigurationChanged(const AlsaSequencerConfiguration &alsaSequencerConfiguration) = 0;
|
virtual void OnAlsaSequencerConfigurationChanged(const AlsaSequencerConfiguration &alsaSequencerConfiguration) = 0;
|
||||||
virtual void Close() = 0;
|
virtual void Close() = 0;
|
||||||
|
virtual void OnTone3000AuthChanged(bool value) = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -266,6 +267,7 @@ namespace pipedal
|
|||||||
void CheckForResourceInitialization(Pedalboard &pedalboard);
|
void CheckForResourceInitialization(Pedalboard &pedalboard);
|
||||||
UiFileProperty::ptr FindLoadedPatchProperty(int64_t instanceId,const std::string&patchPropertyUri);
|
UiFileProperty::ptr FindLoadedPatchProperty(int64_t instanceId,const std::string&patchPropertyUri);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PiPedalModel();
|
PiPedalModel();
|
||||||
virtual ~PiPedalModel();
|
virtual ~PiPedalModel();
|
||||||
@@ -479,6 +481,10 @@ namespace pipedal
|
|||||||
int32_t to);
|
int32_t to);
|
||||||
|
|
||||||
void SetPedalboardItemTitle(int64_t instanceId, const std::string &title);
|
void SetPedalboardItemTitle(int64_t instanceId, const std::string &title);
|
||||||
|
|
||||||
|
void SetTone3000Auth(const std::string &apiKey);
|
||||||
|
bool HasTone3000Auth() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pipedal.
|
} // namespace pipedal.
|
||||||
@@ -1416,6 +1416,10 @@ public:
|
|||||||
pReader->read(&instanceId);
|
pReader->read(&instanceId);
|
||||||
uint64_t result = model.DeleteBank(this->clientId, instanceId);
|
uint64_t result = model.DeleteBank(this->clientId, instanceId);
|
||||||
this->Reply(replyTo, "deleteBankItem", result);
|
this->Reply(replyTo, "deleteBankItem", result);
|
||||||
|
} else if (message == "getHasTone3000Auth")
|
||||||
|
{
|
||||||
|
bool result = model.HasTone3000Auth();
|
||||||
|
this->Reply(replyTo, "getHasTone3000Auth", result);
|
||||||
}
|
}
|
||||||
else if (message == "renameBank")
|
else if (message == "renameBank")
|
||||||
{
|
{
|
||||||
@@ -1863,6 +1867,10 @@ private:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
virtual void OnTone3000AuthChanged(bool value)
|
||||||
|
{
|
||||||
|
Send("onTone3000AuthChanged", value);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void OnErrorMessage(const std::string &message)
|
virtual void OnErrorMessage(const std::string &message)
|
||||||
{
|
{
|
||||||
|
|||||||
+72
-23
@@ -254,6 +254,7 @@ void Storage::Initialize()
|
|||||||
LoadPluginPresetIndex();
|
LoadPluginPresetIndex();
|
||||||
LoadBankIndex();
|
LoadBankIndex();
|
||||||
LoadCurrentBank();
|
LoadCurrentBank();
|
||||||
|
LoadTone3000Auth();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LoadChannelSelection();
|
LoadChannelSelection();
|
||||||
@@ -311,6 +312,11 @@ std::filesystem::path Storage::GetCurrentPresetPath() const
|
|||||||
return this->dataRoot / "currentPreset.json";
|
return this->dataRoot / "currentPreset.json";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::filesystem::path Storage::GetTone3000AuthPath() const
|
||||||
|
{
|
||||||
|
return this->dataRoot / "tone3000.json";
|
||||||
|
}
|
||||||
|
|
||||||
std::filesystem::path Storage::GetChannelSelectionFileName()
|
std::filesystem::path Storage::GetChannelSelectionFileName()
|
||||||
{
|
{
|
||||||
return this->dataRoot / "JackChannelSelection.json";
|
return this->dataRoot / "JackChannelSelection.json";
|
||||||
@@ -741,34 +747,35 @@ JackChannelSelection Storage::GetJackChannelSelection(const JackConfiguration &j
|
|||||||
return jackChannelSelection.RemoveInvalidChannels(jackConfiguration);
|
return jackChannelSelection.RemoveInvalidChannels(jackConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static AlsaSequencerConfiguration MigrateRawMidiToAlsaSequencer(std::vector<AlsaMidiDeviceInfo> &selectedDevices)
|
static AlsaSequencerConfiguration MigrateRawMidiToAlsaSequencer(std::vector<AlsaMidiDeviceInfo> &selectedDevices)
|
||||||
{
|
{
|
||||||
AlsaSequencerConfiguration result;
|
AlsaSequencerConfiguration result;
|
||||||
try {
|
try
|
||||||
auto sequencerPorts = AlsaSequencer::EnumeratePorts();
|
{
|
||||||
|
auto sequencerPorts = AlsaSequencer::EnumeratePorts();
|
||||||
|
|
||||||
// Prepare Migrate raw MIDI devices to ALSA sequencer ports.
|
// Prepare Migrate raw MIDI devices to ALSA sequencer ports.
|
||||||
|
|
||||||
for (auto i = selectedDevices.begin(); i != selectedDevices.end(); ++i)
|
for (auto i = selectedDevices.begin(); i != selectedDevices.end(); ++i)
|
||||||
{
|
{
|
||||||
if (i->name_.starts_with("hw:")) {
|
if (i->name_.starts_with("hw:"))
|
||||||
for (const auto &port: sequencerPorts)
|
{
|
||||||
|
for (const auto &port : sequencerPorts)
|
||||||
{
|
{
|
||||||
if (i->name_ == port.rawMidiDevice)
|
if (i->name_ == port.rawMidiDevice)
|
||||||
{
|
{
|
||||||
result.connections().push_back(
|
result.connections().push_back(
|
||||||
AlsaSequencerPortSelection(port.id, port.name,port.displaySortOrder)
|
AlsaSequencerPortSelection(port.id, port.name, port.displaySortOrder));
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (const std::exception&e) {
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
Lv2Log::error(SS("Failed to migrate MIDI settings. " << e.what()));
|
Lv2Log::error(SS("Failed to migrate MIDI settings. " << e.what()));
|
||||||
// ick.
|
// ick.
|
||||||
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -790,23 +797,25 @@ void Storage::LoadAlsaSequencerConfiguration()
|
|||||||
{
|
{
|
||||||
Lv2Log::error("I/O error reading %s: %s", fileName.c_str(), e.what());
|
Lv2Log::error("I/O error reading %s: %s", fileName.c_str(), e.what());
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
// migrate legacy settings from JackConfiguration?
|
// migrate legacy settings from JackConfiguration?
|
||||||
if (this->isJackChannelSelectionValid)
|
if (this->isJackChannelSelectionValid)
|
||||||
{
|
{
|
||||||
this->alsaSequencerConfiguration =
|
this->alsaSequencerConfiguration =
|
||||||
MigrateRawMidiToAlsaSequencer(
|
MigrateRawMidiToAlsaSequencer(
|
||||||
this->jackChannelSelection.LegacyGetInputMidiDevices());
|
this->jackChannelSelection.LegacyGetInputMidiDevices());
|
||||||
this->jackChannelSelection.LegacyGetInputMidiDevices().clear(); // let them be clear, the next it gets updated.
|
this->jackChannelSelection.LegacyGetInputMidiDevices().clear(); // let them be clear, the next it gets updated.
|
||||||
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
// no legacy settings, so just create a default configuration.
|
// no legacy settings, so just create a default configuration.
|
||||||
this->alsaSequencerConfiguration = AlsaSequencerConfiguration();
|
this->alsaSequencerConfiguration = AlsaSequencerConfiguration();
|
||||||
}
|
}
|
||||||
SaveAlsaSequencerConfiguration();
|
SaveAlsaSequencerConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
void Storage::SaveAlsaSequencerConfiguration()
|
void Storage::SaveAlsaSequencerConfiguration()
|
||||||
{
|
{
|
||||||
@@ -814,7 +823,7 @@ void Storage::SaveAlsaSequencerConfiguration()
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pipedal::ofstream_synced s(fileName);
|
pipedal::ofstream_synced s(fileName);
|
||||||
json_writer writer(s);
|
json_writer writer(s);
|
||||||
writer.write(this->alsaSequencerConfiguration);
|
writer.write(this->alsaSequencerConfiguration);
|
||||||
}
|
}
|
||||||
@@ -822,7 +831,6 @@ void Storage::SaveAlsaSequencerConfiguration()
|
|||||||
{
|
{
|
||||||
Lv2Log::error("I/O error writing %s: %s", fileName.c_str(), e.what());
|
Lv2Log::error("I/O error writing %s: %s", fileName.c_str(), e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration)
|
void Storage::SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration)
|
||||||
@@ -836,7 +844,6 @@ AlsaSequencerConfiguration Storage::GetAlsaSequencerConfiguration() const
|
|||||||
return this->alsaSequencerConfiguration;
|
return this->alsaSequencerConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Storage::LoadChannelSelection()
|
void Storage::LoadChannelSelection()
|
||||||
{
|
{
|
||||||
auto fileName = this->GetChannelSelectionFileName();
|
auto fileName = this->GetChannelSelectionFileName();
|
||||||
@@ -861,7 +868,7 @@ void Storage::SaveChannelSelection()
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// replaced with AlsaSequencerConfiguration. Delete legacy data.
|
// replaced with AlsaSequencerConfiguration. Delete legacy data.
|
||||||
this->jackChannelSelection.LegacyGetInputMidiDevices().clear();
|
this->jackChannelSelection.LegacyGetInputMidiDevices().clear();
|
||||||
pipedal::ofstream_synced s(fileName);
|
pipedal::ofstream_synced s(fileName);
|
||||||
json_writer writer(s, false);
|
json_writer writer(s, false);
|
||||||
writer.write(this->jackChannelSelection);
|
writer.write(this->jackChannelSelection);
|
||||||
@@ -1849,12 +1856,14 @@ static void AddTracksToResult(
|
|||||||
}
|
}
|
||||||
const auto &path = dir_entry.path();
|
const auto &path = dir_entry.path();
|
||||||
auto name = path.filename().string();
|
auto name = path.filename().string();
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
if (dir_entry.is_directory())
|
if (dir_entry.is_directory())
|
||||||
{
|
{
|
||||||
resultFiles.push_back(FileEntry{path, name, true, dir_entry.is_symlink()});
|
resultFiles.push_back(FileEntry{path, name, true, dir_entry.is_symlink()});
|
||||||
}
|
}
|
||||||
} catch (const std::exception &e)
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
Lv2Log::warning(SS("Failed to add directory entry: " << path.string() << " - " << e.what()));
|
Lv2Log::warning(SS("Failed to add directory entry: " << path.string() << " - " << e.what()));
|
||||||
}
|
}
|
||||||
@@ -1903,7 +1912,6 @@ static void AddTracksToResult(
|
|||||||
throw std::logic_error(
|
throw std::logic_error(
|
||||||
SS("AddTracksToResult failed to enumerate audio files. " << rootPath.string() << " - " << error.what()));
|
SS("AddTracksToResult failed to enumerate audio files. " << rootPath.string() << " - " << error.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (const std::exception &error)
|
catch (const std::exception &error)
|
||||||
{
|
{
|
||||||
@@ -2602,6 +2610,47 @@ const PluginPresetIndex &Storage::GetPluginPresetIndex()
|
|||||||
return pluginPresetIndex;
|
return pluginPresetIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Storage::LoadTone3000Auth()
|
||||||
|
{
|
||||||
|
fs::path path = GetTone3000AuthPath();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!fs::exists(path))
|
||||||
|
{
|
||||||
|
this->tone3000Auth = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::ifstream s(path);
|
||||||
|
json_reader reader(s);
|
||||||
|
reader.read(&(this->tone3000Auth));
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
Lv2Log::error("Failed to load tone3000Auth: %s", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Storage::SetTone3000Auth(const std::string &apiKey)
|
||||||
|
{
|
||||||
|
if (tone3000Auth != apiKey)
|
||||||
|
{
|
||||||
|
tone3000Auth = apiKey;
|
||||||
|
|
||||||
|
pipedal::ofstream_synced os(this->GetTone3000AuthPath());
|
||||||
|
if (!os.is_open())
|
||||||
|
{
|
||||||
|
Lv2Log::error("Failed to open Tone3000 auth file for writing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
json_writer writer(os);
|
||||||
|
writer.write(apiKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string Storage::GetTone3000Auth() const
|
||||||
|
{
|
||||||
|
return tone3000Auth;
|
||||||
|
}
|
||||||
|
|
||||||
JSON_MAP_BEGIN(UserSettings)
|
JSON_MAP_BEGIN(UserSettings)
|
||||||
JSON_MAP_REFERENCE(UserSettings, governor)
|
JSON_MAP_REFERENCE(UserSettings, governor)
|
||||||
JSON_MAP_REFERENCE(UserSettings, showStatusMonitor)
|
JSON_MAP_REFERENCE(UserSettings, showStatusMonitor)
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ private:
|
|||||||
BankIndex bankIndex;
|
BankIndex bankIndex;
|
||||||
BankFile currentBank;
|
BankFile currentBank;
|
||||||
PluginPresetIndex pluginPresetIndex;
|
PluginPresetIndex pluginPresetIndex;
|
||||||
|
std::string tone3000Auth;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void FillSampleDirectoryTree(FilePropertyDirectoryTree*node, const std::filesystem::path&directory) const;
|
void FillSampleDirectoryTree(FilePropertyDirectoryTree*node, const std::filesystem::path&directory) const;
|
||||||
@@ -87,6 +88,7 @@ private:
|
|||||||
std::filesystem::path GetChannelSelectionFileName();
|
std::filesystem::path GetChannelSelectionFileName();
|
||||||
std::filesystem::path GetAlsaSequencerConfigurationFileName();
|
std::filesystem::path GetAlsaSequencerConfigurationFileName();
|
||||||
std::filesystem::path GetCurrentPresetPath() const;
|
std::filesystem::path GetCurrentPresetPath() const;
|
||||||
|
std::filesystem::path GetTone3000AuthPath() const;
|
||||||
|
|
||||||
void LoadBankIndex();
|
void LoadBankIndex();
|
||||||
void SaveBankIndex();
|
void SaveBankIndex();
|
||||||
@@ -254,6 +256,10 @@ public:
|
|||||||
const UiFileProperty&uiFileProperty,
|
const UiFileProperty&uiFileProperty,
|
||||||
bool overwrite = false);
|
bool overwrite = false);
|
||||||
FilePropertyDirectoryTree::ptr GetFilePropertydirectoryTree(const UiFileProperty&uiFileProperty,const std::filesystem::path&selectedPath);
|
FilePropertyDirectoryTree::ptr GetFilePropertydirectoryTree(const UiFileProperty&uiFileProperty,const std::filesystem::path&selectedPath);
|
||||||
|
|
||||||
|
void LoadTone3000Auth();
|
||||||
|
void SetTone3000Auth(const std::string&apiKey);
|
||||||
|
std::string GetTone3000Auth() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+15
-1
@@ -188,6 +188,10 @@ public:
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (segment == "Tone3000Auth")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
else if (segment == "PluginPresets")
|
else if (segment == "PluginPresets")
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -734,7 +738,17 @@ public:
|
|||||||
{
|
{
|
||||||
std::string segment = request_uri.segment(1);
|
std::string segment = request_uri.segment(1);
|
||||||
|
|
||||||
if (segment == "uploadPluginPresets")
|
if (segment == "Tone3000Auth") {
|
||||||
|
// https://www.tone3000.com/api/v1/auth?redirect_url=http://10.0.0.151:8080/var/Tone3000Auth&otp_only=true
|
||||||
|
std::string apiKey = request_uri.query("api_key");
|
||||||
|
|
||||||
|
model->SetTone3000Auth(apiKey);
|
||||||
|
|
||||||
|
res.set(HttpField::content_type, "application/json");
|
||||||
|
res.set(HttpField::cache_control, "no-cache");
|
||||||
|
|
||||||
|
res.setBody("\"OK\"");
|
||||||
|
} else if (segment == "uploadPluginPresets")
|
||||||
{
|
{
|
||||||
PluginPresets presets;
|
PluginPresets presets;
|
||||||
fs::path filePath = req.get_body_temporary_file();
|
fs::path filePath = req.get_body_temporary_file();
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
Exctly one underrun per seek in Toob Player
|
||||||
|
|
||||||
Test autohotspot detection.
|
Test autohotspot detection.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import CssBaseline from '@mui/material/CssBaseline';
|
|||||||
import VirtualKeyboardHandler from './VirtualKeyboardHandler';
|
import VirtualKeyboardHandler from './VirtualKeyboardHandler';
|
||||||
import AppThemed from "./AppThemed";
|
import AppThemed from "./AppThemed";
|
||||||
import { isDarkMode } from './DarkMode';
|
import { isDarkMode } from './DarkMode';
|
||||||
|
import Tone3000AuthComplete from './Tone3000AuthComplete';
|
||||||
|
|
||||||
|
|
||||||
declare module '@mui/material/styles' {
|
declare module '@mui/material/styles' {
|
||||||
interface Theme {
|
interface Theme {
|
||||||
@@ -221,6 +223,12 @@ type AppThemeProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function isTone3000Auth() {
|
||||||
|
let url = new URL(window.location.href);
|
||||||
|
let param = url.searchParams.get("api_key");
|
||||||
|
return (param !== null && param !== "")
|
||||||
|
}
|
||||||
|
|
||||||
const App = (class extends React.Component {
|
const App = (class extends React.Component {
|
||||||
// Before the component mounts, we initialise our state
|
// Before the component mounts, we initialise our state
|
||||||
|
|
||||||
@@ -240,8 +248,11 @@ const App = (class extends React.Component {
|
|||||||
<StyledEngineProvider injectFirst>
|
<StyledEngineProvider injectFirst>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
|
{isTone3000Auth() ? (
|
||||||
<AppThemed />
|
<Tone3000AuthComplete />
|
||||||
|
) : (
|
||||||
|
<AppThemed />
|
||||||
|
)}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</StyledEngineProvider>
|
</StyledEngineProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createStyles } from './WithStyles';
|
import { createStyles } from './WithStyles';
|
||||||
|
|
||||||
|
// import Tone3000Dialog from './Tone3000Dialog';
|
||||||
|
import Tone3000HelpDialog from './Tone3000HelpDialog';
|
||||||
|
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
|
||||||
|
import Link from '@mui/material/Link';
|
||||||
import DraggableButtonBase from './DraggableButtonBase';
|
import DraggableButtonBase from './DraggableButtonBase';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import { Theme } from '@mui/material/styles';
|
import { Theme } from '@mui/material/styles';
|
||||||
@@ -64,6 +68,8 @@ import FilePropertyDirectorySelectDialog from './FilePropertyDirectorySelectDial
|
|||||||
import { getAlbumArtUri, getTrackTitle } from './AudioFileMetadata';
|
import { getAlbumArtUri, getTrackTitle } from './AudioFileMetadata';
|
||||||
|
|
||||||
|
|
||||||
|
const ToobNamModelFileUrl = "http://two-play.com/plugins/toob-nam#modelFile";
|
||||||
|
|
||||||
const AUTOSCROLL_TICK_DELAY = 30;
|
const AUTOSCROLL_TICK_DELAY = 30;
|
||||||
const AUTOSCROLL_THRESHOLD = 48;
|
const AUTOSCROLL_THRESHOLD = 48;
|
||||||
const AUTOSCROLL_SCROLL_PER_SECOND = 500;
|
const AUTOSCROLL_SCROLL_PER_SECOND = 500;
|
||||||
@@ -161,6 +167,8 @@ export interface FilePropertyDialogState {
|
|||||||
initialSelection: string;
|
initialSelection: string;
|
||||||
multiSelect: boolean,
|
multiSelect: boolean,
|
||||||
selectedFiles: string[],
|
selectedFiles: string[],
|
||||||
|
//openTone3000Dialog: boolean,
|
||||||
|
openTone3000Help: boolean
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -231,7 +239,9 @@ export default withStyles(
|
|||||||
copyDialogOpen: false,
|
copyDialogOpen: false,
|
||||||
initialSelection: this.props.selectedFile,
|
initialSelection: this.props.selectedFile,
|
||||||
multiSelect: false,
|
multiSelect: false,
|
||||||
selectedFiles: []
|
selectedFiles: [],
|
||||||
|
//openTone3000Dialog: false,
|
||||||
|
openTone3000Help: false
|
||||||
};
|
};
|
||||||
this.requestScroll = true;
|
this.requestScroll = true;
|
||||||
}
|
}
|
||||||
@@ -362,7 +372,7 @@ export default withStyles(
|
|||||||
dragElementDy: 0,
|
dragElementDy: 0,
|
||||||
from: index,
|
from: index,
|
||||||
to: index,
|
to: index,
|
||||||
lastMousePoint: {...this.longPressStartPoint}
|
lastMousePoint: { ...this.longPressStartPoint }
|
||||||
};
|
};
|
||||||
this.setState({ dragState: dragState });
|
this.setState({ dragState: dragState });
|
||||||
}
|
}
|
||||||
@@ -882,7 +892,6 @@ export default withStyles(
|
|||||||
style={{ marginLeft: 16, marginBottom: 8, marginRight: 8, textOverflow: "", display: "flex", flexFlow: "row wrap", alignItems: "center", justifyContent: "start" }}>
|
style={{ marginLeft: 16, marginBottom: 8, marginRight: 8, textOverflow: "", display: "flex", flexFlow: "row wrap", alignItems: "center", justifyContent: "start" }}>
|
||||||
{breadcrumbs}
|
{breadcrumbs}
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -998,8 +1007,7 @@ export default withStyles(
|
|||||||
let dragElementDy = dragState.lastMousePoint.y - this.longPressStartPoint.y;
|
let dragElementDy = dragState.lastMousePoint.y - this.longPressStartPoint.y;
|
||||||
let originalY = dragState.from * dragState.height;
|
let originalY = dragState.from * dragState.height;
|
||||||
let newY = originalY + dragElementDy;
|
let newY = originalY + dragElementDy;
|
||||||
if (newY < 0)
|
if (newY < 0) {
|
||||||
{
|
|
||||||
newY = 0;
|
newY = 0;
|
||||||
dragElementDy = -originalY;
|
dragElementDy = -originalY;
|
||||||
}
|
}
|
||||||
@@ -1040,9 +1048,26 @@ export default withStyles(
|
|||||||
() => { this.handleAutoScrollTick() }, AUTOSCROLL_TICK_DELAY);
|
() => { this.handleAutoScrollTick() }, AUTOSCROLL_TICK_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleTone3000Dialog(e: React.MouseEvent<HTMLButtonElement>) {
|
||||||
|
// e.stopPropagation();
|
||||||
|
// e.preventDefault();
|
||||||
|
|
||||||
|
// this.setState({ openTone3000Dialog: true });
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
handleTone3000Help(e: React.MouseEvent<HTMLButtonElement>) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.setState({ openTone3000Help: true });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const isTracksDirectory = this.isTracksDirectory();
|
const isTracksDirectory = this.isTracksDirectory();
|
||||||
|
const isToobNamModelFile = this.props.fileProperty.patchProperty === ToobNamModelFileUrl;
|
||||||
|
|
||||||
const classes = withStyles.getClasses(this.props);
|
const classes = withStyles.getClasses(this.props);
|
||||||
let columnWidth = this.state.columnWidth;
|
let columnWidth = this.state.columnWidth;
|
||||||
@@ -1102,7 +1127,7 @@ export default withStyles(
|
|||||||
background: this.state.reordering || this.state.multiSelect ?
|
background: this.state.reordering || this.state.multiSelect ?
|
||||||
(isDarkMode() ? "#523" : "#EDF")
|
(isDarkMode() ? "#523" : "#EDF")
|
||||||
: undefined,
|
: undefined,
|
||||||
marginBottom: 16, paddingTop: 0
|
marginBottom: 8, paddingTop: 0
|
||||||
}} >
|
}} >
|
||||||
{this.state.reordering && (
|
{this.state.reordering && (
|
||||||
<Toolbar style={{ padding: 0 }}>
|
<Toolbar style={{ padding: 0 }}>
|
||||||
@@ -1257,7 +1282,7 @@ export default withStyles(
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent style={{
|
<DialogContent dividers style={{
|
||||||
paddingLeft: 0, paddingRight: 0, paddingTop: 0, colorScheme: (isDarkMode() ? "dark" : "light"),
|
paddingLeft: 0, paddingRight: 0, paddingTop: 0, colorScheme: (isDarkMode() ? "dark" : "light"),
|
||||||
position: "relative"
|
position: "relative"
|
||||||
}}
|
}}
|
||||||
@@ -1284,7 +1309,8 @@ export default withStyles(
|
|||||||
ref={(element) => { this.listContainerElementRef = element; this.onMeasureRef(element); }}
|
ref={(element) => { this.listContainerElementRef = element; this.onMeasureRef(element); }}
|
||||||
style={{
|
style={{
|
||||||
flex: "1 1 100%", display: "flex", flexFlow: "row wrap",
|
flex: "1 1 100%", display: "flex", flexFlow: "row wrap",
|
||||||
position: "relative", justifyContent: "flex-start", alignContent: "flex-start", paddingLeft: 16, paddingBottom: 16
|
position: "relative", justifyContent: "flex-start", alignContent: "flex-start",
|
||||||
|
paddingLeft: 16, paddingBottom: 16, paddingTop: 16,
|
||||||
}}>
|
}}>
|
||||||
{
|
{
|
||||||
(this.state.columns !== 0) && // don't render until we have number of columns derived from layout.
|
(this.state.columns !== 0) && // don't render until we have number of columns derived from layout.
|
||||||
@@ -1461,45 +1487,64 @@ export default withStyles(
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
{(!this.state.reordering && !this.state.multiSelect) && (
|
{(!this.state.reordering && !this.state.multiSelect) && (
|
||||||
<>
|
<>
|
||||||
<Divider />
|
|
||||||
<DialogActions style={{ justifyContent: "stretch" }}>
|
<DialogActions style={{ justifyContent: "stretch" }}>
|
||||||
{this.state.windowWidth > 500 ? (
|
{this.state.windowWidth > 500 ? (
|
||||||
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
|
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "column nowrap" }}>
|
||||||
<IconButtonEx style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
|
{isToobNamModelFile && (
|
||||||
tooltip="Delete selected file or folder"
|
<div style={{
|
||||||
disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
|
display: "flex", flexFlow: "row nowrap", justifyContent: "center",
|
||||||
onClick={() => this.handleDelete()} >
|
alignItems: "center", width: "100%"
|
||||||
<OldDeleteIcon fontSize='small' />
|
}}>
|
||||||
</IconButtonEx>
|
<Typography variant="body2" >
|
||||||
|
Download model files from <Link
|
||||||
|
href="https://www.tone3000.com/search" target="_blank">TONE3000</Link>
|
||||||
|
</Typography>
|
||||||
|
<IconButtonEx tooltip="Help"
|
||||||
|
onClick={(e) => { this.handleTone3000Help(e); }} aria-label="help" edge="end" color="inherit" style={{ opacity: 0.6, marginLeft: 8 }}
|
||||||
|
>
|
||||||
|
<HelpOutlineIcon />
|
||||||
|
</IconButtonEx>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ButtonEx tooltip="Upload file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
|
)}
|
||||||
onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
|
|
||||||
>
|
|
||||||
<div>Upload</div>
|
|
||||||
</ButtonEx>
|
|
||||||
|
|
||||||
<ButtonEx tooltip="Download file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
|
<div style={{ flex: "1 1 100%", display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||||
onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}
|
<IconButtonEx style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
|
||||||
>
|
tooltip="Delete selected file or folder"
|
||||||
<div>Download</div>
|
disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
|
||||||
</ButtonEx>
|
onClick={() => this.handleDelete()} >
|
||||||
|
<OldDeleteIcon fontSize='small' />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
<div style={{ flex: "1 1 auto" }}> </div>
|
<ButtonEx tooltip="Upload file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
|
||||||
|
onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
|
||||||
|
>
|
||||||
|
<div>Upload</div>
|
||||||
|
</ButtonEx>
|
||||||
|
|
||||||
<Button variant="dialogSecondary" onClick={() => {
|
<ButtonEx tooltip="Download file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
|
||||||
this.props.onApply(this.props.fileProperty, this.state.initialSelection);
|
onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}
|
||||||
this.props.onCancel();
|
>
|
||||||
}} aria-label="cancel">
|
<div>Download</div>
|
||||||
Cancel
|
</ButtonEx>
|
||||||
</Button>
|
|
||||||
<Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
|
|
||||||
onClick={() => { this.openSelectedFile(); }}
|
|
||||||
|
|
||||||
disabled={(!canSelectFile)
|
<div style={{ flex: "1 1 auto" }}> </div>
|
||||||
} aria-label="select"
|
|
||||||
>
|
<Button variant="dialogSecondary" onClick={() => {
|
||||||
{okButtonText}
|
this.props.onApply(this.props.fileProperty, this.state.initialSelection);
|
||||||
</Button>
|
this.props.onCancel();
|
||||||
|
}} aria-label="cancel">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
|
||||||
|
onClick={() => { this.openSelectedFile(); }}
|
||||||
|
|
||||||
|
disabled={(!canSelectFile)
|
||||||
|
} aria-label="select"
|
||||||
|
>
|
||||||
|
{okButtonText}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: "100%" }}>
|
<div style={{ width: "100%" }}>
|
||||||
@@ -1607,7 +1652,7 @@ export default withStyles(
|
|||||||
{
|
{
|
||||||
this.state.renameDialogOpen && (
|
this.state.renameDialogOpen && (
|
||||||
<RenameDialog open={this.state.renameDialogOpen} defaultName={this.renameDefaultName()}
|
<RenameDialog open={this.state.renameDialogOpen} defaultName={this.renameDefaultName()}
|
||||||
title="Rename"
|
title="Rename"
|
||||||
onOk={(newName) => { this.setState({ renameDialogOpen: false }); this.onExecuteRename(newName) }}
|
onOk={(newName) => { this.setState({ renameDialogOpen: false }); this.onExecuteRename(newName) }}
|
||||||
onClose={() => { this.setState({ renameDialogOpen: false }); }}
|
onClose={() => { this.setState({ renameDialogOpen: false }); }}
|
||||||
acceptActionName="OK"
|
acceptActionName="OK"
|
||||||
@@ -1645,6 +1690,18 @@ export default withStyles(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
{/* {this.state.openTone3000Dialog && (
|
||||||
|
<Tone3000Dialog
|
||||||
|
open={this.state.openTone3000Dialog}
|
||||||
|
onClose={() => this.setState({ openTone3000Dialog: false })}
|
||||||
|
/>
|
||||||
|
)} */}
|
||||||
|
{this.state.openTone3000Help && (
|
||||||
|
<Tone3000HelpDialog
|
||||||
|
open={this.state.openTone3000Help}
|
||||||
|
onClose={() => this.setState({ openTone3000Help: false })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</DialogEx>
|
</DialogEx>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -420,6 +420,8 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
webSocket?: PiPedalSocket;
|
webSocket?: PiPedalSocket;
|
||||||
|
|
||||||
|
|
||||||
|
hasTone3000Auth: ObservableProperty<boolean> = new ObservableProperty<boolean>(false);
|
||||||
|
|
||||||
hasWifiDevice: ObservableProperty<boolean> = new ObservableProperty<boolean>(false);
|
hasWifiDevice: ObservableProperty<boolean> = new ObservableProperty<boolean>(false);
|
||||||
onSnapshotModified: ObservableEvent<SnapshotModifiedEvent> = new ObservableEvent<SnapshotModifiedEvent>();
|
onSnapshotModified: ObservableEvent<SnapshotModifiedEvent> = new ObservableEvent<SnapshotModifiedEvent>();
|
||||||
|
|
||||||
@@ -760,6 +762,8 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
} else if (message === "onErrorMessage") {
|
} else if (message === "onErrorMessage") {
|
||||||
this.showAlert(body as string);
|
this.showAlert(body as string);
|
||||||
|
|
||||||
|
} else if (message == "onTone3000AuthChanged") {
|
||||||
|
this.hasTone3000Auth.set(body as boolean);
|
||||||
}
|
}
|
||||||
else if (message === "onLv2PluginsChanging") {
|
else if (message === "onLv2PluginsChanging") {
|
||||||
this.onLv2PluginsChanging();
|
this.onLv2PluginsChanging();
|
||||||
@@ -1142,6 +1146,9 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
this.alsaSequencerConfiguration.set(new AlsaSequencerConfiguration().deserialize(
|
this.alsaSequencerConfiguration.set(new AlsaSequencerConfiguration().deserialize(
|
||||||
await this.getWebSocket().request<any>("getAlsaSequencerConfiguration")
|
await this.getWebSocket().request<any>("getAlsaSequencerConfiguration")
|
||||||
));
|
));
|
||||||
|
this.hasTone3000Auth.set(
|
||||||
|
await this.getWebSocket().request<boolean>("getHasTone3000Auth")
|
||||||
|
);
|
||||||
this.banks.set(new BankIndex().deserialize(await this.getWebSocket().request<any>("getBankIndex")));
|
this.banks.set(new BankIndex().deserialize(await this.getWebSocket().request<any>("getBankIndex")));
|
||||||
|
|
||||||
this.favorites.set(await this.getWebSocket().request<FavoritesList>("getFavorites"));
|
this.favorites.set(await this.getWebSocket().request<FavoritesList>("getFavorites"));
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
|
||||||
|
|
||||||
|
const Frame = styled('div', {
|
||||||
|
name: 'MuiStat', // The component name
|
||||||
|
slot: 'root', // The slot name
|
||||||
|
})(({ theme }) => ({
|
||||||
|
flex: "1 1 auto",
|
||||||
|
gap: theme.spacing(0.5),
|
||||||
|
padding: theme.spacing(3, 4),
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%"
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
interface Tone3000AuthCompleteProps {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function Tone3000AuthComplete(props: Tone3000AuthCompleteProps) {
|
||||||
|
|
||||||
|
function postApiKey() {
|
||||||
|
let url = new URL(window.location.href);
|
||||||
|
let apiKey = url.searchParams.get("api_key");
|
||||||
|
if (apiKey) {
|
||||||
|
let myUrl = new URL(window.location.href);
|
||||||
|
let port = myUrl.port;
|
||||||
|
if (port === "5173") { // when running debug server.
|
||||||
|
port = "8080";
|
||||||
|
}
|
||||||
|
let postUrl = "http://" + myUrl.hostname + ":" + port + "/var/Tone3000Auth?api_key=" + encodeURIComponent(apiKey);
|
||||||
|
fetch(postUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ api_key: apiKey })
|
||||||
|
}).then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
alert("Failed to post API key to Pipedal: " + response.statusText);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
alert("Error posting API key to Pipedal: " + error.message);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
alert("No API key found in URL.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
React.useEffect(() => {
|
||||||
|
postApiKey();
|
||||||
|
return () => { };
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Frame style={{
|
||||||
|
position: "absolute", top: 0, left: 0, right: 0, bottom: 0,
|
||||||
|
}}>
|
||||||
|
<div style={{ display: "flex", flexFlow: "row nowrap", alignItems: "start" }}>
|
||||||
|
<div style={{ flex: "1 1 1px" }} />
|
||||||
|
<div style={{
|
||||||
|
display: "flex", flexFlow: "column nowrap", alignItems: "start",
|
||||||
|
maxWidth: 600, marginLeft: "auto", marginRight: "auto", gap: 16, padding: 32
|
||||||
|
}}>
|
||||||
|
<Typography variant="h5">TONE3000 Authorization Complete</Typography>
|
||||||
|
<Typography variant="body1">
|
||||||
|
You have successfully obtained an access token from TONE3000, and can now download Toob Neural Amp models from TONE3000
|
||||||
|
directly.</Typography>
|
||||||
|
<Typography variant="body1">
|
||||||
|
Close this page and return to Pipedal in order to continue.</Typography>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: "2 2 1px" }} />
|
||||||
|
</div>
|
||||||
|
</Frame>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tone3000AuthComplete;
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||||
|
import DialogEx from './DialogEx';
|
||||||
|
import Link from '@mui/material/Link';
|
||||||
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
|
import IconButtonEx from './IconButtonEx';
|
||||||
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export interface Tone3000DialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Tone3000Dialog(props: Tone3000DialogProps) {
|
||||||
|
const { open, onClose } = props;
|
||||||
|
|
||||||
|
const model: PiPedalModel = PiPedalModelFactory.getInstance();
|
||||||
|
|
||||||
|
let [openAuthDialog, setOpenAuthDialog] = React.useState(!model.hasTone3000Auth.get());
|
||||||
|
|
||||||
|
|
||||||
|
React.useEffect(()=> {
|
||||||
|
let authListener = (value: boolean) => {
|
||||||
|
setOpenAuthDialog(!value);
|
||||||
|
}
|
||||||
|
model.hasTone3000Auth.addOnChangedHandler(authListener);
|
||||||
|
return () => {
|
||||||
|
model.hasTone3000Auth.removeOnChangedHandler(authListener);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function RedirectUrl() {
|
||||||
|
let baseUrl = new URL(window.location.href);
|
||||||
|
let result = "http://" + baseUrl.hostname + ":" + baseUrl.port + "/";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function AuthUrl() {
|
||||||
|
//return "https://www.tone3000.com/api/v1/auth?redirect_url=" + encodeURIComponent(RedirectUrl()) + "&opt_only=true";
|
||||||
|
return "https://www.tone3000.com/api/v1/auth?redirect_url=" + encodeURIComponent(RedirectUrl()) + "&otp_only=true";
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<DialogEx
|
||||||
|
tag="tone3000"
|
||||||
|
fullScreen={true}
|
||||||
|
open={open}
|
||||||
|
onEnterKey={() => { onClose(); }}
|
||||||
|
onClose={() => { onClose(); }}
|
||||||
|
aria-labelledby="tone3000-dialog-title"
|
||||||
|
aria-describedby="tone3000-dialog-description"
|
||||||
|
>
|
||||||
|
<DialogTitle id="tone3000-dialog-title">
|
||||||
|
<Toolbar style={{ padding: 0 }}>
|
||||||
|
<IconButtonEx
|
||||||
|
tooltip="Close"
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
aria-label="cancel"
|
||||||
|
style={{ opacity: 0.6 }}
|
||||||
|
onClick={() => { onClose(); }}
|
||||||
|
>
|
||||||
|
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||||
|
TONE3000 Models
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
</Toolbar>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Typography variant="body1">
|
||||||
|
This is a placeholder for the Tone 3000 dialog.
|
||||||
|
</Typography>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={onClose} color="primary">
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
{
|
||||||
|
openAuthDialog && (
|
||||||
|
<Dialog
|
||||||
|
fullScreen={true}
|
||||||
|
open={openAuthDialog}>
|
||||||
|
|
||||||
|
<DialogTitle id="tone3000-auth-dialog-title">
|
||||||
|
<Toolbar style={{ padding: 0 }}>
|
||||||
|
<IconButtonEx
|
||||||
|
tooltip="Close"
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
aria-label="cancel"
|
||||||
|
style={{ opacity: 0.6 }}
|
||||||
|
onClick={() => { onClose(); }}
|
||||||
|
>
|
||||||
|
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||||
|
TONE3000 Authorization
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
</Toolbar>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<div style={{ display: "flex", flexFlow: "row nowrap", alignItems: "start" }}>
|
||||||
|
<div style={{ flex: "1 1 1px" }} />
|
||||||
|
<div style={{
|
||||||
|
maxWidth: 500, marginLeft: "auto", marginRight: "auto",
|
||||||
|
display: "flex", flexFlow: "column nowrap", gap: 16, alignItems: "start",
|
||||||
|
}}>
|
||||||
|
<Typography variant="body1" component="div" display="block" >
|
||||||
|
The <Link href="https://www.tone3000.com" target="_blank">TONE3000 website</Link> provides an
|
||||||
|
online library of models for use with TooB Neural Amp Modeller. You can download
|
||||||
|
models from the TONE3000 website onto your local machine and then upload them to
|
||||||
|
PiPedal; or, more conveniently, you can browse the TONE3000 model database directly, and download models directly
|
||||||
|
to the PiPedal server from the TONE3000 model database in a single step.
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" component="div" display="block" >
|
||||||
|
In order to access the TONE3000 database, you must first obtain an access token from the
|
||||||
|
TONE3000 website. Clicking on the button will take you to an external website in
|
||||||
|
order to complete the authorization process.
|
||||||
|
</Typography>
|
||||||
|
<Button variant="contained" color="primary" style={{ alignSelf: "end", marginRight: 32 }}
|
||||||
|
onClick={() => {
|
||||||
|
window.open(AuthUrl(), "_blank");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Continue to TONE3000
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Typography variant="body2" component="div" display="block" style={{ marginTop: 32 }}>
|
||||||
|
Privacy statement: PiPedal will only have access to your authorization token
|
||||||
|
which does not contain personally identifying information, and which is stored locally on
|
||||||
|
your PiPedal server. Please refer to
|
||||||
|
the <Link href="https://www.tone3000.com/privacy" target="_blank">TONE3000 privacy policy</Link> for
|
||||||
|
information on how your data is used by TONE3000.
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: "2 2 1px" }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
|
</DialogEx>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tone3000Dialog;
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import DialogEx from "./DialogEx";
|
||||||
|
import DialogTitle from "@mui/material/DialogTitle";
|
||||||
|
import DialogContent from "@mui/material/DialogContent";
|
||||||
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
|
import IconButtonEx from "./IconButtonEx";
|
||||||
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Link from "@mui/material/Link";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Tone3000HelpDialog(props: {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}) {
|
||||||
|
const { open, onClose } = props;
|
||||||
|
return (
|
||||||
|
<DialogEx
|
||||||
|
tag="tone3000-help"
|
||||||
|
fullWidth={true}
|
||||||
|
maxWidth="sm"
|
||||||
|
onEnterKey={() => { onClose(); }}
|
||||||
|
onClose={() => { onClose(); }}
|
||||||
|
open={open}>
|
||||||
|
|
||||||
|
<DialogTitle >
|
||||||
|
<Toolbar style={{ padding: 0 }}>
|
||||||
|
<IconButtonEx
|
||||||
|
tooltip="Close"
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
aria-label="cancel"
|
||||||
|
style={{ opacity: 0.6 }}
|
||||||
|
onClick={() => { onClose(); }}
|
||||||
|
>
|
||||||
|
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
<Typography id="tone3000-auth-dialog-title" noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||||
|
TONE3000 Help
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
</Toolbar>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<div style={{ display: "flex", flexFlow: "row nowrap", alignItems: "start" }}>
|
||||||
|
<div style={{ flex: "1 1 1px" }} />
|
||||||
|
<div style={{
|
||||||
|
maxWidth: 500, marginLeft: "auto", marginRight: "auto",
|
||||||
|
display: "flex", flexFlow: "column nowrap", gap: 16, alignItems: "start",
|
||||||
|
}}>
|
||||||
|
<Typography variant="body1" component="div" display="block" >
|
||||||
|
The <Link href="https://www.tone3000.com" target="_blank">TONE3000 website</Link> provides a
|
||||||
|
massive collection of neural amp models that can be used by TooB Neural Amp Modeler.
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" component="div" display="block">
|
||||||
|
Using TONE3000 model files in PiPedal is a two step process. First, download model files from
|
||||||
|
the website using your browser; and then upload
|
||||||
|
the files in your browser's Downloads directory to the PiPedal server using
|
||||||
|
the <strong><em>Upload</em></strong> button. PiPedal
|
||||||
|
automatically extracts bundles of model files from zip archives, so manual extraction is unnecessary.
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" component="div" display="block">
|
||||||
|
TONE3000 is a community-driven platform where community members share Neural Amp Modeler profiles.
|
||||||
|
The site showcases the collaborative spirit of the guitar modeling community. And all of this is made
|
||||||
|
possible by Steven Atkins' <Link href="https://www.neuralampmodeler.com/">Neural Amp Modeler library</Link>, which forms the foundation and core of TooB Neural Amp Modeler and other NAM-based plugins.
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" component="div" display="block">
|
||||||
|
If you would like to profile your own amplifiers, and effects, the TONE3000 website allows you to
|
||||||
|
generate your own NAM profiles for free. Refer to
|
||||||
|
the <Link href="https://www.tone3000.com/capture">TONE3000 website</Link> for more details.
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" component="div" display="block" >
|
||||||
|
When you click on the TONE3000 link, you will be taken to an external website. The TONE3000
|
||||||
|
website is not part of PiPedal, and is not affiliated in any way with PiPedal.
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="body2" component="div" display="block" style={{ marginTop: 32 }}>
|
||||||
|
Privacy statement: PiPedal has no access to personal data used by the TONE3000 website. Please refer to
|
||||||
|
the <Link href="https://www.tone3000.com/privacy" target="_blank">TONE3000 privacy policy</Link> for
|
||||||
|
information on how your data is used by TONE3000.
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: "2 2 1px" }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</DialogContent>
|
||||||
|
</DialogEx>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default Tone3000HelpDialog;
|
||||||
Reference in New Issue
Block a user