first working
This commit is contained in:
commit
772376e440
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
[submodule "QxtGlobalShortcut"]
|
||||||
|
path = QxtGlobalShortcut
|
||||||
|
url = https://github.com/MOZGIII/QxtGlobalShortcut.git
|
||||||
|
[submodule "miniaudio"]
|
||||||
|
path = miniaudio
|
||||||
|
url = https://github.com/mackron/miniaudio.git
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 861322355e29a22105ee0809dc3fb68940573335
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QShortcut>
|
||||||
|
#include "qxtglobalshortcut.h"
|
||||||
|
|
||||||
|
#include "sound.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui { class MainWindow; }
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void buttonPressed();
|
||||||
|
void shortcut();
|
||||||
|
void shortcut2();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void playSound();
|
||||||
|
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
QShortcut* sc;
|
||||||
|
QxtGlobalShortcut* globalShortcut;
|
||||||
|
};
|
||||||
|
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "miniaudio.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "sounddevice.h"
|
||||||
|
|
||||||
|
class Sound {
|
||||||
|
public:
|
||||||
|
static Sound& instance();
|
||||||
|
static void deinit();
|
||||||
|
|
||||||
|
void addPlayback(const std::string& name, float volume = 1.f);
|
||||||
|
bool addDefaultDevice();
|
||||||
|
bool addDeviceWithName(const std::string& name);
|
||||||
|
void stopAll();
|
||||||
|
|
||||||
|
const static std::string FOLDER;
|
||||||
|
private:
|
||||||
|
Sound();
|
||||||
|
~Sound();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
ma_context context;
|
||||||
|
|
||||||
|
std::vector<SoundDevice*> devices;
|
||||||
|
|
||||||
|
static Sound* inst;
|
||||||
|
|
||||||
|
friend void sound_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
|
||||||
|
};
|
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "miniaudio.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class SoundDevice {
|
||||||
|
SoundDevice();
|
||||||
|
|
||||||
|
public:
|
||||||
|
~SoundDevice();
|
||||||
|
static SoundDevice* createDevice(ma_context* ctx, const std::string& name); // get device with name
|
||||||
|
static SoundDevice* createDevice(ma_context* ctx, const ma_device_id* did = NULL);
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
void addPlayback(const std::string& name, float volume = 1.f);
|
||||||
|
|
||||||
|
void startDevice();
|
||||||
|
void cleanupDecoders(); //fertige decoder löschen
|
||||||
|
|
||||||
|
// callback
|
||||||
|
void sound_callback(void* outbuffer, ma_uint32 frameCount);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Playback {
|
||||||
|
ma_decoder decoder;
|
||||||
|
float volume;
|
||||||
|
bool isDone = false;
|
||||||
|
Playback() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SoundData {
|
||||||
|
std::list<Playback*> playbacks; //liste der decoder
|
||||||
|
unsigned int decoderCount = 0;
|
||||||
|
unsigned int musicdecoderCount = 0;
|
||||||
|
ma_mutex* mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool deviceRunning = false;
|
||||||
|
ma_device device;
|
||||||
|
SoundData data;
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 4203697b38b25e100bedfc73f3fa28cd8707705e
|
|
@ -0,0 +1,9 @@
|
||||||
|
virtualDeviceName="VirtualMic"
|
||||||
|
realMic="alsa_input.pci-0000_24_00.0.analog-stereo.remapped"
|
||||||
|
|
||||||
|
pacmd load-module module-null-sink "sink_name=${virtualDeviceName}"
|
||||||
|
pacmd update-sink-proplist "${virtualDeviceName}" "device.description=${virtualDeviceName}"
|
||||||
|
pacmd load-module module-virtual-source "source_name=${virtualDeviceName}-Source" "master=${virtualDeviceName}.monitor"
|
||||||
|
pacmd load-module module-loopback "sink=${virtualDeviceName}" "source=${realMic}" "latency_msec=1"
|
||||||
|
|
||||||
|
# todo: set new sink as default
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui(new Ui::MainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
QPushButton* btn = ui->pushButton;
|
||||||
|
QObject::connect(btn, SIGNAL( clicked() ), this, SLOT( buttonPressed() ));
|
||||||
|
|
||||||
|
sc = new QShortcut(QKeySequence("Shift+1"), this, 0, 0, Qt::ApplicationShortcut);
|
||||||
|
QObject::connect(sc, SIGNAL( activated() ), this, SLOT( shortcut() ));
|
||||||
|
|
||||||
|
globalShortcut = new QxtGlobalShortcut(QKeySequence("Shift+F1"));
|
||||||
|
QObject::connect(globalShortcut, SIGNAL(activated()), this, SLOT( shortcut2() ));
|
||||||
|
|
||||||
|
Sound& sound = Sound::instance(); // init sound
|
||||||
|
sound.addDeviceWithName("VirtualMic");
|
||||||
|
sound.addDefaultDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow() {
|
||||||
|
delete ui;
|
||||||
|
|
||||||
|
Sound::deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::buttonPressed() {
|
||||||
|
std::cout << "Button Pressed" << std::endl;
|
||||||
|
|
||||||
|
playSound();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::shortcut() {
|
||||||
|
std::cout << "Shortcut Pressed" << std::endl;
|
||||||
|
|
||||||
|
playSound();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::shortcut2() {
|
||||||
|
std::cout << "Shortcut2 Pressed!!" << std::endl;
|
||||||
|
|
||||||
|
playSound();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::playSound() {
|
||||||
|
//Sound::instance().addPlayback("Uwu_voice-xjrU3N8M4eo-251.mp3");
|
||||||
|
Sound::instance().addPlayback("bonk.wav");
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Diese Datei ist stark an den examples von miniaudio orientiert (vor allem simple_mixing.c). Die Struktur ähnelt sich daher möglicherweise.
|
||||||
|
|
||||||
|
|
||||||
|
#define MA_DEBUG_OUTPUT
|
||||||
|
//#define MA_LOG_LEVEL MA_LOG_LEVEL_VERBOSE
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#ifndef __wtypes_h__
|
||||||
|
#include <wtypes.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __WINDEF_
|
||||||
|
#include <windef.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WINUSER_
|
||||||
|
#include <winuser.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __RPC_H__
|
||||||
|
#include <rpc.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "sound.h"
|
||||||
|
|
||||||
|
#define DR_FLAC_IMPLEMENTATION
|
||||||
|
#include "extras/dr_flac.h" //flac
|
||||||
|
#define DR_MP3_IMPLEMENTATION
|
||||||
|
#include "extras/dr_mp3.h" //mp3
|
||||||
|
#define DR_WAV_IMPLEMENTATION
|
||||||
|
#include "extras/dr_wav.h" //wav
|
||||||
|
|
||||||
|
#include "extras/stb_vorbis.c" //vorbis
|
||||||
|
|
||||||
|
#define MINIAUDIO_IMPLEMENTATION
|
||||||
|
#include "miniaudio.h"
|
||||||
|
|
||||||
|
#include <algorithm> //std::max
|
||||||
|
#include <cstring>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
Sound* Sound::inst = nullptr;
|
||||||
|
const std::string Sound::FOLDER = "/home/yannis/prog/ts3/soundboardsounds/";
|
||||||
|
|
||||||
|
Sound& Sound::instance() {
|
||||||
|
if (!inst) {
|
||||||
|
inst = new Sound(); //TODO: needs delete
|
||||||
|
inst->init();
|
||||||
|
}
|
||||||
|
return *inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::deinit() {
|
||||||
|
if(inst) {
|
||||||
|
inst->stopAll();
|
||||||
|
delete inst;
|
||||||
|
inst = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Sound deinited" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::addPlayback(const std::string& name, float volume) {
|
||||||
|
for(SoundDevice* sd : devices) {
|
||||||
|
sd->addPlayback(name, volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sound::addDefaultDevice() {
|
||||||
|
SoundDevice* sd = SoundDevice::createDevice(&context);
|
||||||
|
if(sd) {
|
||||||
|
devices.push_back(sd);
|
||||||
|
}
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sound::addDeviceWithName(const std::string& name) {
|
||||||
|
SoundDevice* sd = SoundDevice::createDevice(&context, name);
|
||||||
|
if(sd) {
|
||||||
|
devices.push_back(sd);
|
||||||
|
}
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::stopAll() {
|
||||||
|
for(SoundDevice* sd : devices) {
|
||||||
|
sd->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound::Sound() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound::~Sound() {
|
||||||
|
stopAll();
|
||||||
|
for(SoundDevice* sd : devices) {
|
||||||
|
delete sd;
|
||||||
|
}
|
||||||
|
devices.clear();
|
||||||
|
ma_context_uninit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::init() {
|
||||||
|
//dies wird getrennt vom ctor, da es beim ma_device_start sonst zu einer race condition kommen kann
|
||||||
|
|
||||||
|
// enumerate devices
|
||||||
|
if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
|
||||||
|
std::cout << "Failed to initialize Sound context." << std::endl;
|
||||||
|
throw new std::exception();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
#include "sounddevice.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "sound.h"
|
||||||
|
|
||||||
|
#define CHANNELCOUNT 2
|
||||||
|
#define MINSTREAMCOUNT 5
|
||||||
|
|
||||||
|
static void global_sound_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) {
|
||||||
|
//es kann davon ausgegangen werden, das der buffer mit stille gefüllt ist
|
||||||
|
|
||||||
|
//decoder
|
||||||
|
if (!pDevice->pUserData) return;
|
||||||
|
|
||||||
|
((SoundDevice*) pDevice->pUserData)->sound_callback(pOutput, frameCount);
|
||||||
|
|
||||||
|
(void) pInput; //ignore input
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_uint64 readDecoderandAdd(ma_decoder* decoder, float volume, unsigned int streamDivider, ma_uint32 frameCount, void* outputBuffer) {
|
||||||
|
assert(frameCount > 0);
|
||||||
|
|
||||||
|
//read sound
|
||||||
|
int16_t* buffer = new int16_t[frameCount * CHANNELCOUNT];
|
||||||
|
ma_uint64 frameCountRead = ma_decoder_read_pcm_frames(decoder, buffer, frameCount);
|
||||||
|
|
||||||
|
if (frameCountRead > frameCount) frameCountRead = frameCount; //drop unwanted frames (sollte eigentlich nicht nötig sein)
|
||||||
|
|
||||||
|
//zum mixen: samples durch anzahl decoder teilen (overflow verhindern), volume factor anwenden und auf outputbuffer addieren
|
||||||
|
int16_t* outbuf = (int16_t*) outputBuffer;
|
||||||
|
for (ma_uint64 i = 0; i < frameCountRead * CHANNELCOUNT; i++) {
|
||||||
|
outbuf[i] += (buffer[i] * volume) / streamDivider; //TODO: use sse / avx
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
return frameCountRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SoundDevice::sound_callback(void* outbuffer, ma_uint32 frameCount) {
|
||||||
|
ma_mutex_lock(data.mutex);
|
||||||
|
|
||||||
|
unsigned int decoderCount = data.decoderCount;
|
||||||
|
|
||||||
|
unsigned int streamDivider = std::max<unsigned int>(decoderCount, MINSTREAMCOUNT); //nicht listplaybacks.size() verwenden, da manche playbacks möglicherweise noch fertig sind, aber noch nicht aus der list entfernt wurden. (wird von anderem thread gemacht)
|
||||||
|
unsigned int count = 0; //nummer des aktuellen decoders (nur für debugging)
|
||||||
|
|
||||||
|
for (SoundDevice::Playback* pb : data.playbacks) {
|
||||||
|
if (pb->isDone) continue; //fertige encoder ignorieren
|
||||||
|
|
||||||
|
// std::cout << "Play decoder " << (++count) << " of " << decoderCount << std::endl; //DEBUG
|
||||||
|
|
||||||
|
//informationen beschaffen
|
||||||
|
ma_decoder* pDecoder = &(pb->decoder);
|
||||||
|
float volume = pb->volume;
|
||||||
|
|
||||||
|
//decoder "reinaddieren"
|
||||||
|
ma_uint64 read = readDecoderandAdd(pDecoder, volume, streamDivider, frameCount, outbuffer);
|
||||||
|
|
||||||
|
//decoder fertig -> nicht mehr verwenden
|
||||||
|
if (read < frameCount) {
|
||||||
|
pb->isDone = true;
|
||||||
|
--data.decoderCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_mutex_unlock(data.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundDevice::SoundDevice() {
|
||||||
|
data.mutex = new ma_mutex();
|
||||||
|
ma_mutex_init(data.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundDevice::~SoundDevice() {
|
||||||
|
ma_device_uninit(&device);
|
||||||
|
ma_mutex_uninit(data.mutex);
|
||||||
|
delete data.mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundDevice* SoundDevice::createDevice(ma_context* ctx, const std::string& name) {
|
||||||
|
ma_device_info* pPlaybackDeviceInfos;
|
||||||
|
ma_uint32 playbackDeviceCount;
|
||||||
|
ma_result result = ma_context_get_devices(ctx, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, 0);
|
||||||
|
int8_t choosenDevice = -1;
|
||||||
|
|
||||||
|
std::cout << " " << playbackDeviceCount << " playback devices found:" << std::endl;
|
||||||
|
for(uint8_t i = 0; i < playbackDeviceCount; i++) {
|
||||||
|
std::cout << " " << (int) i << ": " << pPlaybackDeviceInfos[i].name << std::endl;
|
||||||
|
|
||||||
|
if(pPlaybackDeviceInfos[i].name == name) {
|
||||||
|
choosenDevice = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(choosenDevice == -1) {
|
||||||
|
std::cout << "Device \"" << name << "\" not found!" << std::endl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createDevice(ctx, &pPlaybackDeviceInfos[choosenDevice].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundDevice* SoundDevice::createDevice(ma_context* ctx, const ma_device_id* did) {
|
||||||
|
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback);
|
||||||
|
deviceConfig.playback.format = ma_format::ma_format_s16;
|
||||||
|
deviceConfig.playback.channels = CHANNELCOUNT;
|
||||||
|
if(did != NULL)
|
||||||
|
deviceConfig.playback.pDeviceID = did;
|
||||||
|
|
||||||
|
deviceConfig.sampleRate = 44100;
|
||||||
|
deviceConfig.dataCallback = global_sound_callback;
|
||||||
|
deviceConfig.pulse.pStreamNamePlayback = "MySoundboard";
|
||||||
|
|
||||||
|
SoundDevice* sd = new SoundDevice();
|
||||||
|
deviceConfig.pUserData = sd;
|
||||||
|
|
||||||
|
if (ma_device_init(ctx, &deviceConfig, &sd->device) != MA_SUCCESS) {
|
||||||
|
std::cout << "Failed to open sound device." << std::endl;
|
||||||
|
|
||||||
|
delete sd;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Sound playback device \"" << sd->device.playback.name << "\" initilized." << std::endl;
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundDevice::stop() {
|
||||||
|
if(deviceRunning)
|
||||||
|
ma_device_stop(&device);
|
||||||
|
|
||||||
|
ma_mutex_lock(data.mutex);
|
||||||
|
for(Playback* pb : data.playbacks) {
|
||||||
|
ma_decoder_uninit(&pb->decoder);
|
||||||
|
delete pb;
|
||||||
|
}
|
||||||
|
data.playbacks.clear();
|
||||||
|
ma_mutex_unlock(data.mutex);
|
||||||
|
|
||||||
|
if(deviceRunning)
|
||||||
|
std::cout << "Sound playback device \"" << device.playback.name << "\" stopped." << std::endl;
|
||||||
|
|
||||||
|
deviceRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundDevice::addPlayback(const std::string& name, float volume) {
|
||||||
|
cleanupDecoders();
|
||||||
|
|
||||||
|
Playback* pb = new Playback();
|
||||||
|
|
||||||
|
if (ma_decoder_init_file((Sound::FOLDER + name).c_str(), NULL, &(pb->decoder)) != MA_SUCCESS) {
|
||||||
|
std::cout << "Sound datei: " << name << " konnte nicht geladen werden!" << std::endl;
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
pb->volume = volume;
|
||||||
|
pb->isDone = false;
|
||||||
|
|
||||||
|
ma_mutex_lock(data.mutex);
|
||||||
|
|
||||||
|
data.playbacks.push_back(pb);
|
||||||
|
++data.decoderCount;
|
||||||
|
ma_mutex_unlock(data.mutex);
|
||||||
|
|
||||||
|
startDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundDevice::startDevice() {
|
||||||
|
if (deviceRunning) return;
|
||||||
|
|
||||||
|
if (ma_device_start(&device) != MA_SUCCESS) {
|
||||||
|
std::cout << "Failed to start sound device \"" << device.playback.name << "\"" << std::endl;
|
||||||
|
stop();
|
||||||
|
ma_device_uninit(&device);
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceRunning = true;
|
||||||
|
|
||||||
|
std::cout << "Sound device \"" << device.playback.name << "\" started." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundDevice::cleanupDecoders() {
|
||||||
|
unsigned int count = 0;
|
||||||
|
ma_mutex_lock(data.mutex);
|
||||||
|
for (std::list<Playback*>::iterator it = data.playbacks.begin(); it != data.playbacks.end(); ) {
|
||||||
|
Playback* pb = *it;
|
||||||
|
if (pb->isDone) {
|
||||||
|
//uninit decoder
|
||||||
|
ma_decoder_uninit(&pb->decoder);
|
||||||
|
count++;
|
||||||
|
data.playbacks.erase(it++);
|
||||||
|
delete pb;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
ma_mutex_unlock(data.mutex);
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
std::cout << "removed " << count << " decoder" << std::endl;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
QT += core gui multimedia
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
CONFIG += c++17
|
||||||
|
CONFIG += debug_info
|
||||||
|
LIBS += -lX11 -ldl
|
||||||
|
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any Qt feature that has been marked deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
src/main.cpp \
|
||||||
|
src/mainwindow.cpp \
|
||||||
|
src/sound.cpp \
|
||||||
|
src/sounddevice.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
include/mainwindow.h \
|
||||||
|
include/sound.h \
|
||||||
|
include/sounddevice.h \
|
||||||
|
miniaudio/miniaudio.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
ui/mainwindow.ui
|
||||||
|
|
||||||
|
TRANSLATIONS += \
|
||||||
|
testqt_de_DE.ts
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD/include/ \
|
||||||
|
$$PWD/miniaudio/
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
|
# Adding QxtGlobalShortcut
|
||||||
|
include(QxtGlobalShortcut/QxtGlobalShortcut.pri)
|
|
@ -0,0 +1,3 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="testqt_de_DE"></TS>
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?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>778</width>
|
||||||
|
<height>613</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>130</x>
|
||||||
|
<y>110</y>
|
||||||
|
<width>151</width>
|
||||||
|
<height>19</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Hello, world!</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QPushButton" name="pushButton">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>120</x>
|
||||||
|
<y>150</y>
|
||||||
|
<width>100</width>
|
||||||
|
<height>27</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Testbutton</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>778</width>
|
||||||
|
<height>24</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
Loading…
Reference in New Issue