// fftcalc class is designed to treat with fft calculations FFTCalc::FFTCalc(QObject *parent) :QObject(parent){ // fftcalc is done in other thread // so it cannot overload the main thread processor.moveToThread(&processorThread); // qRegisterMetaType is used to register QVector<double> as the typename for QVector<double> // it is necessary for signal/slots events treatment. qRegisterMetaType< QVector<double> >("QVector<double>"); // when the spectrum is calculated, setSpectrum function will comunicate // such spectrum to Qt as an emitted signal connect(&processor, SIGNAL(calculatedSpectrum(QVector<double>)), SLOT(setSpectrum(QVector<double>))); // when the processor finishes the calculation, the busy condition is changed. connect(&processor, SIGNAL(allDone()),SLOT(freeCalc())); // start the processor thread with low priority processorThread.start(QThread::LowestPriority); // initially, the processor is not occupied isBusy = false; }
void BufferProcessor::run(){ // spectrum amplitude qreal amplitude; qreal SpectrumAnalyserMultiplier = 0.15e-3; // tells when all chunks has been processed if(pass == chunks){ emit allDone(); return; } // we does not calc spectra when array is too small if(array.size() < SPECSIZE){ return; } // prepare complex frame for fft calculations for(uint i=0; i<SPECSIZE; i++){ complexFrame[i] = Complex(window[i]*array[i+pass*SPECSIZE],0); } // do the magic fft(complexFrame); // some scaling/windowing is needed for displaying the fourier spectrum somewhere for(uint i=0; i<SPECSIZE/2;i++){ amplitude = SpectrumAnalyserMultiplier*std::abs(complexFrame[i]); amplitude = qMax(qreal(0.0), amplitude); amplitude = qMin(qreal(1.0), amplitude); complexFrame[i] = amplitude; } // audio spectrum is usually compressed for better displaying if(compressed){ for (int i = 0; i <SPECSIZE/2; i ++){ /* sum up values in freq array between logscale[i] and logscale[i + 1], including fractional parts */ int a = ceilf (logscale[i]); int b = floorf (logscale[i+1]); float sum = 0; if (b < a) sum += complexFrame[b].real()*(logscale[i+1]-logscale[i]); else{ if (a > 0) sum += complexFrame[a-1].real()*(a-logscale[i]); for (; a < b; a++) sum += complexFrame[a].real(); if (b < SPECSIZE/2) sum += complexFrame[b].real()*(logscale[i+1] - b); } /* fudge factor to make the graph have the same overall height as a 12-band one no matter how many bands there are */ sum *= (float) SPECSIZE/24; /* convert to dB */ float val = 20*log10f (sum); /* scale (-DB_RANGE, 0.0) to (0.0, 1.0) */ val = 1 + val / 40; spectrum[i] = CLAMP (val, 0, 1); } } else{ // if not compressed, just copy the real part clamped between 0 and 1 for(int i=0; i<SPECSIZE/2; i++){ spectrum[i] = CLAMP(complexFrame[i].real()*100,0,1); } } // emit the spectrum emit calculatedSpectrum(spectrum); // count the pass pass++; }
void FFTCalc::setSpectrum(QVector<double> spectrum){ // tells Qt about that a new spectrum has arrived emit calculatedSpectrum(spectrum); }
// 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())); // it connects the signals emiteds via the visualizer to the buttons (ui->control) and the lightCycle(ui->widgetInfo) connect(ui->visualizer,SIGNAL(trocaCor(QColor)),ui->control,SLOT(onColorChanged(QColor))); connect(ui->visualizer,SIGNAL(trocaCor(QColor)),ui->widgetInfo,SLOT(changedColor(QColor))); //this->setStyleSheet(QString("QMainWindow {background-color: black}")); }