Add tool to capture Dimension EG curves
This commit is contained in:
parent
5a7aa90a8e
commit
d1333a5ab3
5 changed files with 764 additions and 0 deletions
|
|
@ -30,6 +30,7 @@ option (SFIZZ_LV2 "Enable LV2 plug-in build [default: ON]" ON)
|
|||
option (SFIZZ_VST "Enable VST plug-in build [default: OFF]" OFF)
|
||||
option (SFIZZ_BENCHMARKS "Enable benchmarks build [default: OFF]" OFF)
|
||||
option (SFIZZ_TESTS "Enable tests build [default: OFF]" OFF)
|
||||
option (SFIZZ_DEVTOOLS "Enable developer tools build [default: OFF]" OFF)
|
||||
option (SFIZZ_SHARED "Enable shared library build [default: ON]" ON)
|
||||
option (SFIZZ_USE_VCPKG "Assume that sfizz is build using vcpkg [default: OFF]" OFF)
|
||||
option (SFIZZ_STATIC_LIBSNDFILE "Link libsndfile statically [default: OFF]" OFF)
|
||||
|
|
@ -64,6 +65,10 @@ if (SFIZZ_TESTS)
|
|||
add_subdirectory (tests)
|
||||
endif()
|
||||
|
||||
if (SFIZZ_DEVTOOLS)
|
||||
add_subdirectory (devtools)
|
||||
endif()
|
||||
|
||||
# Put it at the end so that the vst/lv2 directories are registered
|
||||
if (NOT MSVC)
|
||||
include(SfizzUninstall)
|
||||
|
|
|
|||
15
devtools/CMakeLists.txt
Normal file
15
devtools/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
###############################
|
||||
# Developer tools
|
||||
|
||||
find_package(PkgConfig)
|
||||
if(PKGCONFIG_FOUND)
|
||||
pkg_check_modules(JACK "jack")
|
||||
endif()
|
||||
find_package(Qt5 COMPONENTS Widgets)
|
||||
|
||||
if(JACK_FOUND AND TARGET Qt5::Widgets)
|
||||
add_executable(sfizz_capture_eg CaptureEG.cpp)
|
||||
target_include_directories(sfizz_capture_eg PRIVATE . ${JACK_INCLUDE_DIRS})
|
||||
target_link_libraries(sfizz_capture_eg PRIVATE sfizz-sndfile Qt5::Widgets ${JACK_LIBRARIES})
|
||||
set_target_properties(sfizz_capture_eg PROPERTIES AUTOUIC ON)
|
||||
endif()
|
||||
330
devtools/CaptureEG.cpp
Normal file
330
devtools/CaptureEG.cpp
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "CaptureEG.h"
|
||||
#include "ui_CaptureEG.h"
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QMouseEvent>
|
||||
#include <QDrag>
|
||||
#include <QMimeData>
|
||||
#include <QTimer>
|
||||
#include <QStandardPaths>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <sndfile.hh>
|
||||
#include <cmath>
|
||||
|
||||
Application::Application(int& argc, char *argv[])
|
||||
: QApplication(argc, argv), _ui(new Ui::MainWindow)
|
||||
{
|
||||
setApplicationName("SfizzCaptureEG");
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
}
|
||||
|
||||
bool Application::init()
|
||||
{
|
||||
_cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||
if (_cacheDir.isEmpty()) {
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("Cannot determine the cache directory."));
|
||||
return false;
|
||||
}
|
||||
QDir(_cacheDir).mkpath(".");
|
||||
|
||||
///
|
||||
jack_client_t* client = jack_client_open(
|
||||
applicationName().toUtf8().data(), JackNoStartServer, nullptr);
|
||||
|
||||
if (!client) {
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("Cannot register a new JACK client."));
|
||||
return false;
|
||||
}
|
||||
_client.reset(client);
|
||||
|
||||
std::string clientName = jack_get_client_name(client);
|
||||
|
||||
if (!(_portAudioIn = jack_port_register(client, "audio_in", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)) ||
|
||||
!(_portMidiOut = jack_port_register(client, "midi_out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0)))
|
||||
{
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("Cannot register the JACK client ports."));
|
||||
return false;
|
||||
}
|
||||
|
||||
jack_set_process_callback(client, &processAudio, this);
|
||||
|
||||
if (jack_activate(client) != 0) {
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("Cannot activate the JACK client."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to connect Dimension if it exists
|
||||
{
|
||||
const char** synthAudioPorts = jack_get_ports(client, "^Dimension Pro:", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput);
|
||||
const char** synthMidiPorts = jack_get_ports(client, "^Dimension Pro:", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput);
|
||||
if (synthAudioPorts && *synthAudioPorts)
|
||||
jack_connect(client, *synthAudioPorts, jack_port_name(_portAudioIn));
|
||||
if (synthMidiPorts && *synthMidiPorts)
|
||||
jack_connect(client, jack_port_name(_portMidiOut), *synthMidiPorts);
|
||||
jack_free(synthAudioPorts);
|
||||
jack_free(synthMidiPorts);
|
||||
}
|
||||
|
||||
// Allocate capture buffer (capacity 30 seconds)
|
||||
double sampleRate = jack_get_sample_rate(client);
|
||||
_captureCapacity = static_cast<size_t>(std::ceil(30.0 * sampleRate));
|
||||
_captureBuffer.reset(new float[_captureCapacity]);
|
||||
// Always capture a minimum of 0.5 seconds (ensure not stopping too early)
|
||||
_captureMinFrames = static_cast<size_t>(std::ceil(0.5 * sampleRate));
|
||||
|
||||
///
|
||||
QMainWindow* window = new QMainWindow;
|
||||
_window = window;
|
||||
_ui->setupUi(window);
|
||||
window->setWindowTitle(applicationDisplayName());
|
||||
window->adjustSize();
|
||||
window->setFixedSize(window->size());
|
||||
window->show();
|
||||
|
||||
_ui->dragFileLabel->setDragFilePath(getSfzPath());
|
||||
_ui->dragFileLabel->setPixmap(
|
||||
QIcon::fromTheme("text-x-generic").pixmap(_ui->dragFileLabel->size()));
|
||||
|
||||
_ui->releaseTimeVal->setRange(0.0, 10.0);
|
||||
_ui->releaseTimeVal->setValue(5.0);
|
||||
|
||||
_ui->internalGainVal->setRange(0.1, 2.0);
|
||||
_ui->internalGainVal->setValue(0.342); // default to match Dimension
|
||||
|
||||
_ui->saveButton->setEnabled(false);
|
||||
|
||||
connect(
|
||||
_ui->envelopeEdit, &QPlainTextEdit::textChanged,
|
||||
this, [this]() { onSfzTextChanged(); });
|
||||
|
||||
connect(
|
||||
_ui->captureButton, &QPushButton::clicked,
|
||||
this, [this]() { engageCapture(); });
|
||||
|
||||
connect(
|
||||
_ui->saveButton, &QPushButton::clicked,
|
||||
this, [this]() { saveCapture(); });
|
||||
|
||||
_sfzUpdateTimer = new QTimer;
|
||||
_sfzUpdateTimer->setInterval(500);
|
||||
_sfzUpdateTimer->setSingleShot(true);
|
||||
connect(_sfzUpdateTimer, &QTimer::timeout, this, [this]() { performSfzUpdate(); });
|
||||
|
||||
_idleTimer = new QTimer;
|
||||
_idleTimer->setInterval(50);
|
||||
_idleTimer->setSingleShot(false);
|
||||
connect(_idleTimer, &QTimer::timeout, this, [this]() { performIdleChecks(); });
|
||||
|
||||
_idleTimer->start();
|
||||
|
||||
onSfzTextChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int Application::processAudio(unsigned numFrames, void* arg)
|
||||
{
|
||||
auto* self = static_cast<Application*>(arg);
|
||||
|
||||
const float* audioIn = static_cast<float*>(jack_port_get_buffer(self->_portAudioIn, numFrames));
|
||||
void* midiOut = jack_port_get_buffer(self->_portMidiOut, numFrames);
|
||||
|
||||
jack_midi_clear_buffer(midiOut);
|
||||
|
||||
if (self->_captureStatus != CaptureEngaged)
|
||||
return 0;
|
||||
|
||||
bool over = false;
|
||||
|
||||
size_t captureIndex = self->_captureFill;
|
||||
const size_t captureCapacity = self->_captureCapacity;
|
||||
const size_t captureMinFrames = self->_captureMinFrames;
|
||||
float* captureBuffer = self->_captureBuffer.get();
|
||||
|
||||
constexpr float silentThreshold = 1e-4; // -80 dB
|
||||
|
||||
long tt = self->_framesLeftToTrigger;
|
||||
long tr = self->_framesLeftToRelease;
|
||||
|
||||
for (size_t i = 0; i < numFrames && !over; ++i) {
|
||||
if (tt == 0) {
|
||||
const unsigned char noteOn[3] = {0x90, 69, 127};
|
||||
jack_midi_event_write(midiOut, i, noteOn, sizeof(noteOn));
|
||||
}
|
||||
if (tr == 0) {
|
||||
const unsigned char noteOff[3] = {0x90, 69, 0};
|
||||
jack_midi_event_write(midiOut, i, noteOff, sizeof(noteOff));
|
||||
}
|
||||
--tt;
|
||||
--tr;
|
||||
if (captureIndex == captureCapacity)
|
||||
over = true;
|
||||
else {
|
||||
captureBuffer[captureIndex++] = audioIn[i];
|
||||
if (tr < 0 && captureIndex >= captureMinFrames && audioIn[i] < silentThreshold)
|
||||
over = true;
|
||||
}
|
||||
}
|
||||
|
||||
self->_captureFill = captureIndex;
|
||||
self->_framesLeftToTrigger = tt;
|
||||
self->_framesLeftToRelease = tr;
|
||||
|
||||
if (over)
|
||||
self->_captureStatus = CaptureOver;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString Application::getSfzPath() const
|
||||
{
|
||||
return _cacheDir + "/CaptureEG.sfz";
|
||||
}
|
||||
|
||||
QString Application::getSamplePath() const
|
||||
{
|
||||
return _cacheDir + "/CaptureEG.wav";
|
||||
}
|
||||
|
||||
void Application::onSfzTextChanged()
|
||||
{
|
||||
_ui->dragFileLabel->setEnabled(false);
|
||||
_sfzUpdateTimer->start();
|
||||
}
|
||||
|
||||
void Application::engageCapture()
|
||||
{
|
||||
if (_captureStatus != CaptureIdle)
|
||||
return;
|
||||
|
||||
_ui->saveButton->setEnabled(false);
|
||||
|
||||
const double sampleRate = jack_get_sample_rate(_client.get());
|
||||
|
||||
_framesLeftToTrigger = 0;
|
||||
_framesLeftToRelease = static_cast<size_t>(std::ceil(sampleRate * _ui->releaseTimeVal->value()));
|
||||
_captureFill = 0;
|
||||
|
||||
_captureStatus = CaptureEngaged;
|
||||
}
|
||||
|
||||
void Application::saveCapture()
|
||||
{
|
||||
if (_captureStatus != CaptureIdle)
|
||||
return;
|
||||
|
||||
QString filePath = QFileDialog::getSaveFileName(
|
||||
_window, tr("Save data"), QString(), tr("Data files (*.dat)"));
|
||||
if (filePath.isEmpty())
|
||||
return;
|
||||
|
||||
QFile file(filePath);
|
||||
file.open(QFile::WriteOnly|QFile::Truncate);
|
||||
QTextStream stream(&file);
|
||||
|
||||
const float *data = _captureBuffer.get();
|
||||
size_t size = _captureFill;
|
||||
double sampleRate = jack_get_sample_rate(_client.get());
|
||||
double scaleFactor = 1.0 / _ui->internalGainVal->value();
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
stream << (i / sampleRate) << ' ' << (scaleFactor * data[i]) << '\n';
|
||||
}
|
||||
|
||||
void Application::performSfzUpdate()
|
||||
{
|
||||
const QString sfzPath = getSfzPath();
|
||||
const QString samplePath = getSamplePath();
|
||||
|
||||
QString code;
|
||||
code += "<region>\n";
|
||||
code += "sample="; code += QFileInfo(samplePath).fileName(); code += "\n";
|
||||
code += _ui->envelopeEdit->toPlainText();
|
||||
|
||||
QFile sfzFile(sfzPath);
|
||||
sfzFile.open(QFile::WriteOnly|QFile::Truncate);
|
||||
sfzFile.write(code.toUtf8());
|
||||
sfzFile.close();
|
||||
|
||||
if (!QFile::exists(samplePath)) {
|
||||
// generate all-1s sound file of 30 seconds length
|
||||
constexpr float sampleRate = 44100.0;
|
||||
constexpr float duration = 30.0;
|
||||
constexpr size_t channels = 2;
|
||||
size_t frames = static_cast<size_t>(std::ceil(sampleRate * duration));
|
||||
|
||||
SndfileHandle snd(
|
||||
samplePath.toUtf8().data(),
|
||||
SFM_WRITE, SF_FORMAT_PCM_16|SF_FORMAT_WAV, 2, sampleRate);
|
||||
|
||||
float frameData[channels];
|
||||
for (size_t i = 0; i < channels; ++i)
|
||||
frameData[i] = 1.0;
|
||||
|
||||
for (size_t i = 0; i < frames; ++i)
|
||||
snd.writef(frameData, 1);
|
||||
|
||||
snd.writeSync();
|
||||
|
||||
if (snd.error())
|
||||
QFile::remove(samplePath);
|
||||
}
|
||||
|
||||
_ui->dragFileLabel->setEnabled(true);
|
||||
}
|
||||
|
||||
void Application::performIdleChecks()
|
||||
{
|
||||
if (_captureStatus == CaptureOver) {
|
||||
_captureStatus = CaptureIdle;
|
||||
_ui->saveButton->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DragFileLabel::setDragFilePath(const QString& path)
|
||||
{
|
||||
_dragFilePath = path;
|
||||
}
|
||||
|
||||
void DragFileLabel::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!_dragFilePath.isEmpty() && event->button() == Qt::LeftButton && rect().contains(event->pos())) {
|
||||
QMimeData *mimeData = new QMimeData;
|
||||
mimeData->setUrls(QList<QUrl>() << QUrl::fromLocalFile(_dragFilePath));
|
||||
|
||||
QDrag *drag = new QDrag(this);
|
||||
drag->setMimeData(mimeData);
|
||||
drag->exec();
|
||||
drag->deleteLater();
|
||||
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
QLabel::mousePressEvent(event);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
Application app(argc, argv);
|
||||
|
||||
if (!app.init())
|
||||
return 1;
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
80
devtools/CaptureEG.h
Normal file
80
devtools/CaptureEG.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#pragma once
|
||||
#include <QApplication>
|
||||
#include <QLabel>
|
||||
#include <QString>
|
||||
#include <jack/jack.h>
|
||||
#include <jack/midiport.h>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
class QMainWindow;
|
||||
namespace Ui { class MainWindow; }
|
||||
|
||||
class Application : public QApplication {
|
||||
public:
|
||||
Application(int& argc, char *argv[]);
|
||||
~Application();
|
||||
bool init();
|
||||
|
||||
private:
|
||||
static int processAudio(unsigned numFrames, void* arg);
|
||||
|
||||
private:
|
||||
QMainWindow* _window = nullptr;
|
||||
std::unique_ptr<Ui::MainWindow> _ui;
|
||||
|
||||
QTimer* _sfzUpdateTimer = nullptr;
|
||||
QTimer* _idleTimer = nullptr;
|
||||
QString _cacheDir;
|
||||
|
||||
QString getSfzPath() const;
|
||||
QString getSamplePath() const;
|
||||
|
||||
void onSfzTextChanged();
|
||||
void engageCapture();
|
||||
void saveCapture();
|
||||
|
||||
void performSfzUpdate();
|
||||
void performIdleChecks();
|
||||
|
||||
private:
|
||||
// capture status
|
||||
enum CaptureStatus { CaptureIdle, CaptureEngaged, CaptureOver };
|
||||
std::atomic<CaptureStatus> _captureStatus { CaptureIdle };
|
||||
//
|
||||
bool _capturing = false;
|
||||
long _framesLeftToTrigger = 0;
|
||||
long _framesLeftToRelease = 0;
|
||||
std::unique_ptr<float[]> _captureBuffer;
|
||||
size_t _captureCapacity = 0;
|
||||
size_t _captureMinFrames = 0;
|
||||
size_t _captureFill = 0;
|
||||
|
||||
private:
|
||||
struct jack_client_delete {
|
||||
void operator()(jack_client_t* x) const noexcept { jack_client_close(x); }
|
||||
};
|
||||
|
||||
std::unique_ptr<jack_client_t, jack_client_delete> _client;
|
||||
jack_port_t* _portAudioIn = nullptr;
|
||||
jack_port_t* _portMidiOut = nullptr;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DragFileLabel : public QLabel {
|
||||
public:
|
||||
using QLabel::QLabel;
|
||||
|
||||
void setDragFilePath(const QString& path);
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
QString _dragFilePath;
|
||||
};
|
||||
334
devtools/CaptureEG.ui
Normal file
334
devtools/CaptureEG.ui
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>833</width>
|
||||
<height>337</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QWidget" name="" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Step 1</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Configure player</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Connect audio and MIDI to Dimension synth.</p><p>Initialize the program.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Synth internal gain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="internalGainVal">
|
||||
<property name="decimals">
|
||||
<number>3</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_6">
|
||||
<property name="title">
|
||||
<string>Step 2</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enter the time of release</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="releaseTimeVal"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Step 3</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit envelope opcodes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="envelopeEdit">
|
||||
<property name="plainText">
|
||||
<string>ampeg_attack=1
|
||||
ampeg_hold=0
|
||||
ampeg_decay=1
|
||||
ampeg_sustain=50
|
||||
ampeg_release=1
|
||||
</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Step 4</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>58</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="DragFileLabel" name="dragFileLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Drag SFZ to player</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>Step 5</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="captureButton">
|
||||
<property name="text">
|
||||
<string>Start capture</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
<string>Step 6</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="saveButton">
|
||||
<property name="text">
|
||||
<string>Save data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>DragFileLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>CaptureEG.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Loading…
Add table
Reference in a new issue