12a6eca40e31 — Chris Cannam 4 years ago
Hook up basic controls; channel-handling fixes
5 files changed, 82 insertions(+), 50 deletions(-)

M example.pro
M src/Interface.cpp
M src/Interface.h
M src/Processor.cpp
M src/Processor.h
M example.pro +7 -9
@@ 7,9 7,7 @@ CONFIG += release c++11 object_parallel_
 
 TARGET = "Example"
 
-DEFINES += USE_SPEEX USE_KISSFFT HAVE_PORTAUDIO
-
-LIBS += -lportaudio
+DEFINES += USE_SPEEX USE_KISSFFT
 
 INCLUDEPATH += \
         ext/rubberband \

          
@@ 65,18 63,18 @@ BQ_SOURCES += \
         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 }

          
M src/Interface.cpp +30 -0
@@ 6,6 6,8 @@ 
 #include "Processor.h"
 
 #include <QFileDialog>
+#include <QGridLayout>
+#include <QPushButton>
 #include <QMessageBox>
 
 #include <bqaudiostream/AudioReadStreamFactory.h>

          
@@ 16,6 18,32 @@ Interface::Interface(Processor *p, QWidg
     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("<no file loaded>"));
+    mainLayout->addWidget(m_filenameLabel, 1, 0, 1, 4);
+
+    setCentralWidget(mainFrame);
 }
 
 Interface::~Interface()

          
@@ 47,11 75,13 @@ Interface::open(QString filename)
 {
     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("<no file loaded>"));
     }
 }
 

          
M src/Interface.h +1 -0
@@ 37,6 37,7 @@ protected slots:
 private:
     Processor *m_processor;
     QDoubleSpinBox *m_speedSpin;
+    QLabel *m_filenameLabel;
 };
 
 

          
M src/Processor.cpp +41 -40
@@ 119,6 119,14 @@ Processor::getFilename() const
     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 @@ Processor::FileReadThread::run()
 
     while (m_status == Working) {
 
-        //!!! this should be a call out to a function in m_processor
-
         float *newBlock = 0;
 
         try {

          
@@ 456,12 462,12 @@ Processor::open(QString filename)
 
     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 @@ Processor::getSourceSamples(float *const
         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 @@ Processor::getSourceSamples(float *const
         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 @@ Processor::getSourceSamples(float *const
 
     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 @@ Processor::getSourceSamples(float *const
     // 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 @@ Processor::getSourceSamples(float *const
                 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 @@ Processor::getSourceSamples(float *const
         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;

          
M src/Processor.h +3 -1
@@ 46,6 46,7 @@ public:
     void cancelFileLoad();
 
     QString getFilename() const;
+    QString getTrackName() const;
     
     int getSampleRate() const;
     int getChannelCount() const;

          
@@ 110,6 111,7 @@ public slots:
 
 protected:
     QString m_filename;
+    QString m_trackName;
 
     breakfastquay::SystemPlaybackTarget *m_target;
     RubberBand::RubberBandStretcher *m_stretcher;

          
@@ 120,7 122,7 @@ protected:
 
     mutable QMutex m_mutex;
 
-    int m_blockSize;
+    const int m_blockSize;
 
     struct BlockRec {
         BlockRec(int rate, int c, int bs) :