view audio/AudioCallbackRecordTarget.cpp @ 574:b3c35447ef31 3.0-integration

Wire up record monitoring
author Chris Cannam
date Wed, 04 Jan 2017 16:03:12 +0000
parents audio/AudioRecordTarget.cpp@9fb190c6521b
children c2e27ad7f408
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

/*
    Sonic Visualiser
    An audio file viewer and annotation editor.
    Centre for Digital Music, Queen Mary, University of London.
    
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#include "AudioCallbackRecordTarget.h"

#include "base/ViewManagerBase.h"
#include "base/TempDirectory.h"

#include "data/model/WritableWaveFileModel.h"

#include <QDir>

AudioCallbackRecordTarget::AudioCallbackRecordTarget(ViewManagerBase *manager,
                                                     QString clientName) :
    m_viewManager(manager),
    m_clientName(clientName.toUtf8().data()),
    m_recording(false),
    m_recordSampleRate(44100),
    m_recordChannelCount(2),
    m_frameCount(0),
    m_model(0),
    m_inputLeft(0.f),
    m_inputRight(0.f)
{
    m_viewManager->setAudioRecordTarget(this);

    connect(this, SIGNAL(recordStatusChanged(bool)),
            m_viewManager, SLOT(recordStatusChanged(bool)));
}

AudioCallbackRecordTarget::~AudioCallbackRecordTarget()
{
    QMutexLocker locker(&m_mutex);
    m_viewManager->setAudioRecordTarget(0);
}

int
AudioCallbackRecordTarget::getApplicationSampleRate() const
{
    return 0; // don't care
}

int
AudioCallbackRecordTarget::getApplicationChannelCount() const
{
    return m_recordChannelCount;
}

void
AudioCallbackRecordTarget::setSystemRecordBlockSize(int)
{
}

void
AudioCallbackRecordTarget::setSystemRecordSampleRate(int n)
{
    m_recordSampleRate = n;
}

void
AudioCallbackRecordTarget::setSystemRecordLatency(int)
{
}

void
AudioCallbackRecordTarget::setSystemRecordChannelCount(int c)
{
    m_recordChannelCount = c;
}

void
AudioCallbackRecordTarget::putSamples(const float *const *samples, int, int nframes)
{
    bool secChanged = false;
    sv_frame_t frameToEmit = 0;

    {
        QMutexLocker locker(&m_mutex); //!!! bad here
        if (!m_recording) return;

        m_model->addSamples(samples, nframes);

        sv_frame_t priorFrameCount = m_frameCount;
        m_frameCount += nframes;

        RealTime priorRT = RealTime::frame2RealTime
            (priorFrameCount, m_recordSampleRate);
        RealTime postRT = RealTime::frame2RealTime
            (m_frameCount, m_recordSampleRate);

        secChanged = (postRT.sec > priorRT.sec);
        if (secChanged) {
            m_model->updateModel(); //!!! v bad here
            frameToEmit = m_frameCount;
        }
    }

    if (secChanged) {
        emit recordDurationChanged(frameToEmit, m_recordSampleRate);
    }
}

void
AudioCallbackRecordTarget::setInputLevels(float left, float right)
{
    if (left > m_inputLeft) m_inputLeft = left;
    if (right > m_inputRight) m_inputRight = right;
}

bool
AudioCallbackRecordTarget::getInputLevels(float &left, float &right)
{
    left = m_inputLeft;
    right = m_inputRight;
    m_inputLeft = 0.f;
    m_inputRight = 0.f;
    return true;
}

void
AudioCallbackRecordTarget::modelAboutToBeDeleted()
{
    QMutexLocker locker(&m_mutex);
    if (sender() == m_model) {
        m_model = 0;
        m_recording = false;
    }
}

QString
AudioCallbackRecordTarget::getRecordContainerFolder()
{
    QDir parent(TempDirectory::getInstance()->getContainingPath());
    QString subdirname("recorded");

    if (!parent.mkpath(subdirname)) {
        SVCERR << "ERROR: AudioCallbackRecordTarget::getRecordContainerFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl;
        return "";
    } else {
        return parent.filePath(subdirname);
    }
}

QString
AudioCallbackRecordTarget::getRecordFolder()
{
    QDir parent(getRecordContainerFolder());
    QDateTime now = QDateTime::currentDateTime();
    QString subdirname = QString("%1").arg(now.toString("yyyyMMdd"));

    if (!parent.mkpath(subdirname)) {
        SVCERR << "ERROR: AudioCallbackRecordTarget::getRecordFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl;
        return "";
    } else {
        return parent.filePath(subdirname);
    }
}

WritableWaveFileModel *
AudioCallbackRecordTarget::startRecording()
{
    {
        QMutexLocker locker(&m_mutex);
    
        if (m_recording) {
            SVCERR << "WARNING: AudioCallbackRecordTarget::startRecording: We are already recording" << endl;
            return 0;
        }

        m_model = 0;
        m_frameCount = 0;

        QString folder = getRecordFolder();
        if (folder == "") return 0;
        QDir recordedDir(folder);

        QDateTime now = QDateTime::currentDateTime();

        // Don't use QDateTime::toString(Qt::ISODate) as the ":" character
        // isn't permitted in filenames on Windows
        QString nowString = now.toString("yyyyMMdd-HHmmss-zzz");
    
        QString filename = tr("recorded-%1.wav").arg(nowString);
        QString label = tr("Recorded %1").arg(nowString);

        m_audioFileName = recordedDir.filePath(filename);

        m_model = new WritableWaveFileModel(m_recordSampleRate,
                                            m_recordChannelCount,
                                            m_audioFileName);

        if (!m_model->isOK()) {
            SVCERR << "ERROR: AudioCallbackRecordTarget::startRecording: Recording failed"
                   << endl;
            //!!! and throw?
            delete m_model;
            m_model = 0;
            return 0;
        }

        m_model->setObjectName(label);
        m_recording = true;
    }

    emit recordStatusChanged(true);
    return m_model;
}

void
AudioCallbackRecordTarget::stopRecording()
{
    {
        QMutexLocker locker(&m_mutex);
        if (!m_recording) {
            SVCERR << "WARNING: AudioCallbackRecordTarget::startRecording: Not recording" << endl;
            return;
        }

        m_model->writeComplete();
        m_model = 0;
        m_recording = false;
    }

    emit recordStatusChanged(false);
    emit recordCompleted();
}