This repository was archived by the owner on May 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmainwindow.cpp
More file actions
386 lines (319 loc) · 12 KB
/
mainwindow.cpp
File metadata and controls
386 lines (319 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
#include "mainwindow.h"
#include "ui_mainwindow.h"
// constructor: warm up all stuff
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
//,audioInfo(QAudioDeviceInfo::defaultInputDevice())
{
// draws the ui
ui->setupUi(this);
// test for saving settings
QCoreApplication::setOrganizationName("Agostinho");
/** some settings attempt
*/
QSettings settings; /*!<aloha */
settings.setValue("alo","maria");
// defines sample size equals to spectrum size
sample.resize(SPECSIZE);
// threads are as separate processes running within the same
// program. for fft calculation, it is better to move it
// to another thread to make the calcs faster.
// moreover, it will not slow down the ui
// fftThread = new QThread(this);
calculator = new FFTCalc();
// calculator->moveToThread(fftThread);
// launches the new media player
player = new QMediaPlayer();
// starts a new playlist
playlist = new QMediaPlaylist();
// starts the playlist model
playlistModel = new PlaylistModel(this);
// tell playlistmodel where is the playlist
playlistModel->setPlaylist(playlist);
// attach the listView to the playlistModel
ui->listViewPlaylist->setModel(playlistModel);
// set current index to the first element
ui->listViewPlaylist->setCurrentIndex(playlistModel->index(playlist->currentIndex(), 0));
loadPlaylist();
// attachs the playlist to the player
player->setPlaylist(playlist);
// playlist plays in loop mode. It restarts after last song has finished playing.
playlist->setPlaybackMode(QMediaPlaylist::Loop);
// this allow the user to select the media it wants to play
connect(ui->listViewPlaylist, SIGNAL(doubleClicked(QModelIndex)),
this, SLOT(goToItem(QModelIndex)));
// if some metadata changed for media, display it somewhere
// it seems not work on windows
// but works for linux :)
connect(player,SIGNAL(metaDataChanged()),
this, SLOT(metaDataChanged()));
// the media status changed (new stream has arrived)
connect(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
this, SLOT(mediaStatusChanged(QMediaPlayer::MediaStatus)));
// the user selected a new position on music to play
// perharps using some scrollbar
connect(this,SIGNAL(positionChanged(qint64)),
player,SLOT(setPosition(qint64)));
connect(player,SIGNAL(volumeChanged(int)),
ui->control,SLOT(onVolumeChanged(int)));
connect(player,SIGNAL(stateChanged(QMediaPlayer::State)),
SLOT(mediaStateChanged(QMediaPlayer::State)));
// that is the audio probe object that "listen to"
// the music. It will help with fft stuff
probe = new QAudioProbe();
// fft is delivered using a QVector<double> but
// signal/slot scheme does not recognizes this type by default
// therefore, we have to register it
qRegisterMetaType< QVector<double> >("QVector<double>");
// here goes the control unit event handlers
connect(ui->control, SIGNAL(playPause()), this, SLOT(playPause()));
connect(ui->control, SIGNAL(prev()), this, SLOT(prev()));
connect(ui->control, SIGNAL(next()), this, SLOT(next()));
connect(this, SIGNAL(playPauseChanged(bool)),
ui->control,SLOT(onPlayerStateChanged(bool)));
// when the music position changes on player, it has to be
// informed to the control unit to redraw it ui
connect(player, SIGNAL(positionChanged(qint64)),
ui->control,SLOT(onElapsedChanged(qint64)));
// fft goes here...
// if a new audio buffer is ok, we have to make some
// calcs (fft) to display the spectrum
connect(probe, SIGNAL(audioBufferProbed(QAudioBuffer)),
this, SLOT(processBuffer(QAudioBuffer)));
// when fft is available, we deliver it to
// the visualization widget
connect(this, SIGNAL(spectrumChanged(QVector<double>&)),
ui->visualizer,SLOT(loadSamples(QVector<double>&)));
// communicate the left and right audio levels...
// ...mean levels
connect(this, SIGNAL(levels(double,double)),
ui->visualizer,SLOT(loadLevels(double,double)));
// when fft is available, we deliver it to
// the visualization widget
//connect(this, SIGNAL(spectrumChanged(QVector<double>&)),
// ui->glVisualizer,SLOT(loadSamples(QVector<double>&)));
// communicate the left and right audio levels...
// ...mean levels
//connect(this, SIGNAL(levels(double,double)),
// ui->glVisualizer,SLOT(loadLevels(double,double)));
// if the user selected a new position on stream to play
// we have to tell it to the player
connect(ui->control, SIGNAL(elapsedSelected(qint64)),
player, SLOT(setPosition(qint64)));
// changing audio volume
connect(ui->control, SIGNAL(volumeSelected(int)),
player, SLOT(setVolume(int)));
// calculator is the thead that calcs the ffts we need to display
// every time a new spectrum is available, the calculator
// emits a calculatedSpectrum signal
connect(calculator, SIGNAL(calculatedSpectrum(QVector<double>)),
this, SLOT(spectrumAvailable(QVector<double>)));
connect(ui->library,SIGNAL(addMediaToPlayList(QString)),
SLOT(onAddMediaToPlayList(QString)));
// tells the probe what to probe
probe->setSource(player);
// load directories to library
connect(ui->actionLoadDirectory,SIGNAL(triggered()),this,SLOT(onAddFolderToLibrary()));
// load a single file to library
connect(ui->actionLoadFile,SIGNAL(triggered()),this,SLOT(loadMedia()));
}
void MainWindow::goToItem(const QModelIndex &index){
if (index.isValid()) {
playlist->setCurrentIndex(index.row());
player->play();
}
}
// prepares the playlist to display the media to be played
// it should be stored into a file with settings.
// I will let this to a far future
void MainWindow::loadPlaylist(void){
}
void MainWindow::onAddFolderToLibrary(){
QFileDialog d;
QString folder;
folder = d.getExistingDirectory(this,tr("Select Folder"),
QDir::homePath(),
QFileDialog::ShowDirsOnly |
QFileDialog::DontResolveSymlinks);
ui->library->addToSearchPath(folder);
}
void MainWindow::onAddMediaToPlayList(QString media){
playlist->addMedia(QUrl::fromLocalFile(media));
}
// process audio buffer for fft calculations
void MainWindow::processBuffer(QAudioBuffer buffer){
qreal peakValue;
int duration;
if(buffer.frameCount() < 512)
return;
// return left and right audio mean levels
levelLeft = levelRight = 0;
// It only knows how to process stereo audio frames
// mono frames = :P
if(buffer.format().channelCount() != 2)
return;
sample.resize(buffer.frameCount());
// audio is signed int
if(buffer.format().sampleType() == QAudioFormat::SignedInt){
QAudioBuffer::S16S *data = buffer.data<QAudioBuffer::S16S>();
// peak value changes according to sample size.
if (buffer.format().sampleSize() == 32)
peakValue=INT_MAX;
else if (buffer.format().sampleSize() == 16)
peakValue=SHRT_MAX;
else
peakValue=CHAR_MAX;
// scale everything to [0,1]
for(int i=0; i<buffer.frameCount(); i++){
// for visualization purposes, we only need one of the
// left/right channels
sample[i] = data[i].left/peakValue;
levelLeft+= abs(data[i].left)/peakValue;
levelRight+= abs(data[i].right)/peakValue;
}
}
// audio is unsigned int
else if(buffer.format().sampleType() == QAudioFormat::UnSignedInt){
QAudioBuffer::S16U *data = buffer.data<QAudioBuffer::S16U>();
if (buffer.format().sampleSize() == 32)
peakValue=UINT_MAX;
else if (buffer.format().sampleSize() == 16)
peakValue=USHRT_MAX;
else
peakValue=UCHAR_MAX;
for(int i=0; i<buffer.frameCount(); i++){
sample[i] = data[i].left/peakValue;
levelLeft+= abs(data[i].left)/peakValue;
levelRight+= abs(data[i].right)/peakValue;
}
}
// audio is float type
else if(buffer.format().sampleType() == QAudioFormat::Float){
QAudioBuffer::S32F *data = buffer.data<QAudioBuffer::S32F>();
peakValue = 1.00003;
for(int i=0; i<buffer.frameCount(); i++){
sample[i] = data[i].left/peakValue;
// test if sample[i] is infinity (it works)
// some tests produced infinity values :p
if(sample[i] != sample[i]){
sample[i] = 0;
}
else{
levelLeft+= abs(data[i].left)/peakValue;
levelRight+= abs(data[i].right)/peakValue;
}
}
}
// if the probe is listening to the audio
// do fft calculations
// when it is done, calculator will tell us
if(probe->isActive()){
duration = buffer.format().durationForBytes(buffer.frameCount())/1000;
//qDebug() << "duracao =" << duration;
calculator->calc(sample, duration);
}
// tells anyone interested about left and right mean levels
emit levels(levelLeft/buffer.frameCount(),levelRight/buffer.frameCount());
}
// what to do when fft spectrum is available
void MainWindow::spectrumAvailable(QVector<double> spectrum){
// just tell the spectrum
// the visualization widget will catch the signal...
emit spectrumChanged(spectrum);
}
// destructor... clear all mess
MainWindow::~MainWindow(){
//stops the player
player->stop();
// wait for the calculator to stop
//calculator.wait();
// delete the fftTrhead
delete calculator;
// fftThread->exit();
// fftThread->wait();
// delete fftThread;
// finish the ui
delete ui;
}
void MainWindow::loadMedia(){
// mime database to detect file type
QMimeDatabase db;
// the mime type (to test if it is an audio file
QMimeType type;
// file list to be inserted into playlist
QStringList filelist;
// audio file to be opened
QFileDialog d;
filelist = d.getOpenFileNames(this,tr("Open File"),
"/home",
tr("Audio (*.wav *.mp3 *.ogg *.flac)"));
// retrieve mime type
for(QList<QString>::const_iterator it=filelist.begin(); it!= filelist.end(); it++){
type = db.mimeTypeForFile(*it);
// test if the file is an audio file
// if yes, send it to the playlist
if(type.name().startsWith("audio")){
playlist->addMedia(QUrl::fromLocalFile(*it));
}
}
}
// play the previous song
void MainWindow::prev(){
playlist->previous();
}
// play next song
void MainWindow::next(){
playlist->next();
}
void MainWindow::setMediaAt(qint32 percent){
if(percent < 0){
percent = 0;
}
if(percent > 100){
percent = 100;
}
emit positionChanged(percent*player->duration()/100);
}
void MainWindow::mediaStateChanged(QMediaPlayer::State state){
if(state == QMediaPlayer::PlayingState){
emit playPauseChanged(true);
}
else{
emit playPauseChanged(false);
}
}
// new song arriving
void MainWindow::mediaStatusChanged(QMediaPlayer::MediaStatus status){
Q_UNUSED(status);
ui->control->onDurationChanged(player->duration());
}
// this is for windows compilations
// display the song info
// and now the linux one
// display the song info
// void QMediaObject::metaDataChanged(const QString & key, const QVariant & value) [signal]
void MainWindow::metaDataChanged(){
if(player->isMetaDataAvailable()){
ui->widgetInfo->setAtribute("AlbumArtist",player->metaData("AlbumArtist").toString());
ui->widgetInfo->setAtribute("Title",player->metaData("Title").toString());
ui->widgetInfo->setAtribute("AlbumTitle",player->metaData("AlbumTitle").toString());
ui->widgetInfo->setAtribute("TrackNumber",player->metaData("TrackNumber").toString());
ui->widgetInfo->setAtribute("AudioBitRate",player->metaData("AudioBitRate").toString());
ui->widgetInfo->setAtribute("AudioCodec",player->metaData("AudioCodec").toString());
}
// pehraps lots of metadata may be available, only the above are passed ahead
// ui->widgetInfo->setAtribute(key,variant.toString());*/
}
// sets up the volume
void MainWindow::setVolume(int volume){
player->setVolume(volume);
}
// deal with play/pause button
// no explanation needed here
void MainWindow::playPause(){
if(player->state() == (QMediaPlayer::PausedState|QMediaPlayer::StoppedState))
player->play();
else
player->pause();
}