From 6c84d43d18970d290648dd182d35c2ffb7ba6888 Mon Sep 17 00:00:00 2001 From: Matus Novak <matusnov@gmail.com> Date: Fri, 10 May 2019 18:09:03 +0100 Subject: [PATCH] Added decoder-gui --- include/rew/common/directory_sink.h | 56 ++++ report/images/decoder-gui.png | Bin 0 -> 30335 bytes src/rew/decoder-cli/main.cpp | 52 +--- src/rew/decoder-gui/main.cpp | 406 +++++++++++++++++++++++++++- 4 files changed, 462 insertions(+), 52 deletions(-) create mode 100644 include/rew/common/directory_sink.h create mode 100644 report/images/decoder-gui.png diff --git a/include/rew/common/directory_sink.h b/include/rew/common/directory_sink.h new file mode 100644 index 0000000..0a398e5 --- /dev/null +++ b/include/rew/common/directory_sink.h @@ -0,0 +1,56 @@ +#ifndef REW_COMMON_DIRECTORY_SINK_H +#define REW_COMMON_DIRECTORY_SINK_H + +#include <fstream> +#include "named_raw_file.h" +#include "input.h" + +REW_NAMESPACE { + class DirectorySink: public rew::Input<rew::NamedRawFile> { + public: + DirectorySink(const std::string& destination):destination(destination) { + + } + virtual ~DirectorySink() = default; + + void process(const rew::NamedRawFile* data, const size_t length) override { + for (size_t i = 0; i < length; i++) { + const auto& namedFile = data[i]; + + auto terminator = namedFile.length; + for (size_t i = 0; i < namedFile.length; i++) { + if (namedFile.data[i] == '\0') { + terminator = i; + break; + } + } + + if (terminator != namedFile.length) { + const auto sptr = reinterpret_cast<const char*>(namedFile.data.get()); + const auto name = std::string(sptr, terminator); + + const auto contents = reinterpret_cast<const char*>(namedFile.data.get() + terminator + 1); + const auto total = namedFile.length - terminator - 1; + std::string path = destination + DELIMITER + name; + + std::cout << "Writing file: " << path << " of size: " << total << " bytes!" << std::endl; + std::fstream file(path, std::ios::out | std::ios::binary); + if (!file) { + std::cerr << "Failed to open file: " << path << " for writing!" << std::endl; + } + else { + file.write(contents, total); + } + } + else { + std::cerr << "File index: " << namedFile.index << " successfully inflated but contains bad filename!" << std::endl; + } + } + } + + private: + std::string destination; + }; +} + +#endif diff --git a/report/images/decoder-gui.png b/report/images/decoder-gui.png new file mode 100644 index 0000000000000000000000000000000000000000..a02f2850ecaf2f04a5d70f11b8aeb6f2a5fad90d GIT binary patch literal 30335 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 OVWh<5L`z>81pF5_M1B4M literal 0 HcmV?d00001 diff --git a/src/rew/decoder-cli/main.cpp b/src/rew/decoder-cli/main.cpp index 44c6e42..5e6468e 100644 --- a/src/rew/decoder-cli/main.cpp +++ b/src/rew/decoder-cli/main.cpp @@ -15,10 +15,11 @@ #include <thread> #include <future> #include <rew/decoder/decoder.h> -#include <rew/common/named_raw_file.h> +#include <rew/common/directory_sink.h> #include <rew/decoder/physical_audio_source.h> #include <rew/decoder/tcp_http_server.h> + ///===================================================================================================================== std::string getFullPath(const std::string& path) { #ifdef _WIN32 @@ -89,53 +90,6 @@ argagg::parser argparser{ { } } }; -///===================================================================================================================== -class CustomSink: public rew::Input<rew::NamedRawFile> { -public: - CustomSink(const std::string& destination):destination(destination) { - - } - virtual ~CustomSink() = default; - - void process(const rew::NamedRawFile* data, const size_t length) override { - for (size_t i = 0; i < length; i++) { - const auto& namedFile = data[i]; - - auto terminator = namedFile.length; - for (size_t i = 0; i < namedFile.length; i++) { - if (namedFile.data[i] == '\0') { - terminator = i; - break; - } - } - - if (terminator != namedFile.length) { - const auto sptr = reinterpret_cast<const char*>(namedFile.data.get()); - const auto name = std::string(sptr, terminator); - - const auto contents = reinterpret_cast<const char*>(namedFile.data.get() + terminator + 1); - const auto total = namedFile.length - terminator - 1; - std::string path = destination + DELIMITER + name; - - std::cout << "Writing file: " << path << " of size: " << total << " bytes!" << std::endl; - std::fstream file(path, std::ios::out | std::ios::binary); - if (!file) { - std::cerr << "Failed to open file: " << path << " for writing!" << std::endl; - } - else { - file.write(contents, total); - } - } - else { - std::cerr << "File index: " << namedFile.index << " successfully inflated but contains bad filename!" << std::endl; - } - } - } - -private: - std::string destination; -}; - ///===================================================================================================================== int main (const int argc, char* argv[]) { static bool terminate = false; @@ -185,7 +139,7 @@ int main (const int argc, char* argv[]) { if (!folderIsValid(outputPath)) throw std::runtime_error("Output path: \"" + outputPath + "\" is not a folder!"); - sink = std::make_shared<CustomSink>(outputPath); + sink = std::make_shared<rew::DirectorySink>(outputPath); } else if (args["serve"]) { diff --git a/src/rew/decoder-gui/main.cpp b/src/rew/decoder-gui/main.cpp index e5f2017..5f92fd2 100644 --- a/src/rew/decoder-gui/main.cpp +++ b/src/rew/decoder-gui/main.cpp @@ -13,11 +13,11 @@ #include <iostream> #include <thread> #include <future> +#include <functional> #include <rew/decoder/decoder.h> -#include <rew/common/named_raw_file.h> +#include <rew/common/directory_sink.h> #include <rew/decoder/physical_audio_source.h> -#include "rew/encoder/audio_sink.h" -#include "rew/encoder/wav_writer.h" +#include <rew/decoder/tcp_http_server.h> #ifdef _MSC_VER #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") @@ -127,6 +127,386 @@ private: std::string destination; }; +///===================================================================================================================== +static int onClosing(uiWindow *w, void *data) { + uiQuit(); + return 1; +} + +///===================================================================================================================== +static int onShouldQuit(void *data) { + uiWindow *mainwin = uiWindow(data); + + uiControlDestroy(uiControl(mainwin)); + return 1; +} + +///===================================================================================================================== +class Context; +static struct Globals { + std::shared_ptr<Context> context; + bool sourceDevice = false; + bool sourceFile = false; + bool destinationFile = false; + bool destinationServe = false; + std::string sourceFileStr; + std::string destinationFileStr; +} globals; + +///===================================================================================================================== +class Context: public std::enable_shared_from_this<Context> { +public: + Context(Globals& globals, const std::function<void()>& callback) + : globals(globals), + callback(callback) { + + if (globals.destinationFile) { + std::cout << "destination: " << globals.destinationFileStr << std::endl; + const auto outputPath = getFullPath(globals.destinationFileStr); + + if (!pathIsValid(outputPath)) + throw std::runtime_error("Output path: \"" + outputPath + "\" does not exist!"); + + if (!folderIsValid(outputPath)) + throw std::runtime_error("Output path: \"" + outputPath + "\" is not a folder!"); + + sink = std::make_shared<rew::DirectorySink>(outputPath); + } + + else if (globals.destinationServe) { + std::cout << "Starting server" << std::endl; + server = std::make_shared<rew::TcpHttpServer>("localhost", 8080); + sink = server; + server->start(); + } + + else { + throw std::runtime_error("Please select a destination"); + } + + if (globals.sourceFile) { + std::cout << "source: " << globals.sourceFileStr << std::endl; + const auto inputPath = getFullPath(globals.sourceFileStr); + + if (!pathIsValid(inputPath)) + throw std::runtime_error("Input file: \"" + inputPath + "\" does not exist!"); + + auto wav = std::make_shared<rew::WavReader>(); + auto source = std::make_shared<rew::AudioSource>(wav); + + decoder = std::make_shared<rew::Decoder>(source, sink, DEFAULT_LOW_TONE_FREQ, DEFAULT_HIGH_TONE_FREQ, DEFAULT_SAMPLE_LENGTH_MS); + + thread = std::thread([=]() -> void { + source->open(inputPath); + source->process(); + source->close(); + + if (this->callback && !this->server) { + this->stopped = true; + this->callback(); + } + }); + } + + else if (globals.sourceDevice) { + audio = std::make_shared<rew::PhysicalAudioSource>(); + + decoder = std::make_shared<rew::Decoder>(audio, sink, DEFAULT_LOW_TONE_FREQ, DEFAULT_HIGH_TONE_FREQ, DEFAULT_SAMPLE_LENGTH_MS); + + thread = std::thread([=]() -> void { + audio->start(); + audio->process(); + + if (this->callback && !this->server) { + this->stopped = true; + this->callback(); + } + }); + } + + else { + throw std::runtime_error("Please select a source"); + } + } + + ~Context() { + stop(); + } + + void stop() { + if (thread.joinable()) { + if (server) server->stop(); + if (audio) audio->close(); + thread.join(); + } + } + + inline bool isStopped() const { + return stopped; + } + +private: + Globals& globals; + std::shared_ptr<rew::Input<rew::NamedRawFile>> sink; + std::shared_ptr<rew::PhysicalAudioSource> audio; + std::shared_ptr<rew::TcpHttpServer> server; + std::shared_ptr<rew::Decoder> decoder; + std::thread thread; + std::function<void()> callback; + bool stopped = false; +}; + +///===================================================================================================================== +static void widgetsSelectSource(uiWindow* mainwin, uiBox* box) { + const auto group = uiNewGroup("Select source"); + uiGroupSetMargined(group, 1); + uiBoxAppend(box, uiControl(group), 0); + + uiBoxAppend(box, uiControl(uiNewHorizontalSeparator()), 0); + + const auto cboxDevice = uiNewCheckbox("From audio input device"); + const auto cboxFile = uiNewCheckbox("From audio file"); + + static struct CBoxData { + Globals& globals; + uiCheckbox* device; + uiCheckbox* file; + } cboxData{globals, cboxDevice, cboxFile}; + + uiBoxAppend(box, uiControl(cboxDevice), 0); + uiCheckboxOnToggled(cboxDevice, [](uiCheckbox* cbox, void* data) -> void { + auto& d = *reinterpret_cast<CBoxData*>(data); + d.globals.sourceDevice = uiCheckboxChecked(cbox); + uiCheckboxSetChecked(d.file, !d.globals.sourceDevice); + }, &cboxData); + + const auto combo = uiNewCombobox(); + const auto devices = rew::PhysicalAudioSource::getDevices(); + for (const auto& device : devices) { + if (device.inputChannels == 0) continue; + uiComboboxAppend(combo, device.name.c_str()); + } + uiBoxAppend(box, uiControl(combo), 0); + uiComboboxOnSelected(combo, [](uiCombobox* combo, void* data) -> void { + auto& d = *reinterpret_cast<CBoxData*>(data); + d.globals.sourceDevice = true; + uiCheckboxSetChecked(d.device, d.globals.sourceDevice); + uiCheckboxSetChecked(d.file, !d.globals.sourceDevice); + }, &cboxData); + + uiBoxAppend(box, uiControl(cboxFile), 0); + uiCheckboxOnToggled(cboxFile, [](uiCheckbox* cbox, void* data) -> void { + auto& d = *reinterpret_cast<CBoxData*>(data); + d.globals.sourceFile = uiCheckboxChecked(cbox); + uiCheckboxSetChecked(d.device, !d.globals.sourceFile); + }, &cboxData); + + const auto grid = uiNewGrid(); + uiGridSetPadded(grid, 1); + uiBoxAppend(box, uiControl(grid), 0); + + const auto fileButton = uiNewButton("Select Folder"); + const auto fileEntry = uiNewEntry(); + + static struct SelectFileData { + uiWindow* mainwin; + Globals& globals; + uiCheckbox* device; + uiCheckbox* file; + uiEntry* entry; + } selectFileData{mainwin, globals, cboxDevice, cboxFile, fileEntry}; + + const auto onOpenFileClicked = [](uiButton *b, void *data) -> void { + auto& d = *reinterpret_cast<SelectFileData*>(data); + + const auto filename = uiOpenFile(d.mainwin); + if (filename == NULL) { + uiEntrySetText(d.entry, ""); + return; + } + + if (!pathIsValid(filename)) { + uiMsgBoxError(d.mainwin, + "Select file error", + "You must select a valid file!"); + + uiEntrySetText(d.entry, ""); + return; + } + + uiEntrySetText(d.entry, filename); + d.globals.sourceFileStr = std::string(filename); + uiFreeText(filename); + + d.globals.sourceFile = true; + uiCheckboxSetChecked(d.device, !d.globals.sourceFile); + uiCheckboxSetChecked(d.file, d.globals.sourceFile); + }; + + uiEntryOnChanged(fileEntry, [](uiEntry* e, void* data) -> void { + auto& globals = *reinterpret_cast<Globals*>(data); + globals.sourceFileStr = std::string(uiEntryText(e)); + }, &globals); + + uiEntrySetReadOnly(fileEntry, 0); + uiButtonOnClicked(fileButton, onOpenFileClicked, &selectFileData); + uiGridAppend(grid, uiControl(fileButton), + 0, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(grid, uiControl(fileEntry), + 1, 0, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); +} + +///===================================================================================================================== +static void widgetsSelectDestination(uiWindow* mainwin, uiBox* box) { + const auto group = uiNewGroup("Select destination"); + uiGroupSetMargined(group, 1); + uiBoxAppend(box, uiControl(group), 0); + + uiBoxAppend(box, uiControl(uiNewHorizontalSeparator()), 0); + + const auto cboxServe = uiNewCheckbox("Serve on http://localhost:8080/"); + const auto cboxFile = uiNewCheckbox("Save to folder"); + + static struct CBoxData { + Globals& globals; + uiCheckbox* serve; + uiCheckbox* file; + } cboxData{globals, cboxServe, cboxFile}; + + uiBoxAppend(box, uiControl(cboxServe), 0); + uiCheckboxOnToggled(cboxServe, [](uiCheckbox* cbox, void* data) -> void { + auto& d = *reinterpret_cast<CBoxData*>(data); + d.globals.destinationServe = uiCheckboxChecked(cbox); + uiCheckboxSetChecked(d.file, !d.globals.destinationServe); + }, &cboxData); + + uiBoxAppend(box, uiControl(cboxFile), 0); + uiCheckboxOnToggled(cboxFile, [](uiCheckbox* cbox, void* data) -> void { + auto& d = *reinterpret_cast<CBoxData*>(data); + d.globals.destinationFile = uiCheckboxChecked(cbox); + uiCheckboxSetChecked(d.serve, !d.globals.destinationFile); + }, &cboxData); + + const auto grid = uiNewGrid(); + uiGridSetPadded(grid, 1); + uiBoxAppend(box, uiControl(grid), 0); + + const auto fileButton = uiNewButton("Select Folder"); + const auto fileEntry = uiNewEntry(); + + static struct SelectFileData { + uiWindow* mainwin; + Globals& globals; + uiCheckbox* serve; + uiCheckbox* file; + uiEntry* entry; + } selectFileData{mainwin, globals, cboxServe, cboxFile, fileEntry}; + + const auto onOpenFileClicked = [](uiButton *b, void *data) -> void { + auto& d = *reinterpret_cast<SelectFileData*>(data); + + const auto filename = uiOpenFile(d.mainwin); + if (filename == NULL) { + uiEntrySetText(d.entry, ""); + return; + } + + if (!folderIsValid(filename)) { + uiMsgBoxError(d.mainwin, + "Select folder error", + "You must select a valid folder, not a file!"); + + uiEntrySetText(d.entry, ""); + return; + } + + uiEntrySetText(d.entry, filename); + d.globals.destinationFileStr = std::string(filename); + uiFreeText(filename); + + d.globals.destinationFile = true; + uiCheckboxSetChecked(d.serve, !d.globals.destinationFile); + uiCheckboxSetChecked(d.file, d.globals.destinationFile); + }; + + uiEntryOnChanged(fileEntry, [](uiEntry* e, void* data) -> void { + auto& globals = *reinterpret_cast<Globals*>(data); + globals.destinationFileStr = std::string(uiEntryText(e)); + }, &globals); + + uiEntrySetReadOnly(fileEntry, 0); + uiButtonOnClicked(fileButton, onOpenFileClicked, &selectFileData); + uiGridAppend(grid, uiControl(fileButton), + 0, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(grid, uiControl(fileEntry), + 1, 0, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); +} + +///===================================================================================================================== +static void widgets(uiWindow* mainwin) { + const auto box = uiNewVerticalBox(); + uiBoxSetPadded(box, 1); + uiWindowSetChild(mainwin, uiControl(box)); + + widgetsSelectSource(mainwin, box); + widgetsSelectDestination(mainwin, box); + + uiBoxAppend(box, uiControl(uiNewHorizontalSeparator()), 0); + + auto status = uiNewLabel("Press start to begin"); + uiBoxAppend(box, uiControl(status), 0); + + auto progressbar = uiNewProgressBar(); + uiProgressBarSetValue(progressbar, 0); + uiBoxAppend(box, uiControl(progressbar), 0); + + auto buttonGrid = uiNewGrid(); + uiGridSetPadded(buttonGrid, 1); + + static struct StatusData { + uiWindow* mainwin; + Globals& globals; + uiProgressBar* progress; + uiLabel* label; + } statusData{mainwin, globals, progressbar, status}; + + auto button = uiNewButton("Start"); + uiButtonOnClicked(button, [](uiButton *b, void *data) -> void { + const auto callback = [=]() { + auto& d = *reinterpret_cast<StatusData*>(data); + + uiProgressBarSetValue(d.progress, 0); + uiButtonSetText(b, "Start"); + }; + + auto& d = *reinterpret_cast<StatusData*>(data); + + if (!d.globals.context || d.globals.context->isStopped()) { + try { + d.globals.context = std::make_unique<Context>(d.globals, callback); + uiProgressBarSetValue(d.progress, -1); + uiButtonSetText(b, "Stop"); + } catch (std::exception& e) { + uiMsgBoxError(d.mainwin, "Error", e.what()); + } + } else { + d.globals.context.reset(); + uiProgressBarSetValue(d.progress, 0); + uiButtonSetText(b, "Start"); + } + + }, &statusData); + uiGridAppend(buttonGrid, uiControl(button), + 1, 0, 1, 1, + 1, uiAlignCenter, 0, uiAlignFill); + + uiBoxAppend(box, uiControl(buttonGrid), 0); +} + ///===================================================================================================================== int main (const int argc, char* argv[]) { #ifdef _WIN32 @@ -134,6 +514,26 @@ int main (const int argc, char* argv[]) { #endif try { + uiInitOptions o = {0}; + const char* err; + if ((err = uiInit(&o)) != nullptr) { + const auto e = std::string(err); + uiFreeInitError(err); + throw std::runtime_error(e); + } + + const auto mainwin = uiNewWindow("Decoder", 640, 400, false); + uiWindowSetMargined(mainwin, 1); + uiWindowOnClosing(mainwin, onClosing, NULL); + uiOnShouldQuit(onShouldQuit, mainwin); + + widgets(mainwin); + + uiControlShow(uiControl(mainwin)); + uiMain(); + uiUninit(); + return 0; + return EXIT_SUCCESS; } catch (const std::exception& e) { -- GitLab