Showing progress bar
This commit is contained in:
parent
f14feefeb0
commit
31cb3b195c
|
@ -102,6 +102,7 @@ set(SOURCE_FILES
|
||||||
src/stringTools.cpp
|
src/stringTools.cpp
|
||||||
src/NiceCmdLineOutput.cpp
|
src/NiceCmdLineOutput.cpp
|
||||||
src/TablePrinter.cpp
|
src/TablePrinter.cpp
|
||||||
|
src/ProgressBar.cpp
|
||||||
)
|
)
|
||||||
add_executable(rhubarb ${SOURCE_FILES})
|
add_executable(rhubarb ${SOURCE_FILES})
|
||||||
target_link_libraries(rhubarb ${Boost_LIBRARIES} cppFormat sphinxbase pocketSphinx)
|
target_link_libraries(rhubarb ${Boost_LIBRARIES} cppFormat sphinxbase pocketSphinx)
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "ProgressBar.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <future>
|
||||||
|
#include <chrono>
|
||||||
|
#include <format.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
ProgressBar::ProgressBar() {
|
||||||
|
updateLoopFuture = std::async(std::launch::async, &ProgressBar::updateLoop, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBar::~ProgressBar() {
|
||||||
|
done = true;
|
||||||
|
updateLoopFuture.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressBar::reportProgress(double value) {
|
||||||
|
// Make sure value is in [0..1] range
|
||||||
|
value = std::max(0.0, std::min(1.0, value));
|
||||||
|
|
||||||
|
currentProgress = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressBar::updateLoop() {
|
||||||
|
const int blockCount = 20;
|
||||||
|
const std::chrono::milliseconds animationInterval(1000 / 8);
|
||||||
|
const string animation = "|/-\\";
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
int progressBlockCount = static_cast<int>(currentProgress * blockCount);
|
||||||
|
int percent = static_cast<int>(currentProgress * 100);
|
||||||
|
string text = fmt::format("[{0}{1}] {2:3}% {3}",
|
||||||
|
string(progressBlockCount, '#'), string(blockCount - progressBlockCount, '-'),
|
||||||
|
percent,
|
||||||
|
animation[animationIndex++ % animation.size()]);
|
||||||
|
updateText(text);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(animationInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressBar::updateText(const string& text) {
|
||||||
|
// Get length of common portion
|
||||||
|
int commonPrefixLength = 0;
|
||||||
|
int commonLength = std::min(currentText.size(), text.size());
|
||||||
|
while (commonPrefixLength < commonLength && text[commonPrefixLength] == currentText[commonPrefixLength]) {
|
||||||
|
commonPrefixLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct output string
|
||||||
|
string output;
|
||||||
|
|
||||||
|
// ... backtrack to the first differing character
|
||||||
|
output.append(currentText.size() - commonPrefixLength, '\b');
|
||||||
|
|
||||||
|
// ... add new suffix
|
||||||
|
output.append(text, commonPrefixLength, text.size() - commonPrefixLength);
|
||||||
|
|
||||||
|
// ... if the new text is shorter than the old one: delete overlapping characters
|
||||||
|
int overlapCount = currentText.size() - text.size();
|
||||||
|
if (overlapCount > 0) {
|
||||||
|
output.append(overlapCount, ' ');
|
||||||
|
output.append(overlapCount, '\b');
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << output;
|
||||||
|
currentText = text;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <atomic>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
class ProgressBar {
|
||||||
|
public:
|
||||||
|
ProgressBar();
|
||||||
|
~ProgressBar();
|
||||||
|
void reportProgress(double value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateLoop();
|
||||||
|
void updateText(const std::string& text);
|
||||||
|
|
||||||
|
std::future<void> updateLoopFuture;
|
||||||
|
std::atomic<double> currentProgress { 0 };
|
||||||
|
std::atomic<bool> done { false };
|
||||||
|
|
||||||
|
std::string currentText;
|
||||||
|
int animationIndex = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
24
src/main.cpp
24
src/main.cpp
|
@ -6,9 +6,9 @@
|
||||||
#include "audioInput/WaveFileReader.h"
|
#include "audioInput/WaveFileReader.h"
|
||||||
#include "phoneExtraction.h"
|
#include "phoneExtraction.h"
|
||||||
#include "mouthAnimation.h"
|
#include "mouthAnimation.h"
|
||||||
#include "platformTools.h"
|
|
||||||
#include "appInfo.h"
|
#include "appInfo.h"
|
||||||
#include "NiceCmdLineOutput.h"
|
#include "NiceCmdLineOutput.h"
|
||||||
|
#include "ProgressBar.h"
|
||||||
|
|
||||||
using std::exception;
|
using std::exception;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
@ -81,14 +81,26 @@ int main(int argc, char *argv[]) {
|
||||||
// Parse command line
|
// Parse command line
|
||||||
cmd.parse(argc, argv);
|
cmd.parse(argc, argv);
|
||||||
|
|
||||||
// Create audio streams
|
|
||||||
unique_ptr<AudioStream> audioStream = createAudioStream(inputFileName.getValue());
|
|
||||||
|
|
||||||
// Detect phones
|
// Detect phones
|
||||||
map<centiseconds, Phone> phones = detectPhones(std::move(audioStream));
|
const int columnWidth = 30;
|
||||||
|
std::cerr << std::left;
|
||||||
|
std::cerr << std::setw(columnWidth) << "Analyzing input file";
|
||||||
|
unique_ptr<AudioStream> audioStream = createAudioStream(inputFileName.getValue());
|
||||||
|
map<centiseconds, Phone> phones;
|
||||||
|
{
|
||||||
|
ProgressBar progressBar;
|
||||||
|
phones = detectPhones(
|
||||||
|
std::move(audioStream),
|
||||||
|
[&progressBar](double progress) { progressBar.reportProgress(progress); });
|
||||||
|
}
|
||||||
|
std::cerr << "Done" << std::endl;
|
||||||
|
|
||||||
// Generate mouth shapes
|
// Generate mouth shapes
|
||||||
|
std::cerr << std::setw(columnWidth) << "Generating mouth shapes";
|
||||||
map<centiseconds, Shape> shapes = animate(phones);
|
map<centiseconds, Shape> shapes = animate(phones);
|
||||||
|
std::cerr << "Done" << std::endl;
|
||||||
|
|
||||||
|
std::cerr << std::endl;
|
||||||
|
|
||||||
// Print XML
|
// Print XML
|
||||||
ptree xmlTree = createXmlTree(inputFileName.getValue(), phones, shapes);
|
ptree xmlTree = createXmlTree(inputFileName.getValue(), phones, shapes);
|
||||||
|
@ -99,7 +111,7 @@ int main(int argc, char *argv[]) {
|
||||||
// Error parsing command-line args.
|
// Error parsing command-line args.
|
||||||
cmd.getOutput()->failure(cmd, e);
|
cmd.getOutput()->failure(cmd, e);
|
||||||
return 1;
|
return 1;
|
||||||
} catch (TCLAP::ExitException& e) {
|
} catch (TCLAP::ExitException&) {
|
||||||
// A built-in TCLAP command (like --help) has finished. Exit application.
|
// A built-in TCLAP command (like --help) has finished. Exit application.
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ using std::shared_ptr;
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::map;
|
using std::map;
|
||||||
using boost::filesystem::path;
|
using boost::filesystem::path;
|
||||||
|
using std::function;
|
||||||
|
|
||||||
unique_ptr<AudioStream> to16kHzMono(unique_ptr<AudioStream> stream) {
|
unique_ptr<AudioStream> to16kHzMono(unique_ptr<AudioStream> stream) {
|
||||||
// Downmix, if required
|
// Downmix, if required
|
||||||
|
@ -75,7 +76,7 @@ int16_t floatSampleToInt16(float sample) {
|
||||||
return static_cast<int16_t>(((sample + 1) / 2) * (INT16_MAX - INT16_MIN) + INT16_MIN);
|
return static_cast<int16_t>(((sample + 1) / 2) * (INT16_MAX - INT16_MIN) + INT16_MIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void processAudioStream(AudioStream& audioStream16kHzMono, ps_decoder_t& recognizer) {
|
void processAudioStream(AudioStream& audioStream16kHzMono, ps_decoder_t& recognizer, function<void(double)> reportProgress) {
|
||||||
// Start recognition
|
// Start recognition
|
||||||
int error = ps_start_utt(&recognizer);
|
int error = ps_start_utt(&recognizer);
|
||||||
if (error) throw runtime_error("Error starting utterance processing.");
|
if (error) throw runtime_error("Error starting utterance processing.");
|
||||||
|
@ -85,6 +86,7 @@ void processAudioStream(AudioStream& audioStream16kHzMono, ps_decoder_t& recogni
|
||||||
const int capacity = 1600; // 0.1 second capacity
|
const int capacity = 1600; // 0.1 second capacity
|
||||||
buffer.reserve(capacity);
|
buffer.reserve(capacity);
|
||||||
int sampleCount = 0;
|
int sampleCount = 0;
|
||||||
|
reportProgress(0);
|
||||||
do {
|
do {
|
||||||
// Read to buffer
|
// Read to buffer
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
|
@ -99,6 +101,7 @@ void processAudioStream(AudioStream& audioStream16kHzMono, ps_decoder_t& recogni
|
||||||
if (searchedFrameCount < 0) throw runtime_error("Error analyzing raw audio data.");
|
if (searchedFrameCount < 0) throw runtime_error("Error analyzing raw audio data.");
|
||||||
|
|
||||||
sampleCount += buffer.size();
|
sampleCount += buffer.size();
|
||||||
|
reportProgress(static_cast<double>(sampleCount) / audioStream16kHzMono.getFrameCount());
|
||||||
} while (buffer.size());
|
} while (buffer.size());
|
||||||
error = ps_end_utt(&recognizer);
|
error = ps_end_utt(&recognizer);
|
||||||
if (error) throw runtime_error("Error ending utterance processing.");
|
if (error) throw runtime_error("Error ending utterance processing.");
|
||||||
|
@ -153,7 +156,7 @@ void sphinxErrorCallback(void* user_data, err_lvl_t errorLevel, const char* form
|
||||||
*errorString += message;
|
*errorString += message;
|
||||||
}
|
}
|
||||||
|
|
||||||
map<centiseconds, Phone> detectPhones(unique_ptr<AudioStream> audioStream) {
|
map<centiseconds, Phone> detectPhones(unique_ptr<AudioStream> audioStream, function<void(double)> reportProgress) {
|
||||||
// Discard Pocketsphinx output
|
// Discard Pocketsphinx output
|
||||||
err_set_logfp(nullptr);
|
err_set_logfp(nullptr);
|
||||||
|
|
||||||
|
@ -173,7 +176,7 @@ map<centiseconds, Phone> detectPhones(unique_ptr<AudioStream> audioStream) {
|
||||||
audioStream = to16kHzMono(std::move(audioStream));
|
audioStream = to16kHzMono(std::move(audioStream));
|
||||||
|
|
||||||
// Process data
|
// Process data
|
||||||
processAudioStream(*audioStream.get(), *recognizer.get());
|
processAudioStream(*audioStream.get(), *recognizer.get(), reportProgress);
|
||||||
|
|
||||||
// Collect results into map
|
// Collect results into map
|
||||||
return getPhones(*recognizer.get());
|
return getPhones(*recognizer.get());
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <chrono>
|
|
||||||
#include <ratio>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "audioInput/AudioStream.h"
|
#include "audioInput/AudioStream.h"
|
||||||
#include "Phone.h"
|
#include "Phone.h"
|
||||||
#include "centiseconds.h"
|
#include "centiseconds.h"
|
||||||
|
|
||||||
std::map<centiseconds, Phone> detectPhones(std::unique_ptr<AudioStream> audioStream);
|
std::map<centiseconds, Phone> detectPhones(std::unique_ptr<AudioStream> audioStream, std::function<void(double)> reportProgress);
|
||||||
|
|
Loading…
Reference in New Issue