Skip to content

Commit 2771d77

Browse files
committed
- Complete audio player and UI
1 parent c916f8a commit 2771d77

File tree

6 files changed

+133
-61
lines changed

6 files changed

+133
-61
lines changed

src/gui/AudioPlaybackWidget.cpp

Lines changed: 108 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@
33
#include <QJsonDocument>
44
#include <QStringRef>
55

6+
void checkConnection(bool connection){
7+
if(!connection){
8+
QMessageBox::warning(nullptr, "Qt connection error", "Unable to connect UI component");
9+
}
10+
}
11+
612
bool AudioPlaybackWidget::serialize(std::vector<audioConfig>* pConf, const QString& outPath) {
713
QJsonObject audioJson;
814
audioJson["Tracks"] = (int)pConf->size();
915
int x = 0;
1016
for(const auto& audioConfig: *pConf){
1117
QJsonObject track;
1218
track["Name"] = audioConfig.audioName;
13-
track["Path"] = audioConfig.audioPath.absolutePath();
19+
track["Path"] = audioConfig.audioPath.absoluteFilePath();
1420
track["Playback"] = audioConfig.playbackEnable;
1521
track["Volume"] = audioConfig.volume;
1622
track["StartFrame"] = audioConfig.startFrame;
@@ -48,8 +54,8 @@ bool AudioPlaybackWidget::deserialize(const QJsonObject& pConf, std::vector<audi
4854

4955
void AudioPlaybackWidget::aPlayer(std::vector<audioConfig>* pConf, bool play, mediaState* state, int fps, int curFrame, int frameCount){
5056
qDebug("Media player requested");
51-
for(int x = 0; x < pConf->size(); x++){
52-
if(pConf->size() > x + 1){ return; }
57+
int x = 0;
58+
for(auto conf: *pConf){
5359
if(state->players.size() < x + 1 && state->outputs.size() < x + 1){
5460
auto* mediaPlayer = new QMediaPlayer;
5561
auto* audioOutput = new QAudioOutput;
@@ -59,45 +65,66 @@ void AudioPlaybackWidget::aPlayer(std::vector<audioConfig>* pConf, bool play, me
5965
QMediaPlayer* player = state->players.at(x);
6066
QAudioOutput* output = state->outputs.at(x);
6167
const audioConfig& config = pConf->at(x);
62-
if(player->audioOutput() == nullptr){
63-
player->setAudioOutput(output);
64-
}
68+
if(player->audioOutput() == nullptr){ player->setAudioOutput(output); }
6569
if(player->source() != QUrl::fromLocalFile(config.audioPath.absoluteFilePath())){
6670
player->setSource(config.audioPath.absoluteFilePath());
6771
}
6872
if(output->volume() != getVol(config.volume)){ output->setVolume(getVol(config.volume)); }
69-
70-
// FIXME
71-
int framesInMs = (frameCount / fps) / 1000;
72-
int startFrameInMs = ((frameCount - (config.startFrame + 1)) / fps) / 1000;
73-
int curFrameInMs = (((curFrame + 1) - frameCount)/ fps) / 1000;
74-
75-
uint initialFrameInMs = framesInMs - startFrameInMs;
76-
uint posInMs = framesInMs - curFrameInMs;
77-
78-
if(player->hasAudio() && posInMs >= initialFrameInMs) {
79-
player->setPosition(posInMs);
80-
}
81-
82-
if(!play && player->isPlaying()){ player->stop(); state->playing = false;}
83-
if(play && config.startFrame < curFrame && config.endFrame < curFrame && config.playbackEnable && !player->isPlaying()){
73+
correctTrackPos(player, curFrame, frameCount, fps, const_cast<audioConfig&>(config));
74+
qDebug("---");
75+
qDebug() << "x = " << x << "; track = " << player->source() << "; playing = " << player->isPlaying() << "; play = " << play;
76+
qDebug("---");
77+
if(!play){ player->stop(); state->playing = false;}
78+
if(play && config.startFrame < curFrame && config.endFrame > curFrame && config.playbackEnable){
8479
player->play();
8580
state->playing = true;
8681
}
82+
x++;
8783
}
8884
}
8985

9086
void AudioPlaybackWidget::connect(QWidget *audioWidget, mediaState *state, std::vector<audioConfig>* config){
91-
Q_UNIMPLEMENTED();
92-
}
87+
checkConnection(QToolButton::connect(saveConfigButton, &QToolButton::clicked, [=](){
88+
QFileDialog diag(audioWidget);
89+
diag.setAcceptMode(QFileDialog::AcceptSave);
90+
diag.setDirectory(QDir::currentPath());
91+
diag.setNameFilter(QCoreApplication::translate("SaveMus", "Anie audio configuration file (*.aemus)", nullptr));
92+
if(diag.exec()) {
93+
QString fileName = diag.selectedFiles().first();
94+
static QRegularExpression fileRegex("\\.aemus$");
95+
if (!fileRegex.match(fileName).hasMatch()) { fileName.append(".aemus"); }
96+
serialize(config, fileName);
97+
}
98+
}));
99+
checkConnection(QToolButton::connect(loadConfigButton, &QToolButton::clicked, [=]()mutable {
100+
QFileDialog diag(audioWidget);
101+
diag.setAcceptMode(QFileDialog::AcceptOpen);
102+
diag.setDirectory(QDir::currentPath());
103+
diag.setNameFilter(QCoreApplication::translate("LoadMus", "Anie audio configuration file (*.aemus)", nullptr));
104+
if(diag.exec()) {
105+
QFile file = QFile(diag.selectedFiles().first());
106+
if(!file.exists()) {
107+
QMessageBox::warning(audioWidget, "File does not exist", "Qt was unable to load the requested file.");
108+
return;
109+
}
110+
QJsonDocument json = QJsonDocument::fromJson(file.readAll());
111+
if(!json.isObject()){
112+
QMessageBox::warning(audioWidget, "Invalid file", "The requested file is not a valid aemus file.");
113+
return;
114+
}
115+
auto configBackup = *config;
116+
if(!deserialize(json.object(), config)) {
117+
*config = configBackup;
118+
QMessageBox::warning(audioWidget, "Invalid file", "The requested file is not a valid aemus file.");
119+
return;
120+
}
121+
rectifyUI(config, state);
122+
}
123+
}));
93124

94-
void checkConnection(bool connection){
95-
if(!connection){
96-
QMessageBox::warning(nullptr, "Qt connection error", "Unable to connect UI component");
97-
}
98125
}
99126

100-
void AudioPlaybackWidget::rectifyUI(std::vector<audioConfig>* config, mediaState* mediaPlayer) {
127+
void AudioPlaybackWidget::rectifyUI(std::vector<audioConfig>* config, mediaState* mediaPlayer, bool bulk) {
101128
// We reconstruct the entire state so we don't mismatch the UI indices
102129
{
103130
QLayoutItem* item;
@@ -117,18 +144,15 @@ void AudioPlaybackWidget::rectifyUI(std::vector<audioConfig>* config, mediaState
117144
config->emplace_back();
118145
}
119146
for(auto conf: *config){
120-
addUIState(config, x, mediaPlayer, true);
147+
addUIState(config, x, mediaPlayer, bulk);
121148
x++;
122149
}
123150
gridLayout_2->update();
124151
}
152+
125153
void AudioPlaybackWidget::addUIState(std::vector<audioConfig>* config, int index, mediaState* mediaPlayer, bool bulk) {
126154
// This is a great time to tell you that we accept pull requests that fix this kind of nonsense
127-
auto newState = UIState{
128-
new QToolButton(musPlayer), new QToolButton(musPlayer), new QCheckBox(musPlayer),
129-
new QSpinBox(musPlayer), new QSpinBox(musPlayer), new QLabel(musPlayer), new QLabel(musPlayer),
130-
new QLabel(musPlayer), new QLabel(musPlayer), new QSlider(musPlayer), new QFrame(musPlayer),
131-
true };
155+
auto newState = UIState();
132156
// Grid layout pos
133157
int vecIdx = (int)vecUIState.size();
134158
int offset = vecIdx * 4;
@@ -141,9 +165,13 @@ void AudioPlaybackWidget::addUIState(std::vector<audioConfig>* config, int index
141165
gridLayout_2->addWidget(newState.playAudio, idxRow1, 0, 1, 1);
142166
// Frame end
143167
newState.endSpinBox->setObjectName(QString::fromUtf8("endSpinBox"));
168+
newState.endSpinBox->setMaximum(INT32_MAX);
169+
newState.endSpinBox->setMinimum(0);
144170
gridLayout_2->addWidget(newState.endSpinBox, idxRow1, 2, 1, 1);
145171
// Frame start
146172
newState.startSpinBox->setObjectName(QString::fromUtf8("startSpinBox"));
173+
newState.startSpinBox->setMaximum(INT32_MAX);
174+
newState.startSpinBox->setMinimum(0);
147175
gridLayout_2->addWidget(newState.startSpinBox, idxRow1, 1 , 1, 1);
148176
// New track
149177
newState.addNewTrack->setObjectName(QString::fromUtf8("startLabel"));
@@ -213,6 +241,20 @@ void AudioPlaybackWidget::addUIState(std::vector<audioConfig>* config, int index
213241
}
214242

215243
if(bulk){
244+
if(index == 0) {
245+
int x = 0;
246+
for(const auto& conf: *config){
247+
if(mediaPlayer->players.size() - 1 >= x){
248+
mediaPlayer->players.at(x)->stop();
249+
mediaPlayer->players.at(x)->deleteLater();
250+
mediaPlayer->outputs.at(x)->deleteLater();
251+
mediaPlayer->players.clear();
252+
mediaPlayer->outputs.clear();
253+
}
254+
addTrack(mediaPlayer, conf.audioPath.absoluteFilePath());
255+
modifyTrack(mediaPlayer, config, x);
256+
}
257+
}
216258
if (index > 0 && index + 1 != config->size() || (index == 1 && config->size() == 2)) {
217259
newState.addNewTrack->setText(QCoreApplication::translate("audioWidget", "Remove audio track", nullptr));
218260
newState.addTrack = false;
@@ -232,6 +274,7 @@ void AudioPlaybackWidget::addUIState(std::vector<audioConfig>* config, int index
232274
));
233275
config->at(index).audioPath = file;
234276
config->at(index).audioName = file.fileName();
277+
modifyTrack(mediaPlayer, config, index);
235278

236279
{
237280
QString ref = QStringRef(&config->at(index).audioName, 0, 17).toString();
@@ -258,6 +301,7 @@ void AudioPlaybackWidget::addUIState(std::vector<audioConfig>* config, int index
258301
return;
259302
}
260303
config->emplace_back();
304+
addTrack(mediaPlayer, QUrl());
261305
this->addUIState(config, index + 1, mediaPlayer);
262306
if (index == 0) {
263307
vecUIState.at(1).addNewTrack->setText(QCoreApplication::translate("audioWidget", "Remove audio track", nullptr));
@@ -273,6 +317,7 @@ void AudioPlaybackWidget::addUIState(std::vector<audioConfig>* config, int index
273317
}
274318
}
275319
else {
320+
removeTrack(mediaPlayer, index);
276321
config->erase(config->begin() + index);
277322
if (config->size() >= vecUIState.size() && !config->empty()) { config->pop_back(); }
278323
rectifyUI(config, mediaPlayer);
@@ -295,26 +340,48 @@ void AudioPlaybackWidget::addUIState(std::vector<audioConfig>* config, int index
295340
}));
296341
checkConnection(QSlider::connect(newState.volumeSlider, &QSlider::valueChanged, [=](int val){
297342
config->at(index).volume = val;
343+
modifyTrack(mediaPlayer, config, index);
298344
newState.volumeLabel->setText(QCoreApplication::translate("audioWidget", "Media volume", nullptr) + " (" + QString::number(val) + "%)");
299345
}));
300346
}
301347

302-
void AudioPlaybackWidget::addTrack(mediaState* state, QUrl source) {
348+
void AudioPlaybackWidget::addTrack(mediaState* state, const QUrl& source) {
303349
auto* mediaPlayer = new QMediaPlayer;
304350
auto* audioOutput = new QAudioOutput;
305351
mediaPlayer->setSource(source);
306352
mediaPlayer->setAudioOutput(audioOutput);
307353
state->players.append(mediaPlayer);
308354
state->outputs.append(audioOutput);
309355
}
310-
void AudioPlaybackWidget::modifyTrack(mediaState* state, std::vector<audioConfig>* config, int index, audioConfig modifiedConfig) {
311-
config->at(index) = std::move(modifiedConfig);
356+
357+
void AudioPlaybackWidget::removeTrack(mediaState* state, int index) {
358+
state->players.at(index)->stop();
359+
state->players.at(index)->deleteLater();
360+
state->outputs.at(index)->deleteLater();
361+
state->players.removeAt(index);
362+
state->outputs.removeAt(index);
363+
}
364+
365+
void AudioPlaybackWidget::modifyTrack(mediaState* state, std::vector<audioConfig>* config, int index) {
366+
const auto& modifiedConfig = config->at(index);
367+
if(state->players.isEmpty() || state->players.size() - 1 < index){
368+
addTrack(state, QUrl());
369+
}
312370
auto* currentPlayer = state->players.at(index);
313371
auto* currentOutput = state->outputs.at(index);
314372
if(currentPlayer->isPlaying()) { currentPlayer->stop(); }
315-
currentPlayer->setSource(modifiedConfig.audioPath.absoluteFilePath());
316-
currentOutput->setVolume((float)modifiedConfig.volume);
373+
if(currentPlayer->source().isEmpty() || currentPlayer->source().fileName() != modifiedConfig.audioPath.fileName()){
374+
currentPlayer->setSource(modifiedConfig.audioPath.absoluteFilePath());
375+
}
376+
if(currentOutput->volume() != getVol(modifiedConfig.volume)){
377+
currentOutput->setVolume(getVol(modifiedConfig.volume));
378+
}
317379
}
318-
void AudioPlaybackWidget::removeTrack(mediaState* state, std::vector<audioConfig>* config, int index) {
319-
Q_UNIMPLEMENTED();
380+
381+
void AudioPlaybackWidget::correctTrackPos(QMediaPlayer* player, int curFrame, int frameCount, int fps, audioConfig& config) {
382+
// Calculate times in milliseconds
383+
const float toMillis = 1000.0;
384+
int frame = frameCount - ((frameCount - config.startFrame) - curFrame);
385+
auto positionMs = static_cast<qint64>((static_cast<float>(frame) / static_cast<float>(fps)) * toMillis);
386+
if (player->hasAudio() && positionMs >= 0) { player->setPosition(positionMs); }
320387
}

src/gui/AudioPlaybackWidget.h

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct audioConfig{
3030
bool playbackEnable = true;
3131
int volume = 100;
3232
int startFrame = 0;
33-
int endFrame = 0;
33+
int endFrame = 250;
3434
};
3535
struct mediaState{
3636
bool playing = false;
@@ -39,17 +39,17 @@ struct mediaState{
3939
};
4040

4141
struct UIState{
42-
QToolButton *addNewTrack;
43-
QToolButton *selectMusButton;
44-
QCheckBox *playAudio;
45-
QSpinBox *endSpinBox;
46-
QSpinBox *startSpinBox;
47-
QLabel *startLabel;
48-
QLabel *endLabel;
49-
QLabel *musDurationLabel;
50-
QLabel *volumeLabel;
51-
QSlider *volumeSlider;
52-
QFrame *line;
42+
QToolButton *addNewTrack{ new QToolButton };
43+
QToolButton *selectMusButton{ new QToolButton };
44+
QCheckBox *playAudio{ new QCheckBox };
45+
QSpinBox *endSpinBox{ new QSpinBox };
46+
QSpinBox *startSpinBox{ new QSpinBox };
47+
QLabel *startLabel{ new QLabel };
48+
QLabel *endLabel{ new QLabel };
49+
QLabel *musDurationLabel{ new QLabel };
50+
QLabel *volumeLabel{ new QLabel };
51+
QSlider *volumeSlider{ new QSlider };
52+
QFrame *line{ new QFrame };
5353
mutable bool addTrack = true;
5454
};
5555

@@ -71,15 +71,15 @@ class AudioPlaybackWidget {
7171

7272
void connect(QWidget *audioWidget, mediaState *state, std::vector<audioConfig>* config);
7373
void addUIState(std::vector<audioConfig>* config, int index, mediaState *mediaPlayer, bool bulk = false);
74-
void rectifyUI(std::vector<audioConfig>* config, mediaState* mediaPlayer);
75-
static void addTrack(mediaState *state, QUrl source);
76-
static void modifyTrack(mediaState *state, std::vector<audioConfig>* config, int index, audioConfig modifiedConfig);
77-
static void removeTrack(mediaState *state, std::vector<audioConfig>* config, int index);
74+
void rectifyUI(std::vector<audioConfig>* config, mediaState* mediaPlayer, bool bulk = true);
75+
static void addTrack(mediaState *state, const QUrl& source);
76+
static void modifyTrack(mediaState *state, std::vector<audioConfig>* config, int index);
77+
static void removeTrack(mediaState *state, int index);
7878
static void aPlayer(std::vector<audioConfig>* pConf, bool play, mediaState* state, int fps, int curFrame, int frameCount);
7979
static bool serialize(std::vector<audioConfig>* pConf, const QString& outPath);
8080
static bool deserialize(const QJsonObject& pConf, std::vector<audioConfig>* playbackConfig) ;
8181
static float getVol(int volume){ return static_cast<float>(volume / 100.0); }
82-
82+
static void correctTrackPos(QMediaPlayer* player, int curFrame, int frameCount, int fps, audioConfig& config);
8383
void setupUi(QWidget *audioWidget, mediaState *mediaPlayer, std::vector<audioConfig>* config){
8484
if (audioWidget->objectName().isEmpty()) {audioWidget->setObjectName(QString::fromUtf8("audioWidget")); }
8585
mainWidget = audioWidget;
@@ -136,7 +136,6 @@ class AudioPlaybackWidget {
136136
loadConfigButton->setText(QCoreApplication::translate("audioWidget", "Load audio configuration from file", nullptr));
137137
tabWidget->setTabText(tabWidget->indexOf(configTab), QCoreApplication::translate("audioWidget", "Save/Load audio config", nullptr));
138138
// Initialize
139-
connect(audioWidget, mediaPlayer, config);
140139
QMetaObject::connectSlotsByName(audioWidget);
141140
config->emplace_back();
142141
rectifyUI(config, mediaPlayer);

src/gui/PlayBackWidget.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ void PlayBackWidget::setPushDelegate(const PushDelegate& aDelegate) {
5757
if(!audioUI->isHidden()){
5858
return;
5959
}
60+
for(auto player: mediaPlayer.players){ player->stop(); }
6061
audioUI->show();
6162
});
6263
}

src/gui/TimeLineInfoWidget.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ void TimeLineInfoWidget::onUpdate() {
3939
auto* player = mProject->mediaPlayer->players.at(currentPlayer);
4040

4141
if(player->isPlaying()){
42+
if(currentFrame +- 1 != latestFrame && currentFrame != latestFrame +- 1 && currentFrame != latestFrame){
43+
qDebug() << "Current = " << currentFrame << "; Latest = " << latestFrame;
44+
AudioPlaybackWidget::correctTrackPos(player, currentFrame, frameMax, fps, const_cast<audioConfig&>(file));
45+
}
4246
qDebug() << "Player at " << currentPlayer << " is playing.";
4347
if (mProject->animator().isSuspended() || file.endFrame <= currentFrame || !file.playbackEnable) {
4448
qDebug("Request player stop");
@@ -49,13 +53,15 @@ void TimeLineInfoWidget::onUpdate() {
4953
qDebug() << "Player at " << currentPlayer << " is suspended.";
5054
if (file.startFrame <= currentFrame && file.endFrame >= currentFrame && file.playbackEnable) {
5155
qDebug("Request player start");
56+
AudioPlaybackWidget::correctTrackPos(player, currentFrame, frameMax, fps, const_cast<audioConfig&>(file));
5257
player->play();
5358
mProject->mediaPlayer->playing = true;
5459
}
5560
}
5661
currentPlayer++;
5762
}
5863
}
64+
latestFrame = timeInfo.frame.get();
5965
}
6066
}
6167
void TimeLineInfoWidget::setPlayback(PlayBackWidget* aPlayback) {

src/gui/TimeLineInfoWidget.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class TimeLineInfoWidget: public QLabel {
3535
? static_cast<core::TimeFormatType>(mSettings.value("generalsettings/ui/timeformat").toInt())
3636
: core::TimeFormatType::TimeFormatType_Frames_From0;
3737
// --------------------- //
38+
int latestFrame = 0;
3839
bool mIsFirstTime;
3940
int mSuspendCount;
4041
};

src/gui/TimeLineWidget.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ void TimeLineWidget::setPlayBackActivity(bool aIsActive, std::vector<audioConfig
5353
mBeginFrame = currentFrame();
5454
mLastFrame = mBeginFrame;
5555
// Play audio
56-
if(pConf->empty()){ qDebug("aConf is empty"); }
5756
AudioPlaybackWidget::aPlayer(pConf, true, mediaPlayer, getFps(), currentFrame().get(),
5857
mProject->attribute().maxFrame());
5958
mediaPlayer->playing = true;
@@ -62,7 +61,6 @@ void TimeLineWidget::setPlayBackActivity(bool aIsActive, std::vector<audioConfig
6261
mTimer.stop();
6362
mBeginFrame.set(0);
6463
mLastFrame.set(0);
65-
if(pConf->empty()){ qDebug("aConf is empty"); }
6664
// Stop audio
6765
if(mediaPlayer->playing) {
6866
AudioPlaybackWidget::aPlayer(pConf, false, mediaPlayer, getFps(), currentFrame().get(),

0 commit comments

Comments
 (0)