Commit 55b75b0e authored by Leander Schulten's avatar Leander Schulten
Browse files

Move Audio Logic to new namespace Audio and the class AudioCaptureManager

The raw data are now not getting modified.
The fft output data are now right scaled.
The Colorplot, graph, Oscillogram have now other computations to compute the displayed output.
parent e97aa955
......@@ -43,7 +43,10 @@ SOURCES += main.cpp \
test/DriverDummy.cpp \
graph.cpp \
oscillogram.cpp \
colorplot.cpp
colorplot.cpp \
audio/sample.cpp \
test/testsampleclass.cpp \
audio/audiocapturemanager.cpp
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
......@@ -87,7 +90,11 @@ HEADERS += \
test/DriverDummy.h \
graph.h \
oscillogram.h \
colorplot.h
colorplot.h \
audio/sample.h \
test/testsampleclass.h \
audio/capturedatamanager.h \
audio/audiocapturemanager.h
# Default rules for deployment.
......
#include "audiocapturemanager.h"
#include "graph.h"
#include "colorplot.h"
#include "oscillogram.h"
#include <algorithm>
namespace Audio {
AudioCaptureManager::AudioCaptureManager():audiofft(sample.size())
{
}
void AudioCaptureManager::initCallback(int channels){
this->channels = channels;
if(Colorplot::getLast())
Colorplot::getLast()->setBlockSize(512);
}
void AudioCaptureManager::dataCallback(float* data, unsigned int frames, bool*done){
*done = !run;
if(!data)
return;
if(channels<0)
return;
int firstIndex = -1;
for (int i = 0; i < channels; ++i) {
if(data[i]!=0){
firstIndex=i;
break;
}
}
if(firstIndex==-1)
return;
sample.addData(data,data+frames*static_cast<unsigned>(channels),channels-1,firstIndex);
audiofft.analyse(sample.data(),1,fftoutput.data());
//db scale
std::transform(fftoutput.begin(),fftoutput.end(),fftoutput.begin(),[](auto i){return 10*std::log10(1+i);});
if(Graph::getLast())
Graph::getLast()->showData(fftoutput.data(),fftoutput.size());
if(Colorplot::getLast()){
Colorplot::getLast()->startBlock();
for (int i = 0; i < 512; ++i) {
Colorplot::getLast()->pushDataToBlock(fftoutput.at(i));
}
Colorplot::getLast()->endBlock();
}
if(Oscillogram::getLast())
Oscillogram::getLast()->showData(sample.data(),sample.size());
}
bool AudioCaptureManager::startCapturing(QString filePathToCaptureLibrary){
typedef int (*capture)(void(*)(int),void(*)(float*,unsigned int, bool*)) ;
auto func = reinterpret_cast<capture>(QLibrary::resolve(filePathToCaptureLibrary,"captureAudio"));
if(func){
captureAudioThread = std::thread([this,func](){
run = true;
func(&AudioCaptureManager::staticInitCallback,&AudioCaptureManager::staticDataCallback);
});
}
return func;
}
}
#ifndef AUDIOCAPTUREMANAGER_H
#define AUDIOCAPTUREMANAGER_H
#include "sample.h"
#include <thread>
#include "audio_fft.h"
namespace Audio {
/**
* @brief The AudioCaptureManager class gets the data from the captureWindowsSountoutput Project and analyse the data and give the data to the other components
*/
class AudioCaptureManager
{
Sample<float,4096> sample;
std::array<float,2048> fftoutput;
std::thread captureAudioThread;
std::atomic_bool run;
AudioFFT audiofft;
int channels = -1;
private:
AudioCaptureManager();
~AudioCaptureManager(){
if(captureAudioThread.joinable()){
run.store(false);
captureAudioThread.join();
}
}
private:
static void staticInitCallback(int channels){get().initCallback(channels);}
static void staticDataCallback(float* data, unsigned int frames, bool*done){get().dataCallback(data,frames,done);}
void initCallback(int channels);
void dataCallback(float* data, unsigned int frames, bool*done);
public:
bool startCapturing(QString filePathToCaptureLibrary);
public:
AudioCaptureManager(AudioCaptureManager const&) = delete;
void operator=(AudioCaptureManager const&) = delete;
static AudioCaptureManager & get(){static AudioCaptureManager m;return m;}
};
}
#endif // AUDIOCAPTUREMANAGER_H
#include "sample.h"
#ifndef SAMPLE_H
#define SAMPLE_H
#include <array>
#include <cstring>
#include <algorithm>
#include <cassert>
#include <QtCore>
namespace Audio {
template<typename Type, std::size_t size_>
class Sample
{
std::array<Type,size_> array;
public:
Sample(){
std::memset(array.data(),0,array.size()*sizeof (Type));
}
/**
* @brief addData adds data to the end of the sample and moves old data to the left
* @param newData a pointer to the new Data
* @param endPointer a pointer pointing to the byte after the data block, like the std::end by the
* @param interleaved space between two relevant data values, eg. 1 by sterio data
* @param startOffset the offset from the newData Pointer to the first relevant byte
*/
void addData(Type * newData, Type * endPointer, int interleaved = 0, int startOffset = 0){
Q_ASSERT(newData<endPointer);
newData += startOffset;
int dis = std::distance(newData,endPointer);
int length = (dis+interleaved) / (1+interleaved);
if(length >= size_){
--endPointer;
for (auto i = array.rbegin();i != array.rend();++i,std::advance(endPointer,-(1+interleaved))) {
*i = *endPointer;
}
return;
}
std::copy(std::next(array.cbegin(),length),array.cend(),array.begin());
for ( auto i = std::next(array.begin(),array.size()-length);newData<endPointer;++i,std::advance(newData,1+interleaved)) {
*i = *newData;
Q_ASSERT(i != array.cend());
}
//Q_ASSERT(newData == endPointer);
}
const std::array<Type,size_> & getArray()const{return array;}
std::array<Type,size_> & getArray(){return array;}
Type* data(){return array.data();}
const Type* data()const{return array.data();}
std::size_t size(){return array.size();}
};
}
#endif // SAMPLE_H
......@@ -78,13 +78,14 @@ QSGNode * Colorplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *){
lines->y /*= points->y*/ = height()-i*2-1;
lines->a /*= points->a*/ = 255;
lines->r /*= points->r*/ = (unsigned char)(block[i]*0.5>255.f?255:block[i]*0.5);
lines->g /*= points->g*/ = (unsigned char)std::max(block[i]*0.2f,255.f);
lines->b /*= points->b*/ = 00;
lines->g /*= points->g*/ = (unsigned char)std::min(block[i]*0.2f,255.f);
lines->g /*= points->g*/ = (unsigned char)std::min(block[i]/20*255,255.f);
lines->r /*= points->b*/ = (unsigned char)(block[i]/10 * 255);
++lines; //++points;
++counter;
}
}
qDebug()<<"counter : " << counter << " size : "<< size << " blocks : "<< dataBlocks.size();
//qDebug()<<"counter : " << counter << " size : "<< size << " blocks : "<< dataBlocks.size();
mutex.unlock();
......
......@@ -17,7 +17,7 @@ class Colorplot : public QQuickItem
Q_PROPERTY(QColor lineColor READ getLineColor WRITE setLineColor NOTIFY lineColorChanged)
static Colorplot * lastCreated;
std::atomic_bool haveNewData;
const static unsigned int MAX_BLOCKS_COUNT = 500;
const static unsigned int MAX_BLOCKS_COUNT = 900;
std::mutex mutex;
public:
......@@ -46,7 +46,7 @@ public:
}
void pushDataToBlock(float d){
if (currentBlockCounter>=0) {
dataBlocks.back()[currentBlockCounter] = d*0.4f;//std::pow(1+d*0.001,3)*20;
dataBlocks.back()[currentBlockCounter] = d;//*0.4f;//std::pow(1+d*0.001,3)*20;
++currentBlockCounter;
if(currentBlockCounter>=blockSize)
currentBlockCounter=-1;
......
......@@ -91,7 +91,8 @@ QSGNode * Graph::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *){
*/
for(int i = 0 ; i< size; ++i){
vertices->x = i*2;
vertices->y = height() - 50 - 20* (std::log10(data[i]));
//vertices->y = height() - 50 - 20* (std::log10(data[i]));
vertices->y = height() - 50 - 2*data[i];
++vertices;
}
......
......@@ -21,14 +21,7 @@ public:
static Graph * getLast(){return lastCreated;}
void showData(float* data, int size){
this->size = std::min(2048,size);
memcpy(this->data,data,this->size);
haveNewData.store(true);
}
void showData(volatile float* data, int size){
this->size = std::min(2048,size);
for(int i = 0; i<this->size;++i){
this->data[i] = data[i];
}
memcpy(this->data,data,this->size*sizeof (float));
haveNewData.store(true);
}
void setLineColor(QColor c){lineColor=c;update();lineColorChanged();}
......
......@@ -35,105 +35,15 @@
#include <QTimer>
#include "oscillogram.h"
#include "colorplot.h"
#include "audio/audiocapturemanager.h"
#include "test/testsampleclass.h"
#define WIN_ONLY(a)
#ifdef Q_OS_WIN
#ifndef _MSC_VER
#include "audio_fft.h"
#else /*_MSC_VER*/
#include "audio_fft.h"
#endif /*_MSC_VER*/
#define WIN_ONLY(a) a
int channel;
const int fft_size=960;
AudioFFT audioFft(fft_size);
std::atomic_bool done (false);
float fftOutput[fft_size/2];
volatile float fftOutputV[fft_size/2];
float data960[960*2];
volatile float data960V[960*2];
void init(int c){
channel = c;
Colorplot::getLast()->setBlockSize(300);
}
int counter480, counter960;
void callback(float* data, unsigned int frames, bool*d){
if(data){
if(channel>2){
int index = -1;
for (int i = 0; i < frames*2; ++i) {
data[i] = data[++index];
data[++i] = data[++index];
index+=6;
}
}
auto d = qDebug();
for(int i = 0;i<frames*2;i++){
data[i] *= 400;
}
/*for(int i = 0; i<frames;++i){
data960[i] = data[i*2];
}*/
if(frames==480){
//memmove(data960,data960+960,960);
//memmove(data960+960,data,960);
for(int i = 0; i<960;++i){
data960[i] = data960[i+960];
}
for(int i = 0; i<960;++i){
data960[i+960] = data[i];
}
counter480++;
}else{
//memcpy(data960,data,960*2);
counter960++;
}
audioFft.analyse(data960,channel,fftOutput);
for(int i = 0; i < fft_size/2;++i){
fftOutputV[i] = fftOutput[i];
}
if(Graph::getLast())
Graph::getLast()->showData(fftOutputV,960/2);
if(Colorplot::getLast()){
Colorplot::getLast()->startBlock();
for (int i = 0; i < 300; ++i) {
Colorplot::getLast()->pushDataToBlock(fftOutputV[i]);
}
Colorplot::getLast()->endBlock();
}
for(int i = 0 ; i < 960*2;++i){
data960V[i] = data960[i];
}
if(Oscillogram::getLast())
Oscillogram::getLast()->showData(data960V,960*2);
/*for(int i = 0; i< fft_size/2;++i){
std::cout << fftOutput[i]<< ' ';
}
std::cout<<std::endl;*/
qDebug()<<"480 : "<<counter480 << " 960 : "<<counter960;
}
*d = done;
}
#endif /*Q_OS_WIN*/
int main(int argc, char *argv[])
{
std::thread captureAudioThread;
Test::testSampleClass();
class CatchingErrorApplication : public QGuiApplication{
public:
......@@ -207,11 +117,6 @@ int main(int argc, char *argv[])
QFile savePath(settings.getJsonSettingsFilePath());
ApplicationData::saveData(savePath);
Driver::stopAndUnloadDriver();
qDebug()<<"is captureAudioThread jionable : "<<captureAudioThread.joinable();
if(captureAudioThread.joinable()){
WIN_ONLY(done.store(true);)
captureAudioThread.join();
}
});
settings.connect(&settings,&Settings::driverFilePathChanged,[&](){
Driver::loadAndStartDriver(settings.getDriverFilePath());
......@@ -240,18 +145,6 @@ int main(int argc, char *argv[])
// laden erst nach dem laden des qml ausführen
after();
#ifdef Q_OS_WIN
typedef int (*capture)(void(*)(int),void(*)(float*,unsigned int, bool*)) ;
auto func = reinterpret_cast<capture>(QLibrary::resolve(settings.getAudioCaptureFilePath(),"captureAudio"));
if(func){
captureAudioThread = std::thread([&](){
qDebug()<<"Start capture";
qDebug()<<"Capture Result : "<<func(&init,&callback);
});
}
#endif /*Q_OS_WIN*/
// Treiber laden
......@@ -288,6 +181,8 @@ int main(int argc, char *argv[])
});
timer.start();
qDebug() << "start capturing : " << Audio::AudioCaptureManager::get().startCapturing(settings.getAudioCaptureFilePath());
//ControlPanel::getLastCreated()->addDimmerGroupControl();
return app.exec();
}
......@@ -46,7 +46,7 @@ QSGNode * Oscillogram::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *){
for(int i = 0 ; i< size; ++i){
vertices->x = i;
vertices->y = height()/2 - (data[i]);
vertices->y = height()/2 - (data[i])*scale;
++vertices;
}
......
......@@ -2,12 +2,16 @@
#define OSCILLOGRAM_H
#include <QQuickItem>
#include <queue>
class Oscillogram : public QQuickItem
{
Q_OBJECT
float data[2048];
int size = 0;
std::queue<float> lastMaxValues;
float maxValuesSum=0;
float scale = 1;
QColor lineColor = QColor(0,0,0);
Q_PROPERTY(QColor lineColor READ getLineColor WRITE setLineColor NOTIFY lineColorChanged)
static Oscillogram * lastCreated;
......@@ -21,14 +25,15 @@ public:
static Oscillogram * getLast(){return lastCreated;}
void showData(float* data, int size){
this->size = std::min(2048,size);
memcpy(this->data,data,this->size);
haveNewData.store(true);
}
void showData(volatile float* data, int size){
this->size = std::min(2048,size);
for(int i = 0; i<this->size;++i){
this->data[i] = data[i];
lastMaxValues.push(*std::max_element(data,data+size));
maxValuesSum+=lastMaxValues.back();
if(lastMaxValues.size()>500){
maxValuesSum-=lastMaxValues.front();
lastMaxValues.pop();
}
scale = std::min<float>(250.f,height()/2-50)/(maxValuesSum/lastMaxValues.size());
std::copy(data,data+this->size,this->data);
haveNewData.store(true);
}
void setLineColor(QColor c){lineColor=c;update();lineColorChanged();}
......
#include "testsampleclass.h"
#include "audio/sample.h"
namespace Test{
void testSampleClass(){
using namespace Audio;
Sample<int,10> s;
using namespace std;
std::array<int,10> v;
std::iota(v.begin(),v.end(),0);
//for_each(v.cbegin(),v.cend(),[](auto i ){qDebug() << i;});
s.addData(v.begin(),v.end());
//for_each(s.getArray().cbegin(),s.getArray().cend(),[](auto i ){qDebug() << i;});
for (int i = 0; i < 10; ++i) {
Q_ASSERT(s.getArray().at(i)==i);
}
s.addData(v.begin(),v.end(),1,1);
//for_each(s.getArray().cbegin(),s.getArray().cend(),[](auto i ){qDebug() << i;});
for (int i = 5, j=1; i < 10; ++i,j+=2) {
//qDebug()<<i << ':'<<j;
Q_ASSERT(s.getArray().at(i)==j);
}
s.addData(v.begin(),v.end(),4,4);
//qDebug();
//for_each(s.getArray().cbegin(),s.getArray().cend(),[](auto i ){qDebug() << i;});
Q_ASSERT(s.data()[8]==4);
Q_ASSERT(s.data()[9]==9);
s.addData(v.begin(),v.end(),8,2);
//qDebug();
//for_each(s.getArray().cbegin(),s.getArray().cend(),[](auto i ){qDebug() << i;});
Q_ASSERT(s.data()[8]==9);
Q_ASSERT(s.data()[9]==2);
}
}
#ifndef TESTSAMPLECLASS_H
#define TESTSAMPLECLASS_H
namespace Test {
void testSampleClass();
}
#endif // TESTSAMPLECLASS_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment