Initial commit, basically working I guess

This commit is contained in:
Nico 2023-03-27 21:02:15 +01:00
parent d984a7c36e
commit 769cf427cb
6 changed files with 973 additions and 0 deletions

73
.gitignore vendored Normal file
View File

@ -0,0 +1,73 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe

26
AmigaConverterQT.pro Normal file
View File

@ -0,0 +1,26 @@
QT += core gui multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
LIBS +=
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

12
main.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

469
mainwindow.cpp Normal file
View File

@ -0,0 +1,469 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
#include <QMessageBox>
#include <QFileDialog>
#include <QVector>
#include <QMetaType>
#include <QTemporaryFile>
#include <QProcess>
#include <QMediaPlayer>
#include <QAudioOutput>
#include <algorithm>
using namespace std;
// TODO
// add a bunch of actual error handling
// saving
// types
struct Sample {
QString path;
QString name;
bool normalise = true;
bool ready = false;
bool lowcutOn = false;
bool highcutOn = false;
int lowcutFrequency = 50;
int highcutFrequency = 18000;
QString rate = "C-2";
};
Q_DECLARE_METATYPE(Sample);
static QMap<QString,int> protrackerTable{
{"C-1",4144},
{"C#1",4390},
{"D-1",4655},
{"D#1",4926},
{"E-1",5231},
{"F-1",5542},
{"F#1",5872},
{"G-1",6223},
{"G#1",6593},
{"A-1",6982},
{"A#1",7389},
{"B-1",7830},
{"C-2",8287},
{"C#2",8779},
{"D-2",9309},
{"D#2",9852},
{"E-2",10463},
{"F-2",11084},
{"F#2",11745},
{"G-2",12445},
{"G#2",13185},
{"A-2",13964},
{"A#2",14779},
{"B-2",15694},
{"C-3",16574},
{"C#3",17559},
{"D-3",18668},
{"D#3",19705},
{"E-3",20864},
{"F-3",22168},
{"F#3",23489},
{"G-3",24803},
{"G#3",26273},
{"A-3",27928},
};
// variables
int sampleSelection;
QString OutputPath;
QList<Sample> samples;
// helpers
int logslider(int position) {
// position will be between 0 and 1000
int minp = 0;
int maxp = 100;
// The result should be between 50 and 18000
int minv = log(50);
int maxv = log(18000);
// calculate adjustment factor
int scale = (maxv-minv) / (maxp-minp);
return exp(minv + scale*(position-minp));
}
void quitDialog(void) {
QMessageBox msgBox;
msgBox.setText("Are you sure you want to quit?");
msgBox.setStandardButtons(QMessageBox::No|QMessageBox::Yes);
msgBox.setDefaultButton(QMessageBox::No);
int reply = msgBox.exec();
if(reply == QMessageBox::Yes)
QCoreApplication::quit();
}
// does a sample conversion with sox. returns the path to the temporary file containing the converted sample
QString convert(Sample s, bool preview) {
// get a temporary file name
QTemporaryFile f;
QString path;
if(f.open()) {
path = f.fileName();
}
f.close();
if(preview)
path.append(".wav");
else
path.append(".8svx");
int rate = protrackerTable[s.rate];
// construct sox command line
QStringList args = QStringList{s.path,QString("-b"),QString("8"),QString{"-c"},QString("1"), path}; // 8bit mono
args << QString("remix") << QString("-");
if(s.normalise) args << QString("norm") << QString("-6");
if(s.highcutOn) args << QString("lowpass") << QString("-1") << QString::number(s.highcutFrequency);
if(s.lowcutOn) args << QString("highpass") << QString("-1") << QString::number(s.lowcutFrequency);
args << QString("rate") << QString::number(rate); // TODO adjust this based on user selection
args += QString("dither -S").split(' ');
qDebug() << args;
QProcess sox;
sox.setProgram("sox");
sox.setArguments(args);
sox.start();
sox.waitForFinished();
qDebug() << sox.readAllStandardOutput();
qDebug() << sox.readAllStandardError();
return path;
}
// mainwindow proper
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// initialise frequency combo box
// TODO make this in order and actually matter
// invert the table
QList<QString> k = protrackerTable.keys();
QList<QPair<int,QString>> inv;
// Populate the inverted list
for (auto k : protrackerTable.keys()) {
inv.append(QPair<int,QString>(protrackerTable[k], k));
}
// sort it
std::sort(std::begin(inv), std::end(inv));
// add things to combobox
for (QPair<int,QString> p : inv) {
this->ui->FrequencyCombo->addItem(p.second,QVariant(p.first));
}
this->ui->centralwidget->setEnabled(false);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *event) {
event->ignore();
quitDialog();
}
void MainWindow::on_actionQuit_triggered()
{
quitDialog();
}
// updates the UI components relating to the current selected sample:
void MainWindow::updateSampleUI() {
Ui::MainWindow* u = this->ui;
QVariant v = u->sampleCombo->currentData();
Sample s = v.value<Sample>();
u->NormaliseCheckbox->setChecked(s.normalise);
u->lowCutCheckbox->setChecked(s.lowcutOn);
u->highCutCheckbox->setChecked(s.lowcutOn);
u->lowCutSlider->setValue(s.lowcutFrequency);
u->highCutSlider->setValue(abs(s.highcutFrequency-20000+20)); // reverse it
// update sample rate box
int t = u->FrequencyCombo->findText(s.rate);
u->FrequencyCombo->setCurrentIndex(t);
// fix enable/disable for sliders
this->updateSliders();
}
void MainWindow::updateSliders() {
this->ui->lowCutSlider->setEnabled(this->ui->lowCutCheckbox->isChecked());
this->ui->highCutSlider->setEnabled(this->ui->highCutCheckbox->isChecked());
}
bool clearDialog() {
QMessageBox msgBox;
msgBox.setText("Lose current samples?");
msgBox.setStandardButtons(QMessageBox::No|QMessageBox::Yes);
msgBox.setDefaultButton(QMessageBox::No);
int reply = msgBox.exec();
if(reply == QMessageBox::No)
return false;
return true;
}
void MainWindow::on_actionLoad_Samples_triggered()
{
if(this->ui->sampleCombo->count() != 0) {
if(!clearDialog())
return;
}
this->ui->sampleCombo->clear();
QStringList fileNames = QFileDialog::getOpenFileNames(this,
tr("Open Samples"), ".", tr("Wave Files (*.wav)"));
if(fileNames.count() != 0) {
for(QString path : fileNames) {
Sample tmp;
tmp.path = path;
tmp.name = path.split(QChar('/')).last();
QVariant var;
var.setValue(tmp);
this->ui->sampleCombo->addItem(tmp.name.append(" | Not Ready"),var);
}
this->ui->centralwidget->setEnabled(true);
this->updateSampleUI();
}
}
void MainWindow::on_browseButton_clicked()
{
QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
"/home",
QFileDialog::ShowDirsOnly
| QFileDialog::DontResolveSymlinks);
this->ui->DirectoryName->setText(dir);
OutputPath = dir;
}
void MainWindow::on_NormaliseCheckbox_clicked()
{
QComboBox* s = this->ui->sampleCombo;
if(s->count() > 0) {
Sample d = s->currentData().value<Sample>();
d.normalise = !d.normalise;
QVariant v;
v.setValue(d);
s->setItemData(s->currentIndex(),v);
}
}
void MainWindow::on_sampleCombo_currentIndexChanged(int index)
{
this->updateSampleUI();
}
void MainWindow::on_lowCutCheckbox_clicked()
{
QComboBox* s = this->ui->sampleCombo;
if(s->count() > 0) {
Sample d = s->currentData().value<Sample>();
d.lowcutOn = !d.lowcutOn;
QVariant v;
v.setValue(d);
s->setItemData(s->currentIndex(),v);
}
this->updateSliders();
}
void MainWindow::on_highCutCheckbox_clicked()
{
QComboBox* s = this->ui->sampleCombo;
if(s->count() > 0) {
Sample d = s->currentData().value<Sample>();
d.highcutOn = !d.highcutOn;
QVariant v;
v.setValue(d);
s->setItemData(s->currentIndex(),v);
}
this->updateSliders();
}
void MainWindow::on_lowCutSlider_sliderMoved(int position)
{
QComboBox* s = this->ui->sampleCombo;
if(s->count() > 0) {
Sample d = s->currentData().value<Sample>();
d.lowcutFrequency = position;
QVariant v;
v.setValue(d);
s->setItemData(s->currentIndex(),v);
}
this->updateSliders();
}
void MainWindow::on_highCutSlider_sliderMoved(int position)
{
QComboBox* s = this->ui->sampleCombo;
if(s->count() > 0) {
Sample d = s->currentData().value<Sample>();
d.highcutFrequency = abs(position-20000+20);
QVariant v;
v.setValue(d);
s->setItemData(s->currentIndex(),v);
}
this->updateSliders();
}
void MainWindow::on_actionClear_All_triggered()
{
if(this->ui->sampleCombo->count() != 0) {
if(!clearDialog())
return;
}
this->ui->sampleCombo->clear();
this->ui->centralwidget->setEnabled(false);
}
void MainWindow::on_readyButton_clicked()
{
QComboBox* s = this->ui->sampleCombo;
if(s->count() > 0) {
Sample d = s->currentData().value<Sample>();
d.ready = !d.ready;
QVariant v;
v.setValue(d);
s->setItemData(s->currentIndex(),v);
QString newLabel;
if(d.ready) {
newLabel = d.name.append(" | Ready");
} else {
newLabel = d.name.append(" | Not Ready");
}
s->setItemText(s->currentIndex(),newLabel);
}
this->updateSliders();
}
// TODO playback here, the sample's getting made fine! sox just being funky
void MainWindow::on_previewButton_clicked()
{
if(this->ui->sampleCombo->count() != 0) {
QVariant d = this->ui->sampleCombo->currentData();
QString path = convert(d.value<Sample>(), true);
// use sox for playback lol
QMediaPlayer* player = new QMediaPlayer;
QAudioOutput* audioOutput = new QAudioOutput;
QFile file = QFile(path);
QLocale l = QLocale();
QString size = l.formattedDataSize(file.size(),0);
this->ui->SizeLabel->setText(size);
player->setAudioOutput(audioOutput);
player->setSource(QUrl::fromLocalFile(path));
audioOutput->setVolume(50);
player->play();
}
}
void MainWindow::on_FrequencyCombo_activated(int index)
{
QComboBox* s = this->ui->sampleCombo;
if(s->count() > 0) {
Sample d = s->currentData().value<Sample>();
d.rate = this->ui->FrequencyCombo->currentText();
QVariant v;
v.setValue(d);
s->setItemData(s->currentIndex(),v);
}
this->updateSliders();
}
void MainWindow::on_ExportButton_clicked()
{
if(OutputPath.isEmpty()) {
QMessageBox box;
box.setText("no output directory selected!");
box.exec();
return;
}
bool ready = true;
QList<Sample> samples;
QComboBox* b = this->ui->sampleCombo;
for(int i = 0;i < b->count(); i++) {
Sample s = b->itemData(i).value<Sample>();
samples += s;
if(!s.ready) {
ready = false;
}
}
if(!ready) {
QMessageBox msgBox;
msgBox.setText("Not all samples are marked as ready. Do you want to proceed?");
msgBox.setStandardButtons(QMessageBox::No|QMessageBox::Yes);
msgBox.setDefaultButton(QMessageBox::No);
int reply = msgBox.exec();
if(reply == QMessageBox::Yes)
ready = true;
}
if(!ready) return;
for(Sample s : samples) {
QString p = convert(s,false);
QString dest = s.name;
dest.replace("wav","8svx");
dest = OutputPath + QString("/") + dest;
qDebug() << dest;
QFile::copy(p,dest);
}
}

55
mainwindow.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QCloseEvent>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void updateSampleUI();
void updateSliders();
private slots:
void on_actionQuit_triggered();
void on_actionLoad_Samples_triggered();
void closeEvent(QCloseEvent *event);
void on_browseButton_clicked();
void on_sampleCombo_currentIndexChanged(int index);
void on_NormaliseCheckbox_clicked();
void on_lowCutCheckbox_clicked();
void on_highCutCheckbox_clicked();
void on_lowCutSlider_sliderMoved(int position);
void on_highCutSlider_sliderMoved(int position);
void on_actionClear_All_triggered();
void on_readyButton_clicked();
void on_previewButton_clicked();
void on_FrequencyCombo_activated(int index);
void on_ExportButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

338
mainwindow.ui Normal file
View File

@ -0,0 +1,338 @@
<?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>343</width>
<height>331</height>
</rect>
</property>
<property name="windowTitle">
<string>AmigaConverterQT</string>
</property>
<widget class="QWidget" name="centralwidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="label">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Sample:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sampleCombo">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Currently Selected Sample</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="FrequencyCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Currently Selected Sample</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="NormaliseCheckbox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Normalise</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QCheckBox" name="lowCutCheckbox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Low Cut</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="lowCutSlider">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimum">
<number>20</number>
</property>
<property name="maximum">
<number>20000</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::NoTicks</enum>
</property>
<property name="tickInterval">
<number>1000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QCheckBox" name="highCutCheckbox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>High Cut</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="highCutSlider">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimum">
<number>20</number>
</property>
<property name="maximum">
<number>20000</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::NoTicks</enum>
</property>
<property name="tickInterval">
<number>1000</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QPushButton" name="previewButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Preview</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="SizeLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>0b</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="readyButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Toggle Ready</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="DirectoryName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Select a Directory</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browseButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ExportButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Export</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>343</width>
<height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionLoad_Samples"/>
<addaction name="actionClear_All"/>
<addaction name="actionQuit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionLoad_Samples">
<property name="text">
<string>Load Samples</string>
</property>
<property name="shortcut">
<string>Ctrl+L</string>
</property>
</action>
<action name="actionClear_All">
<property name="text">
<string>Clear All</string>
</property>
<property name="shortcut">
<string>Ctrl+W</string>
</property>
</action>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>