# HG changeset patch # User Chris Cannam # Date 1582202378 0 # Thu Feb 20 12:39:38 2020 +0000 # Node ID 12a6eca40e310a5de54665f88ae3981341304d04 # Parent e945fe2d9503865794f9b3c23bd6b87f927cd138 Hook up basic controls; channel-handling fixes diff --git a/example.pro b/example.pro --- a/example.pro +++ b/example.pro @@ -7,9 +7,7 @@ TARGET = "Example" -DEFINES += USE_SPEEX USE_KISSFFT HAVE_PORTAUDIO - -LIBS += -lportaudio +DEFINES += USE_SPEEX USE_KISSFFT INCLUDEPATH += \ ext/rubberband \ @@ -65,18 +63,18 @@ ext/bqaudiostream/src/AudioStreamExceptions.cpp win32-msvc* { - DEFINES += HAVE_MEDIAFOUNDATION _USE_MATH_DEFINES - LIBS += -lmfplat -lmfreadwrite -lmfuuid -lpropsys -ladvapi32 -lwinmm -lws2_32 + DEFINES += HAVE_PORAUDIO HAVE_MEDIAFOUNDATION _USE_MATH_DEFINES + LIBS += -lportaudio -lmfplat -lmfreadwrite -lmfuuid -lpropsys -ladvapi32 -lwinmm -lws2_32 } macx* { - DEFINES += HAVE_COREAUDIO HAVE_VDSP USE_PTHREADS - LIBS += -framework CoreAudio -framework CoreMidi -framework AudioUnit -framework AudioToolbox -framework CoreFoundation -framework CoreServices -framework Accelerate + DEFINES += HAVE_COREAUDIO HAVE_PORTAUDIO HAVE_VDSP USE_PTHREADS + LIBS += -lportaudio -framework CoreAudio -framework CoreMidi -framework AudioUnit -framework AudioToolbox -framework CoreFoundation -framework CoreServices -framework Accelerate } linux* { - DEFINES += HAVE_SNDFILE USE_PTHREADS - LIBS += -lsndfile + DEFINES += HAVE_SNDFILE HAVE_LIBPULSE USE_PTHREADS + LIBS += -lsndfile -lpulse } for (file, BQ_SOURCES) { SOURCES += $$file } diff --git a/src/Interface.cpp b/src/Interface.cpp --- a/src/Interface.cpp +++ b/src/Interface.cpp @@ -6,6 +6,8 @@ #include "Processor.h" #include +#include +#include #include #include @@ -16,6 +18,32 @@ QMainWindow(parent), m_processor(p) { + auto mainFrame = new QFrame; + auto mainLayout = new QGridLayout; + mainFrame->setLayout(mainLayout); + + auto open = new QPushButton(tr("Open")); + mainLayout->addWidget(open, 0, 0); + + auto rewind = new QPushButton(tr("<<")); + mainLayout->addWidget(rewind, 0, 1); + + auto play = new QPushButton(tr(">")); + mainLayout->addWidget(play, 0, 2); + + auto pause = new QPushButton(tr("||")); + mainLayout->addWidget(pause, 0, 3); + + connect(open, SIGNAL(clicked()), this, SLOT(open())); + connect(rewind, SIGNAL(clicked()), this, SLOT(rewind())); + connect(play, SIGNAL(clicked()), this, SLOT(play())); + connect(pause, SIGNAL(clicked()), this, SLOT(pause())); + + m_filenameLabel = new QLabel; + m_filenameLabel->setText(tr("")); + mainLayout->addWidget(m_filenameLabel, 1, 0, 1, 4); + + setCentralWidget(mainFrame); } Interface::~Interface() @@ -47,11 +75,13 @@ { try { m_processor->open(filename); + m_filenameLabel->setText(m_processor->getTrackName()); } catch (const exception &f) { QMessageBox::critical (this, tr("Failed to open file"), tr("Could not open audio file \"%1\": %2") .arg(filename).arg(QString::fromUtf8(f.what()))); + m_filenameLabel->setText(tr("")); } } diff --git a/src/Interface.h b/src/Interface.h --- a/src/Interface.h +++ b/src/Interface.h @@ -37,6 +37,7 @@ private: Processor *m_processor; QDoubleSpinBox *m_speedSpin; + QLabel *m_filenameLabel; }; diff --git a/src/Processor.cpp b/src/Processor.cpp --- a/src/Processor.cpp +++ b/src/Processor.cpp @@ -119,6 +119,14 @@ return m_filename; } +QString +Processor::getTrackName() const +{ + QMutexLocker locker(&m_mutex); + if (m_trackName != "") return m_trackName; + else return QFileInfo(m_filename).baseName(); +} + int Processor::getSampleRate() const { @@ -345,8 +353,6 @@ while (m_status == Working) { - //!!! this should be a call out to a function in m_processor - float *newBlock = 0; try { @@ -456,12 +462,12 @@ m_stretcher->setMaxProcessSize(m_blockSize); - m_stretchIn = new float *[m_stretcher->getChannelCount()]; + m_stretchIn = new float *[m_blocks.channels]; - for (int c = 0; c < (int)m_stretcher->getChannelCount(); ++c) { + for (int c = 0; c < m_blocks.channels; ++c) { m_stretchIn[c] = new float[m_blockSize]; } - m_stretchOutPtrs = new float *[m_stretcher->getChannelCount()]; + m_stretchOutPtrs = new float *[m_blocks.channels]; m_fileReadThread = new FileReadThread(this, &m_blocks, m_rs); m_fileReadThread->start(); @@ -637,11 +643,18 @@ windowSort = 2; } - if (m_windowSort != windowSort || - m_centreFocus != m_lastCentreFocus || - !m_stretcher || - nchannels || m_stretcher->getChannelCount()) { + // We always run the stretcher with m_blocks.channels, the channel + // count of the source file. But nchannels, the channel count of + // the audio device, may differ. We could mix down later after + // stretching (though actually we just discard/silence excess + // channels). + + if (!m_stretcher || + m_windowSort != windowSort || + m_centreFocus != m_lastCentreFocus) { + delete m_stretcher; + RubberBandStretcher::Options options = RubberBandStretcher::OptionProcessRealTime | RubberBandStretcher::OptionTransientsCrisp | @@ -652,7 +665,8 @@ if (m_centreFocus) options |= RubberBandStretcher::OptionChannelsTogether; m_stretcher = new RubberBandStretcher - (m_blocks.sampleRate, nchannels, options, timeRatio, pitchScale); + (m_blocks.sampleRate, m_blocks.channels, + options, timeRatio, pitchScale); m_windowSort = windowSort; } @@ -665,6 +679,8 @@ m_lastCentreFocus = m_centreFocus; + int fileChannels = m_blocks.channels; + // We de-interleave the audio data and write the input for the // stretcher into m_stretchIn. m_blockSize is an arbitrary size // which is both the number of interleaved frames in each block of @@ -672,11 +688,9 @@ // the maximum we can de-interleave and feed in any one chunk. int done = 0; - int fileChannels = m_blocks.channels; - while (done < (int)nframes) { -// cout << "getSourceSamples: nframes = "<< nframes << ", done = " << done << ", m_processBlock = " << m_processBlock << ", blocks = " << m_blocks.blocks.size() << endl; +// cout << "getSourceSamples: nframes = "<< nframes << ", done = " << done << ", m_processBlock = " << m_processBlock << ", blocks = " << m_blocks.blocks.size() << ", m_blockSize = " << m_blockSize << endl; int available = m_stretcher->available(); if (available < 0) break; @@ -721,47 +735,27 @@ m_playing = false; emit playEnded(); } - - // nchannels is the number of channels required for output - // to the audio device. This value came directly from the - // device handler, and the stretcher has been configured - // with a matching channel count. fileChannels is the - // number in the audio file, which may differ. We always - // run the stretcher at the audio device channel count. - - for (int c = 0; c < nchannels && c < fileChannels; ++c) { + + for (int c = 0; c < fileChannels; ++c) { for (int i = 0; i < toProcess; ++i) { m_stretchIn[c][i] = source[(i * fileChannels) + c]; } } - for (int c = fileChannels; c < nchannels; ++c) { - if (c > 0) { - // excess channels on audio output: duplicate the - // first file channel for them (an arbitrary decision) - for (int i = 0; i < toProcess; ++i) { - m_stretchIn[c][i] = m_stretchIn[0][i]; - } - } else { - // zero channels in the file! - for (int i = 0; i < toProcess; ++i) { - m_stretchIn[c][i] = 0.f; - } - } - } - m_stretcher->process(m_stretchIn, toProcess, false); } int count = m_stretcher->available(); if (count == 0) continue; - if (count > ((int)nframes - done)) count = (int)nframes - done; + if (count > ((int)nframes - done)) { + count = (int)nframes - done; + } // m_stretchOutPtrs is a set of temporary pointers indicating // where to write the output to, as a set of offsets into the // desired samples arrays - for (int c = 0; c < nchannels; ++c) { + for (int c = 0; c < fileChannels && c < nchannels; ++c) { m_stretchOutPtrs[c] = samples[c] + done; } @@ -769,7 +763,14 @@ done += count; } - // any excess should be filled up with zero samples + // fill excess channels with zeroes + for (int c = fileChannels; c < nchannels; ++c) { + for (int i = 0; i < (int)nframes; ++i) { + samples[c][i] = 0.f; + } + } + + // and fill excess samples with zeroes (should only happen at end of file) for (int c = 0; c < nchannels; ++c) { for (int i = done; i < (int)nframes; ++i) { samples[c][i] = 0.f; diff --git a/src/Processor.h b/src/Processor.h --- a/src/Processor.h +++ b/src/Processor.h @@ -46,6 +46,7 @@ void cancelFileLoad(); QString getFilename() const; + QString getTrackName() const; int getSampleRate() const; int getChannelCount() const; @@ -110,6 +111,7 @@ protected: QString m_filename; + QString m_trackName; breakfastquay::SystemPlaybackTarget *m_target; RubberBand::RubberBandStretcher *m_stretcher; @@ -120,7 +122,7 @@ mutable QMutex m_mutex; - int m_blockSize; + const int m_blockSize; struct BlockRec { BlockRec(int rate, int c, int bs) :