commit
14eaf9b9ef
11 changed files with 493 additions and 0 deletions
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
# CLion |
||||
.idea/ |
||||
|
||||
# Cmake files |
||||
Makefile |
||||
CMakeFiles/ |
||||
cmake-build-debug/ |
||||
build/ |
||||
CMakeCache.txt |
||||
CMakeLists.txt.user |
||||
.cmake |
||||
*_autogen/ |
||||
cmake_install.cmake |
||||
*.cbp |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
cmake_minimum_required(VERSION 3.17) |
||||
project(voice_toolkit) |
||||
|
||||
# Comment this to get a prod build |
||||
add_definitions(-DPROJECT_TESTING=true) |
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON) |
||||
set(CMAKE_AUTOUIC ON) |
||||
set(CMAKE_AUTOMOC ON) |
||||
set(CMAKE_AUTORCC ON) |
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON) |
||||
set(CMAKE_CXX_STANDARD 20) |
||||
file(GLOB FILES |
||||
# This is needed for QT, don't question it. |
||||
include/*.h |
||||
include/**/*.h |
||||
|
||||
# Sauce |
||||
src/*.cpp |
||||
src/**/*.cpp |
||||
|
||||
# Libs we use |
||||
libs/**/*.c |
||||
libs/**/*.cpp |
||||
|
||||
# Autogenerated UI |
||||
src/ui/mainwindow.ui) |
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} cmake/) |
||||
set(EXECUTABLE_OUTPUT_PATH build/) |
||||
|
||||
find_package(Qt5 COMPONENTS Core Widgets REQUIRED) |
||||
find_package(Qwt REQUIRED) |
||||
|
||||
include_directories(include/ libs/ ${QT}) |
||||
add_executable(voice_toolkit ${FILES}) |
||||
target_link_libraries(voice_toolkit PRIVATE portaudiocpp Qt5::Widgets Qt5::Core qwt) # IDK how to make this a static link |
@ -0,0 +1,118 @@
@@ -0,0 +1,118 @@
|
||||
# Qt Widgets for Technical Applications |
||||
# available at http://www.http://qwt.sourceforge.net/ |
||||
# |
||||
# The module defines the following variables: |
||||
# QWT_FOUND - the system has Qwt |
||||
# QWT_INCLUDE_DIR - where to find qwt_plot.h |
||||
# QWT_INCLUDE_DIRS - qwt includes |
||||
# QWT_LIBRARY - where to find the Qwt library |
||||
# QWT_LIBRARIES - aditional libraries |
||||
# QWT_MAJOR_VERSION - major version |
||||
# QWT_MINOR_VERSION - minor version |
||||
# QWT_PATCH_VERSION - patch version |
||||
# QWT_VERSION_STRING - version (ex. 5.2.1) |
||||
# QWT_ROOT_DIR - root dir (ex. /usr/local) |
||||
|
||||
#============================================================================= |
||||
# Copyright 2010-2013, Julien Schueller |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are met: |
||||
# |
||||
# 1. Redistributions of source code must retain the above copyright notice, this |
||||
# list of conditions and the following disclaimer. |
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, |
||||
# this list of conditions and the following disclaimer in the documentation |
||||
# and/or other materials provided with the distribution. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
# The views and conclusions contained in the software and documentation are those |
||||
# of the authors and should not be interpreted as representing official policies, |
||||
# either expressed or implied, of the FreeBSD Project. |
||||
#============================================================================= |
||||
|
||||
|
||||
find_path ( QWT_INCLUDE_DIR |
||||
NAMES qwt_plot.h |
||||
HINTS ${QT_INCLUDE_DIR} |
||||
PATH_SUFFIXES qwt qwt-qt3 qwt-qt4 qwt-qt5 |
||||
) |
||||
|
||||
set ( QWT_INCLUDE_DIRS ${QWT_INCLUDE_DIR} ) |
||||
|
||||
# version |
||||
set ( _VERSION_FILE ${QWT_INCLUDE_DIR}/qwt_global.h ) |
||||
if ( EXISTS ${_VERSION_FILE} ) |
||||
file ( STRINGS ${_VERSION_FILE} _VERSION_LINE REGEX "define[ ]+QWT_VERSION_STR" ) |
||||
if ( _VERSION_LINE ) |
||||
string ( REGEX REPLACE ".*define[ ]+QWT_VERSION_STR[ ]+\"(.*)\".*" "\\1" QWT_VERSION_STRING "${_VERSION_LINE}" ) |
||||
string ( REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1" QWT_MAJOR_VERSION "${QWT_VERSION_STRING}" ) |
||||
string ( REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\2" QWT_MINOR_VERSION "${QWT_VERSION_STRING}" ) |
||||
string ( REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\3" QWT_PATCH_VERSION "${QWT_VERSION_STRING}" ) |
||||
endif () |
||||
endif () |
||||
|
||||
|
||||
# check version |
||||
set ( _QWT_VERSION_MATCH TRUE ) |
||||
if ( Qwt_FIND_VERSION AND QWT_VERSION_STRING ) |
||||
if ( Qwt_FIND_VERSION_EXACT ) |
||||
if ( NOT Qwt_FIND_VERSION VERSION_EQUAL QWT_VERSION_STRING ) |
||||
set ( _QWT_VERSION_MATCH FALSE ) |
||||
endif () |
||||
else () |
||||
if ( QWT_VERSION_STRING VERSION_LESS Qwt_FIND_VERSION ) |
||||
set ( _QWT_VERSION_MATCH FALSE ) |
||||
endif () |
||||
endif () |
||||
endif () |
||||
|
||||
|
||||
find_library ( QWT_LIBRARY |
||||
NAMES qwt qwt-qt3 qwt-qt4 qwt-qt5 |
||||
HINTS ${QT_LIBRARY_DIR} |
||||
) |
||||
|
||||
set ( QWT_LIBRARIES ${QWT_LIBRARY} ) |
||||
|
||||
|
||||
# try to guess root dir from include dir |
||||
if ( QWT_INCLUDE_DIR ) |
||||
string ( REGEX REPLACE "(.*)/include.*" "\\1" QWT_ROOT_DIR ${QWT_INCLUDE_DIR} ) |
||||
# try to guess root dir from library dir |
||||
elseif ( QWT_LIBRARY ) |
||||
string ( REGEX REPLACE "(.*)/lib[/|32|64].*" "\\1" QWT_ROOT_DIR ${QWT_LIBRARY} ) |
||||
endif () |
||||
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments |
||||
include ( FindPackageHandleStandardArgs ) |
||||
if ( CMAKE_VERSION LESS 2.8.3 ) |
||||
find_package_handle_standard_args( Qwt DEFAULT_MSG QWT_LIBRARY QWT_INCLUDE_DIR _QWT_VERSION_MATCH ) |
||||
else () |
||||
find_package_handle_standard_args( Qwt REQUIRED_VARS QWT_LIBRARY QWT_INCLUDE_DIR _QWT_VERSION_MATCH VERSION_VAR QWT_VERSION_STRING ) |
||||
endif () |
||||
|
||||
|
||||
mark_as_advanced ( |
||||
QWT_LIBRARY |
||||
QWT_LIBRARIES |
||||
QWT_INCLUDE_DIR |
||||
QWT_INCLUDE_DIRS |
||||
QWT_MAJOR_VERSION |
||||
QWT_MINOR_VERSION |
||||
QWT_PATCH_VERSION |
||||
QWT_VERSION_STRING |
||||
QWT_ROOT_DIR |
||||
) |
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Created by mart on 10/24/20.
|
||||
//
|
||||
|
||||
#ifndef VOICE_TOOLKIT_TRANSFORMER_H |
||||
#define VOICE_TOOLKIT_TRANSFORMER_H |
||||
|
||||
#include <portaudiocpp/PortAudioCpp.hxx> |
||||
#include "constants.h" |
||||
#include <unistd.h> |
||||
|
||||
namespace pa = portaudio; |
||||
|
||||
class AudioTransformer : public pa::CallbackInterface { |
||||
int fd; |
||||
|
||||
public: |
||||
AudioTransformer(int fd); |
||||
|
||||
int paCallbackFun(const void *inputBuffer, void *outputBuffer, unsigned long numFrames, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) override; |
||||
}; |
||||
|
||||
|
||||
#endif //VOICE_TOOLKIT_TRANSFORMER_H
|
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Created by mart on 10/22/20.
|
||||
//
|
||||
|
||||
#ifndef VOICE_TOOLKIT_CONSTANTS_H |
||||
#define VOICE_TOOLKIT_CONSTANTS_H |
||||
|
||||
#define SAMPLERATE 48000.0 |
||||
#define CHANNELS 2 |
||||
#define FRAMES (int)(SAMPLERATE*0.02) |
||||
|
||||
#define MS * 1000L |
||||
#define S * 1000L MS |
||||
|
||||
#endif //VOICE_TOOLKIT_CONSTANTS_H
|
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
#ifndef MAINWINDOW_H |
||||
#define MAINWINDOW_H |
||||
|
||||
#include <QMainWindow> |
||||
#include <qwt/qwt_plot_curve.h> |
||||
#include <QtCore/QThread> |
||||
#include <include/audio/transformer.h> |
||||
#include "constants.h" |
||||
|
||||
QT_BEGIN_NAMESPACE |
||||
namespace Ui { class MainWindow; } |
||||
QT_END_NAMESPACE |
||||
|
||||
class Worker : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
int fd; |
||||
|
||||
public: |
||||
Worker(int pipe) { |
||||
this->fd = pipe; |
||||
} |
||||
|
||||
public slots: |
||||
void doWork(); |
||||
|
||||
signals: |
||||
void resultReady(void* result); |
||||
}; |
||||
|
||||
|
||||
class MainWindow : public QMainWindow { |
||||
Q_OBJECT |
||||
|
||||
QThread workerThread; |
||||
AudioTransformer* transformer; |
||||
|
||||
QwtPlotCurve* curves[CHANNELS]; |
||||
double xData[FRAMES]; |
||||
public: |
||||
MainWindow(QWidget *parent = nullptr, int pipe = 0, AudioTransformer* transformer = nullptr); |
||||
~MainWindow(); |
||||
|
||||
private: |
||||
Ui::MainWindow *ui; |
||||
|
||||
public slots: |
||||
void handleResults(void* res); |
||||
|
||||
signals: |
||||
void operate(); |
||||
|
||||
}; |
||||
#endif // MAINWINDOW_H
|
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Created by mart on 10/22/20.
|
||||
//
|
||||
|
||||
#ifndef VOICE_TOOLKIT_UTIL_H |
||||
#define VOICE_TOOLKIT_UTIL_H |
||||
|
||||
#include "constants.h" |
||||
|
||||
float* interleave(const float** channels) { |
||||
auto arr = new float[FRAMES * CHANNELS]; |
||||
for (int i = 0; i < FRAMES; i++) { |
||||
for (int j = 0; j < CHANNELS; j++) { |
||||
arr[CHANNELS*i + j] = channels[j][i]; |
||||
} |
||||
} |
||||
return arr; |
||||
} |
||||
|
||||
float** deinterleave(const float* buffer) { |
||||
auto arr = new float*[CHANNELS]; |
||||
for (int i = 0; i < CHANNELS; i++) { |
||||
arr[i] = new float[FRAMES]; |
||||
} |
||||
for (int i = 0; i < FRAMES; i++) { |
||||
for (int j = 0; j < CHANNELS; j++) { |
||||
arr[j][i] = buffer[CHANNELS*i + j]; |
||||
} |
||||
} |
||||
return arr; |
||||
} |
||||
|
||||
#endif //VOICE_TOOLKIT_UTIL_H
|
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Created by mart on 10/24/20.
|
||||
//
|
||||
|
||||
#include "include/audio/transformer.h" |
||||
|
||||
AudioTransformer::AudioTransformer(int fd) { |
||||
this->fd = fd; |
||||
} |
||||
|
||||
int AudioTransformer::paCallbackFun(const void *inputBuffer, void *outputBuffer, unsigned long numFrames, |
||||
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) { |
||||
auto in = (float**)inputBuffer; |
||||
auto out = (float**)outputBuffer; |
||||
|
||||
for (int i = 0; i < CHANNELS; i++) { |
||||
auto inBuf = in[i]; |
||||
auto outBuf = out[i]; |
||||
for (int j = 0; j < FRAMES; j++) { |
||||
outBuf[j] = inBuf[j]; |
||||
} |
||||
} |
||||
|
||||
// inBuf is wet signal, outBuf is dry signal.
|
||||
// TODO: Process
|
||||
|
||||
#ifndef PROJECT_TESTING |
||||
if (true) return paContinue; |
||||
#endif |
||||
|
||||
for (int i = 0; i < CHANNELS; i++) { |
||||
auto yData = new double[FRAMES]; |
||||
for (int j = 0; j < FRAMES; j++) { |
||||
yData[j] = (double)out[i][j]; |
||||
} |
||||
|
||||
write(fd, (char*)yData, FRAMES*sizeof(double)); |
||||
// curves2[i]->setRawSamples(xData, yData, FRAMES);
|
||||
} |
||||
|
||||
return paContinue; |
||||
} |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
#include <unistd.h> |
||||
#include <cmath> |
||||
#include <cstring> |
||||
#include <QApplication> |
||||
#include <qwt/qwt_plot.h> |
||||
#include "ui/mainwindow.h" |
||||
#include "audio/transformer.h" |
||||
#include "constants.h" |
||||
|
||||
namespace pa = portaudio; |
||||
|
||||
pa::DirectionSpecificStreamParameters* getInParameters() { |
||||
auto dev = &pa::System::instance().defaultInputDevice(); |
||||
return new pa::DirectionSpecificStreamParameters(*dev, CHANNELS, pa::FLOAT32, false, dev->defaultLowInputLatency(), nullptr); |
||||
} |
||||
|
||||
pa::DirectionSpecificStreamParameters* getOutParameters() { |
||||
auto dev = &pa::System::instance().defaultOutputDevice(); |
||||
return new pa::DirectionSpecificStreamParameters(*dev, CHANNELS, pa::FLOAT32, false, dev->defaultLowOutputLatency(), nullptr); |
||||
} |
||||
|
||||
int main() { |
||||
pa::AutoSystem autoSys; // Ensure cleanup
|
||||
pa::System::initialize(); |
||||
|
||||
int pipes[2]; |
||||
pipe(pipes); |
||||
|
||||
pa::InterfaceCallbackStream* stream; |
||||
stream = new pa::InterfaceCallbackStream(); |
||||
auto params = new pa::StreamParameters(*getInParameters(), *getOutParameters(), SAMPLERATE, FRAMES, paClipOff); |
||||
auto r = new AudioTransformer(pipes[1]); |
||||
stream->open(*params, *r); |
||||
stream->start(); |
||||
|
||||
int x = 0; |
||||
auto a = new QApplication(x, (char**)nullptr); |
||||
auto w = new MainWindow(nullptr, pipes[0], r); |
||||
w->show(); |
||||
return a->exec(); |
||||
|
||||
stream->stop(); |
||||
stream->close(); |
||||
|
||||
pa::System::terminate(); |
||||
return 0; |
||||
} |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
#include "include/ui/mainwindow.h" |
||||
#include "./ui_mainwindow.h" |
||||
#include <portaudiocpp/PortAudioCpp.hxx> |
||||
#include <cstdlib> |
||||
#include <unistd.h> |
||||
|
||||
void Worker::doWork() { |
||||
while (true) { |
||||
auto data = new double[CHANNELS*FRAMES]; |
||||
for (int i = 0; i < CHANNELS; i++) { |
||||
read(fd, (char*)&data[i*FRAMES], FRAMES*sizeof(double)); |
||||
} |
||||
|
||||
resultReady((void*)data); |
||||
} |
||||
} |
||||
|
||||
MainWindow::MainWindow(QWidget *parent, int pipefd, AudioTransformer* transformer) |
||||
: QMainWindow(parent) |
||||
, ui(new Ui::MainWindow) { |
||||
this->transformer = transformer; |
||||
|
||||
for (int i = 0; i < FRAMES; i++) { |
||||
xData[i] = (double)i; |
||||
} |
||||
|
||||
ui->setupUi(this); |
||||
|
||||
ui->qwtPlot->setAxisScale(0, -1.0, 1.0); |
||||
ui->qwtPlot->setAxisScale(1, 0.0, 1024.0); |
||||
|
||||
srandom(0); |
||||
for (int i = 0; i < CHANNELS; i++) { |
||||
auto c = new QwtPlotCurve(); |
||||
curves[i] = c; |
||||
|
||||
c->setPen(QColor::fromRgb( |
||||
(int)(random() % 256), |
||||
(int)(random() % 256), |
||||
(int)(random() % 256) |
||||
)); |
||||
c->attach(ui->qwtPlot); |
||||
} |
||||
|
||||
auto worker = new Worker(pipefd); |
||||
worker->moveToThread(&workerThread); |
||||
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); |
||||
connect(worker, &Worker::resultReady, this, &MainWindow::handleResults); |
||||
workerThread.start(); |
||||
|
||||
// Start read thread
|
||||
QMetaObject::invokeMethod(worker, "doWork", Qt::ConnectionType::QueuedConnection); |
||||
} |
||||
|
||||
MainWindow::~MainWindow() |
||||
{ |
||||
workerThread.quit(); |
||||
workerThread.wait(); |
||||
delete ui; |
||||
} |
||||
|
||||
void MainWindow::handleResults(void* res) { |
||||
auto d = (double*)res; |
||||
for (int i = 0; i < CHANNELS; i++) { |
||||
this->curves[i]->setRawSamples(xData, &d[i*FRAMES], FRAMES); |
||||
} |
||||
free(d); |
||||
this->ui->qwtPlot->replot(); |
||||
QCoreApplication::processEvents(); |
||||
} |
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
<?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>800</width> |
||||
<height>600</height> |
||||
</rect> |
||||
</property> |
||||
<property name="windowTitle"> |
||||
<string>MainWindow</string> |
||||
</property> |
||||
<widget class="QWidget" name="centralwidget"> |
||||
<widget class="QwtPlot" name="qwtPlot"> |
||||
<property name="geometry"> |
||||
<rect> |
||||
<x>59</x> |
||||
<y>39</y> |
||||
<width>631</width> |
||||
<height>481</height> |
||||
</rect> |
||||
</property> |
||||
</widget> |
||||
</widget> |
||||
</widget> |
||||
<customwidgets> |
||||
<customwidget> |
||||
<class>QwtPlot</class> |
||||
<extends>QFrame</extends> |
||||
<header>qwt/qwt_plot.h</header> |
||||
</customwidget> |
||||
</customwidgets> |
||||
<resources/> |
||||
<connections/> |
||||
</ui> |
Loading…
Reference in new issue