Zugriff auf Methoden anderer Klassen
-
Erstmal vielen Dank für die Unterstützung.
Damit bin ich jetzt schon einen großen Schritt weitergekommen.Ein, zwei Fragen habe ich aber dennoch
AudioRecorder *audioRecorder = new AudioRecorder;
Wann und wo müsste ich am Besten audioRecorder wieder löschen? Nach meinem Verständnis wäre es hier wohl richtig:
MainWindow::~MainWindow() {delete ui;}
nur ist audioRecorder ja dort gar nicht bekannt.
Anderes Thema, ich habe nun über connect den Weg von Form "MainWindow" nach AudioRecorder.
Nun müsste ich die Objekte auf Form "MainWindow" mit Aktualwerten vom AudioRecorder beschreiben.Hier ein ein etwas zusammengestückeltes Fragment aus der AudioRecorder.cpp
AudioRecorder::AudioRecorder(QWidget *parent) : QDialog(parent), ui(new Ui::AudioRecorder) { ui->setupUi(this); m_audioRecorder = new QAudioRecorder(this); m_probe = new QAudioProbe(this); connect(m_probe, &QAudioProbe::audioBufferProbed, this, &AudioRecorder::processBuffer); m_probe->setSource(m_audioRecorder); ************* void AudioRecorder::updateProgress(qint64 duration) { if (m_audioRecorder->error() != QMediaRecorder::NoError || duration < 2000) return; ui->statusbar->showMessage(tr("Recorded %1 sec").arg(duration / 1000)); }
so wurde es ursprünglich geschrieben. Gibt es eine Möglichkeit "ui->" mit der MainForm auszutauschen?
Oder brauche ich jetzt public Slots in der Class MainWindow? Wie sollten die aussehen?Schon mal vielen Dank für eure Mühe...
-
ad Frage 1)
DerAudioRecorder
heißt einmalaudioRecorder
und es sieht so aus, als wird er im Scope einer Methode instatiiert. Dort muss er wieder gelöscht werden, ansonsten leaked er. Das ist der falsche Weg, da die angeschlossenen Signale offenbar den Scope der Methode (Konstruktor?) überleben sollen.Ein anderes mal heißt er
m_audioRecorder
. Das scheint der richtige Weg zu sein: Eine member variable, im Header deklariert und ergo im Destruktor bekannt. Damit kann er Destruktor der Klasse gelöscht werden in der die member variable deklariert wurde. Da nur Code-Fragmente gepostet sind, ist das wohl der Destruktor vonMainWindow
. Dort gehört danndelete m_audioRecorder;
hinein.Eine stabile und einfache Variante, die ich gerne verwende, ist
std::unique_ptr
. Damit braucht man sich um's Löschen nicht mehr zu kümmern.Deklaration im header:
std::unique_ptr<AudioRecorder> m_audioRecorder;
Implementierung:
m_audioRecorder.reset(new AudioRecorder);
ad Frage 2)
Verstehe ich nicht ganz.ui->
ist nur in der Ui-Klasse bekannt. Die Kommunikation sollte über Signals/Slots erfolgen.
Wie die aussehen sollen - das sprengt diesen Rahmen etwas. -
@JonB said in Zugriff auf Methoden anderer Klassen:
Also die connect() wird nicht ausgeführt.
Das stimmt.
Besser:const bool success = connect(...); Q_ASSERT(success).
@Axel-Spoerl said in Zugriff auf Methoden anderer Klassen:
const bool success = connect(...);
Q_ASSERT(success).
[Auf Englisch, entschuldigung :) ]
This bothered me, having to write 2 lines for each such statement. What we want is something which always evaluates/executes its argument (unlikeQ_ASSERT()
which may not), but only bothers to test the result and fail in debug mode (likeQ_ASSERT()
). Turns outQ_ASSUME(connect(...));
does just this :)
-
Nice find!
-
Hallo,
ist nun schon eine Weile her, kämpfe aber noch immer mit meinem Problem.
Daher möchte ich das jetzt nochmal aufgreifen.Ich möchte auf Objekte auf dem Form MainWindow zugreifen.
Habe jetzt folgendes deklariert:class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); public slots: QStatusBar statusbar(); QPushButton recordButton(); QPushButton pauseButton(); QComboBox *bitrateBox(); QVBoxLayout levelsLayout(); QSlider qualitySlider(); QRadioButton constantQualityRadioButton(); private slots: void on_pushButton_clicked(); void on_actionAufnahmeparameter_triggered(); private: Ui::MainWindow *ui; AudioRecorder *m_audioRecorder = nullptr; void clearAudioLevels(); }; #endif // MAINWINDOW_H
Auf die public_slots möchte ich von der audiorecorder.cpp zugreifen.
Dazu habe ich dort folgendes deklariert:
audiorecorder.h (auszugsweise)
private: MainWindow *m_mainWindow = nullptr;
audiorecorder.cpp (auszugsweise)
AudioRecorder::AudioRecorder(QWidget *parent) : QDialog(parent), ui(new Ui::AudioRecorder) { ui->setupUi(this); m_audioRecorder = new QAudioRecorder(this); m_probe = new QAudioProbe(this); connect(m_probe, &QAudioProbe::audioBufferProbed, this, &AudioRecorder::processBuffer); m_probe->setSource(m_audioRecorder); m_mainWindow = new QMainWindow(MainWindow); } AudioRecorder::~AudioRecorder() { delete ui; delete m_mainWindow; } void AudioRecorder::onStateChanged(QMediaRecorder::State state) { switch (state) { case QMediaRecorder::RecordingState: //ui->recordButton->setText(tr("Stop")); //ui->pauseButton->setText(tr("Pause")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Pause")); break; case QMediaRecorder::PausedState: //ui->recordButton->setText(tr("Stop")); //ui->pauseButton->setText(tr("Resume")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Resume")); break; case QMediaRecorder::StoppedState: //ui->recordButton->setText(tr("Record")); //ui->pauseButton->setText(tr("Pause")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Pause")); break; } //ui->pauseButton->setEnabled(m_audioRecorder->state() != QMediaRecorder::StoppedState); m_mainWindow->pauseButton().setEnabled(m_audioRecorder->state() != QMediaRecorder::StoppedState); }
Ich kann nun, wie man in der Funktion "onStateChanged" beispielhaft sehen kann, auf die Objekte von MainWindow zugreifen.
Es wird auch nichts als Fehler oder Problem markiert. Sieht erstmal schön aus.
Lässt sich nur leider nicht kompilieren. Bekomme folgende Fehlermeldung:audiorecorder.h:57: Fehler: ‘MainWindow’ does not name a type; did you mean ‘QMainWindow’? In file included from mainwindow.h:17, from mainwindow.cpp:1: audiorecorder.h:57:5: error: ‘MainWindow’ does not name a type; did you mean ‘QMainWindow’? 57 | MainWindow *m_mainWindow = nullptr; | ^~~~~~~~~~ | QMainWindow mainwindow.cpp:11: Fehler: expected primary-expression before ‘)’ token mainwindow.cpp:11:55: error: expected primary-expression before ‘)’ token 11 | m_audioRecorder = new QAudioRecorder(AudioRecorder); | ^ mainwindow.h:49: Fehler: ‘AudioRecorder’ does not name a type; did you mean ‘QAudioRecorder’? In file included from audiorecorder.h:13, from audiorecorder.cpp:1: mainwindow.h:49:5: error: ‘AudioRecorder’ does not name a type; did you mean ‘QAudioRecorder’? 49 | AudioRecorder *m_audioRecorder = nullptr; | ^~~~~~~~~~~~~ | QAudioRecorder audiorecorder.cpp:25: Fehler: expected primary-expression before ‘)’ token audiorecorder.cpp:25:46: error: expected primary-expression before ‘)’ token 25 | m_mainWindow = new QMainWindow(MainWindow); | ^
Ich habe zwar Geduld, aber nach zwei Wochen ohne Erfolg hoffe ich auf ein wenig Hilfe von euch...
VG
Matze -
Bitte poste alle .cpp und .h files vollständig.
Das Problem liest sich wie rekursive #include statements. Das bekommen wir in den Griff.
LG
Axel -
@MHage said in Zugriff auf Methoden anderer Klassen:
in MainWindow.h hast du
AudioRecorder *m_audioRecorder = nullptr;
in AudioRecorder hast du
m_mainWindow = new QMainWindow(MainWindow);
hier stimmt was hinten und vorne nicht und wenn es kompilieren würde, fürchte ich gehst du davon aus, dass du nur 1 MainWindow Instance hast, anstatt 2.
Es sieht für mich so aus, als ob dein AudioRecorder instance in MainWindow instanziert wird. So weit so gut, dann brauchst du entweder Setters in AudioRecorder die du von deiner MainWindow instance aus fütterst oder du kommunizierst via Signals und Slots zwischen den beiden Instanzen. Was auch prinzipiell der bessere Weg ist. Deutlich weniger Spaghetti dann
-
Das lässt hoffen. Ich habe nämlich komplett den Faden verloren...
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QUrl> #include <QDir> #include <QFileDialog> #include <QStandardPaths> #include <QObject> #include <QMultimedia> #include <QAudioRecorder> #include <QMediaRecorder> #include <QSlider> #include <QRadioButton> #include "audiodevicesbase.h" #include "audiorecorder.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); public slots: QStatusBar statusbar(); QPushButton recordButton(); QPushButton pauseButton(); QComboBox *bitrateBox(); QVBoxLayout levelsLayout(); QSlider qualitySlider(); QRadioButton constantQualityRadioButton(); private slots: void on_pushButton_clicked(); void on_actionAufnahmeparameter_triggered(); private: Ui::MainWindow *ui; AudioRecorder *m_audioRecorder = nullptr; void clearAudioLevels(); }; #endif // MAINWINDOW_H
mainwoindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); m_audioRecorder = new QAudioRecorder(AudioRecorder); //connect(ui->constantQualityRadioButton, &QRadioButton::toggled, this, &MainWindow::setEnabled); //connect(ui->constantBitrateRadioButton, &QRadioButton::toggled, this, &MainWindow::setEnabled); //Q_ASSUME(connect(ui->actionSpeichern, &QMenuBar::triggered, audioRecorder, &AudioRecorder::setOutputLocation)); Q_ASSUME(connect(ui->outputButton, &QPushButton::clicked, m_audioRecorder, &AudioRecorder::setOutputLocation)); Q_ASSUME(connect(ui->recordButton, &QPushButton::clicked, m_audioRecorder, &AudioRecorder::toggleRecord)); Q_ASSUME(connect(ui->pauseButton, &QPushButton::clicked, m_audioRecorder, &AudioRecorder::togglePause)); //quality ui->qualitySlider->setRange(0, int(QMultimedia::VeryHighQuality)); ui->qualitySlider->setValue(int(QMultimedia::NormalQuality)); //bitrates: ui->bitrateBox->addItem(tr("Default"), QVariant(0)); ui->bitrateBox->addItem(QStringLiteral("32000"), QVariant(32000)); ui->bitrateBox->addItem(QStringLiteral("64000"), QVariant(64000)); ui->bitrateBox->addItem(QStringLiteral("96000"), QVariant(96000)); ui->bitrateBox->addItem(QStringLiteral("128000"), QVariant(128000)); } MainWindow::~MainWindow() { delete ui; delete m_audioRecorder; }
audiorecorder.h
#ifndef AUDIORECORDER_H #define AUDIORECORDER_H #include <QUrl> #include <QAudioProbe> #include <QAudioRecorder> #include <QMediaRecorder> #include <QDir> #include <QFileDialog> #include <QStandardPaths> #include <QDialog> #include "mainwindow.h" QT_BEGIN_NAMESPACE namespace Ui { class AudioRecorder; } class QAudioRecorder; class QAudioProbe; class QAudioBuffer; QT_END_NAMESPACE class AudioLevel; class AudioRecorder : public QDialog { Q_OBJECT public: explicit AudioRecorder(QWidget *parent = nullptr); ~AudioRecorder(); public slots: void processBuffer(const QAudioBuffer&); void setOutputLocation(); void togglePause(); void toggleRecord(); void updateStatus(QMediaRecorder::Status); void onStateChanged(QMediaRecorder::State); void updateProgress(qint64 pos); void displayErrorMessage(); private slots: private: QAudioRecorder *m_audioRecorder = nullptr; bool m_outputLocationSet = false; QAudioProbe *m_probe = nullptr; QList<AudioLevel*> m_audioLevels; void clearAudioLevels(); MainWindow *m_mainWindow = nullptr; Ui::AudioRecorder *ui = nullptr; }; #endif // AUDIORECORDER_H
audiorecorder.cpp
#include "audiorecorder.h" #include "audiolevel.h" #include "ui_audiorecorder.h" #include "mainwindow.h" #include "ui_mainwindow.h" static qreal getPeakValue(const QAudioFormat &format); static QVector<qreal> getBufferLevels(const QAudioBuffer &buffer); template <class T> static QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels); AudioRecorder::AudioRecorder(QWidget *parent) : QDialog(parent), ui(new Ui::AudioRecorder) { ui->setupUi(this); m_audioRecorder = new QAudioRecorder(this); m_probe = new QAudioProbe(this); connect(m_probe, &QAudioProbe::audioBufferProbed, this, &AudioRecorder::processBuffer); m_probe->setSource(m_audioRecorder); m_mainWindow = new QMainWindow(MainWindow); //audio devices ui->audioDeviceBox->addItem(tr("Default"), QVariant(QString())); for (auto &device: m_audioRecorder->audioInputs()) { ui->audioDeviceBox->addItem(device, QVariant(device)); } //audio codecs ui->audioCodecBox->addItem(tr("Default"), QVariant(QString())); for (auto &codecName: m_audioRecorder->supportedAudioCodecs()) { ui->audioCodecBox->addItem(codecName, QVariant(codecName)); } //containers ui->containerBox->addItem(tr("Default"), QVariant(QString())); for (auto &containerName: m_audioRecorder->supportedContainers()) { ui->containerBox->addItem(containerName, QVariant(containerName)); } //sample rate ui->sampleRateBox->addItem(tr("Default"), QVariant(0)); for (int sampleRate: m_audioRecorder->supportedAudioSampleRates()) { ui->sampleRateBox->addItem(QString::number(sampleRate), QVariant( sampleRate)); } //channels ui->channelsBox->addItem(tr("Default"), QVariant(-1)); ui->channelsBox->addItem(QStringLiteral("1"), QVariant(1)); ui->channelsBox->addItem(QStringLiteral("2"), QVariant(2)); ui->channelsBox->addItem(QStringLiteral("4"), QVariant(4)); /* //quality ui->qualitySlider->setRange(0, int(QMultimedia::VeryHighQuality)); ui->qualitySlider->setValue(int(QMultimedia::NormalQuality)); //bitrates: ui->bitrateBox->addItem(tr("Default"), QVariant(0)); ui->bitrateBox->addItem(QStringLiteral("32000"), QVariant(32000)); ui->bitrateBox->addItem(QStringLiteral("64000"), QVariant(64000)); ui->bitrateBox->addItem(QStringLiteral("96000"), QVariant(96000)); ui->bitrateBox->addItem(QStringLiteral("128000"), QVariant(128000)); */ connect(m_audioRecorder, &QAudioRecorder::durationChanged, this, &AudioRecorder::updateProgress); connect(m_audioRecorder, &QAudioRecorder::durationChanged, this, &AudioRecorder::updateProgress); connect(m_audioRecorder, &QAudioRecorder::statusChanged, this, &AudioRecorder::updateStatus); connect(m_audioRecorder, &QAudioRecorder::stateChanged, this, &AudioRecorder::onStateChanged); connect(m_audioRecorder, QOverload<QMediaRecorder::Error>::of(&QAudioRecorder::error), this, &AudioRecorder::displayErrorMessage); } AudioRecorder::~AudioRecorder() { delete ui; delete m_mainWindow; } void AudioRecorder::updateProgress(qint64 duration) { if (m_audioRecorder->error() != QMediaRecorder::NoError || duration < 2000) return; //ui->statusbar->showMessage(tr("Recorded %1 sec").arg(duration / 1000)); m_mainWindow->statusbar().showMessage(tr("Recorded %1 sec").arg(duration / 1000)); } void AudioRecorder::updateStatus(QMediaRecorder::Status status) { QString statusMessage; switch (status) { case QMediaRecorder::RecordingStatus: statusMessage = tr("Recording to %1").arg(m_audioRecorder->actualLocation().toString()); break; case QMediaRecorder::PausedStatus: clearAudioLevels(); statusMessage = tr("Paused"); break; case QMediaRecorder::UnloadedStatus: case QMediaRecorder::LoadedStatus: clearAudioLevels(); statusMessage = tr("Stopped"); default: break; } if (m_audioRecorder->error() == QMediaRecorder::NoError) //ui->statusbar->showMessage(statusMessage); m_mainWindow->statusbar().showMessage(statusMessage); } void AudioRecorder::onStateChanged(QMediaRecorder::State state) { switch (state) { case QMediaRecorder::RecordingState: //ui->recordButton->setText(tr("Stop")); //ui->pauseButton->setText(tr("Pause")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Pause")); break; case QMediaRecorder::PausedState: //ui->recordButton->setText(tr("Stop")); //ui->pauseButton->setText(tr("Resume")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Resume")); break; case QMediaRecorder::StoppedState: //ui->recordButton->setText(tr("Record")); //ui->pauseButton->setText(tr("Pause")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Pause")); break; } //ui->pauseButton->setEnabled(m_audioRecorder->state() != QMediaRecorder::StoppedState); m_mainWindow->pauseButton().setEnabled(m_audioRecorder->state() != QMediaRecorder::StoppedState); } static QVariant boxValue(const QComboBox *box) { int idx = box->currentIndex(); if (idx == -1) return QVariant(); return box->itemData(idx); } void AudioRecorder::toggleRecord() { if (m_audioRecorder->state() == QMediaRecorder::StoppedState) { m_audioRecorder->setAudioInput(boxValue(ui->audioDeviceBox).toString()); QAudioEncoderSettings settings; settings.setCodec(boxValue(ui->audioCodecBox).toString()); settings.setSampleRate(boxValue(ui->sampleRateBox).toInt()); //settings.setBitRate(boxValue(ui->bitrateBox).toInt()); settings.setBitRate(boxValue(m_mainWindow->bitrateBox()).toInt()); settings.setChannelCount(boxValue(ui->channelsBox).toInt()); //settings.setQuality(QMultimedia::EncodingQuality(ui->qualitySlider->value())); settings.setQuality(QMultimedia::EncodingQuality(m_mainWindow->qualitySlider().value())); //settings.setEncodingMode(ui->constantQualityRadioButton->isChecked() ? // QMultimedia::ConstantQualityEncoding : // QMultimedia::ConstantBitRateEncoding); settings.setEncodingMode(m_mainWindow->constantQualityRadioButton().isChecked() ? QMultimedia::ConstantQualityEncoding : QMultimedia::ConstantBitRateEncoding); QString container = boxValue(ui->containerBox).toString(); m_audioRecorder->setEncodingSettings(settings, QVideoEncoderSettings(), container); m_audioRecorder->record(); } else { m_audioRecorder->stop(); } } void AudioRecorder::togglePause() { if (m_audioRecorder->state() != QMediaRecorder::PausedState) m_audioRecorder->pause(); else m_audioRecorder->record(); } void AudioRecorder::setOutputLocation() { #ifdef Q_OS_WINRT // UWP does not allow to store outside the sandbox const QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); if (!QDir().mkpath(cacheDir)) { qWarning() << "Failed to create cache directory"; return; } QString fileName = cacheDir + QLatin1String("/output.wav"); #else QString fileName = QFileDialog::getSaveFileName(); #endif m_audioRecorder->setOutputLocation(QUrl::fromLocalFile(fileName)); m_outputLocationSet = true; } void AudioRecorder::displayErrorMessage() { //ui->statusbar->showMessage(m_audioRecorder->errorString()); m_mainWindow->statusbar().showMessage(m_audioRecorder->errorString()); } void AudioRecorder::clearAudioLevels() { for (int i = 0; i < m_audioLevels.size(); ++i) m_audioLevels.at(i)->setLevel(0); } // This function returns the maximum possible sample value for a given audio format qreal getPeakValue(const QAudioFormat& format) { // Note: Only the most common sample formats are supported if (!format.isValid()) return qreal(0); if (format.codec() != "audio/pcm") return qreal(0); switch (format.sampleType()) { case QAudioFormat::Unknown: break; case QAudioFormat::Float: if (format.sampleSize() != 32) // other sample formats are not supported return qreal(0); return qreal(1.00003); case QAudioFormat::SignedInt: if (format.sampleSize() == 32) return qreal(INT_MAX); if (format.sampleSize() == 16) return qreal(SHRT_MAX); if (format.sampleSize() == 8) return qreal(CHAR_MAX); break; case QAudioFormat::UnSignedInt: if (format.sampleSize() == 32) return qreal(UINT_MAX); if (format.sampleSize() == 16) return qreal(USHRT_MAX); if (format.sampleSize() == 8) return qreal(UCHAR_MAX); break; } return qreal(0); } // returns the audio level for each channel QVector<qreal> getBufferLevels(const QAudioBuffer& buffer) { QVector<qreal> values; if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian) return values; if (buffer.format().codec() != "audio/pcm") return values; int channelCount = buffer.format().channelCount(); values.fill(0, channelCount); qreal peak_value = getPeakValue(buffer.format()); if (qFuzzyCompare(peak_value, qreal(0))) return values; switch (buffer.format().sampleType()) { case QAudioFormat::Unknown: case QAudioFormat::UnSignedInt: if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData<quint32>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData<quint16>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData<quint8>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] = qAbs(values.at(i) - peak_value / 2) / (peak_value / 2); break; case QAudioFormat::Float: if (buffer.format().sampleSize() == 32) { values = getBufferLevels(buffer.constData<float>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] /= peak_value; } break; case QAudioFormat::SignedInt: if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData<qint32>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData<qint16>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData<qint8>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] /= peak_value; break; } return values; } template <class T> QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels) { QVector<qreal> max_values; max_values.fill(0, channels); for (int i = 0; i < frames; ++i) { for (int j = 0; j < channels; ++j) { qreal value = qAbs(qreal(buffer[i * channels + j])); if (value > max_values.at(j)) max_values.replace(j, value); } } return max_values; } void AudioRecorder::processBuffer(const QAudioBuffer& buffer) { if (m_audioLevels.count() != buffer.format().channelCount()) { qDeleteAll(m_audioLevels); m_audioLevels.clear(); for (int i = 0; i < buffer.format().channelCount(); ++i) { AudioLevel *level = new AudioLevel(ui->centralwidget); m_audioLevels.append(level); //ui->levelsLayout->addWidget(level); m_mainWindow->levelsLayout().addWidget(level); } } QVector<qreal> levels = getBufferLevels(buffer); for (int i = 0; i < levels.count(); ++i) m_audioLevels.at(i)->setLevel(levels.at(i)); }
-
Ein Problem ist, dass sich
audiorecorder.h
undmainwindow.h
gegenseitig inkludieren.
Beim Kompilieren greift der include guard am Beginn der Dateien und der Rest steht in der Fehlermeldung.Das Problem lässt sich lösen, indem in
mainwindow.h
das#include <audiorecorder.h>
durch eine forward-declaration ersetzt wird:class AudioRecorder;
Das entfernte include statement muss dann inmainwindow.cpp
am Anfang eingefügt werden.Da die Datei
audiolevel.h
nicht gepostet ist, lässt sich nicht beurteilen, ob sie weitere Probleme enthält und ggfs. den o.g. Lösungsweg zunichte macht. -
Das lässt hoffen. Ich habe nämlich komplett den Faden verloren...
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QUrl> #include <QDir> #include <QFileDialog> #include <QStandardPaths> #include <QObject> #include <QMultimedia> #include <QAudioRecorder> #include <QMediaRecorder> #include <QSlider> #include <QRadioButton> #include "audiodevicesbase.h" #include "audiorecorder.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); public slots: QStatusBar statusbar(); QPushButton recordButton(); QPushButton pauseButton(); QComboBox *bitrateBox(); QVBoxLayout levelsLayout(); QSlider qualitySlider(); QRadioButton constantQualityRadioButton(); private slots: void on_pushButton_clicked(); void on_actionAufnahmeparameter_triggered(); private: Ui::MainWindow *ui; AudioRecorder *m_audioRecorder = nullptr; void clearAudioLevels(); }; #endif // MAINWINDOW_H
mainwoindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); m_audioRecorder = new QAudioRecorder(AudioRecorder); //connect(ui->constantQualityRadioButton, &QRadioButton::toggled, this, &MainWindow::setEnabled); //connect(ui->constantBitrateRadioButton, &QRadioButton::toggled, this, &MainWindow::setEnabled); //Q_ASSUME(connect(ui->actionSpeichern, &QMenuBar::triggered, audioRecorder, &AudioRecorder::setOutputLocation)); Q_ASSUME(connect(ui->outputButton, &QPushButton::clicked, m_audioRecorder, &AudioRecorder::setOutputLocation)); Q_ASSUME(connect(ui->recordButton, &QPushButton::clicked, m_audioRecorder, &AudioRecorder::toggleRecord)); Q_ASSUME(connect(ui->pauseButton, &QPushButton::clicked, m_audioRecorder, &AudioRecorder::togglePause)); //quality ui->qualitySlider->setRange(0, int(QMultimedia::VeryHighQuality)); ui->qualitySlider->setValue(int(QMultimedia::NormalQuality)); //bitrates: ui->bitrateBox->addItem(tr("Default"), QVariant(0)); ui->bitrateBox->addItem(QStringLiteral("32000"), QVariant(32000)); ui->bitrateBox->addItem(QStringLiteral("64000"), QVariant(64000)); ui->bitrateBox->addItem(QStringLiteral("96000"), QVariant(96000)); ui->bitrateBox->addItem(QStringLiteral("128000"), QVariant(128000)); } MainWindow::~MainWindow() { delete ui; delete m_audioRecorder; }
audiorecorder.h
#ifndef AUDIORECORDER_H #define AUDIORECORDER_H #include <QUrl> #include <QAudioProbe> #include <QAudioRecorder> #include <QMediaRecorder> #include <QDir> #include <QFileDialog> #include <QStandardPaths> #include <QDialog> #include "mainwindow.h" QT_BEGIN_NAMESPACE namespace Ui { class AudioRecorder; } class QAudioRecorder; class QAudioProbe; class QAudioBuffer; QT_END_NAMESPACE class AudioLevel; class AudioRecorder : public QDialog { Q_OBJECT public: explicit AudioRecorder(QWidget *parent = nullptr); ~AudioRecorder(); public slots: void processBuffer(const QAudioBuffer&); void setOutputLocation(); void togglePause(); void toggleRecord(); void updateStatus(QMediaRecorder::Status); void onStateChanged(QMediaRecorder::State); void updateProgress(qint64 pos); void displayErrorMessage(); private slots: private: QAudioRecorder *m_audioRecorder = nullptr; bool m_outputLocationSet = false; QAudioProbe *m_probe = nullptr; QList<AudioLevel*> m_audioLevels; void clearAudioLevels(); MainWindow *m_mainWindow = nullptr; Ui::AudioRecorder *ui = nullptr; }; #endif // AUDIORECORDER_H
audiorecorder.cpp
#include "audiorecorder.h" #include "audiolevel.h" #include "ui_audiorecorder.h" #include "mainwindow.h" #include "ui_mainwindow.h" static qreal getPeakValue(const QAudioFormat &format); static QVector<qreal> getBufferLevels(const QAudioBuffer &buffer); template <class T> static QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels); AudioRecorder::AudioRecorder(QWidget *parent) : QDialog(parent), ui(new Ui::AudioRecorder) { ui->setupUi(this); m_audioRecorder = new QAudioRecorder(this); m_probe = new QAudioProbe(this); connect(m_probe, &QAudioProbe::audioBufferProbed, this, &AudioRecorder::processBuffer); m_probe->setSource(m_audioRecorder); m_mainWindow = new QMainWindow(MainWindow); //audio devices ui->audioDeviceBox->addItem(tr("Default"), QVariant(QString())); for (auto &device: m_audioRecorder->audioInputs()) { ui->audioDeviceBox->addItem(device, QVariant(device)); } //audio codecs ui->audioCodecBox->addItem(tr("Default"), QVariant(QString())); for (auto &codecName: m_audioRecorder->supportedAudioCodecs()) { ui->audioCodecBox->addItem(codecName, QVariant(codecName)); } //containers ui->containerBox->addItem(tr("Default"), QVariant(QString())); for (auto &containerName: m_audioRecorder->supportedContainers()) { ui->containerBox->addItem(containerName, QVariant(containerName)); } //sample rate ui->sampleRateBox->addItem(tr("Default"), QVariant(0)); for (int sampleRate: m_audioRecorder->supportedAudioSampleRates()) { ui->sampleRateBox->addItem(QString::number(sampleRate), QVariant( sampleRate)); } //channels ui->channelsBox->addItem(tr("Default"), QVariant(-1)); ui->channelsBox->addItem(QStringLiteral("1"), QVariant(1)); ui->channelsBox->addItem(QStringLiteral("2"), QVariant(2)); ui->channelsBox->addItem(QStringLiteral("4"), QVariant(4)); /* //quality ui->qualitySlider->setRange(0, int(QMultimedia::VeryHighQuality)); ui->qualitySlider->setValue(int(QMultimedia::NormalQuality)); //bitrates: ui->bitrateBox->addItem(tr("Default"), QVariant(0)); ui->bitrateBox->addItem(QStringLiteral("32000"), QVariant(32000)); ui->bitrateBox->addItem(QStringLiteral("64000"), QVariant(64000)); ui->bitrateBox->addItem(QStringLiteral("96000"), QVariant(96000)); ui->bitrateBox->addItem(QStringLiteral("128000"), QVariant(128000)); */ connect(m_audioRecorder, &QAudioRecorder::durationChanged, this, &AudioRecorder::updateProgress); connect(m_audioRecorder, &QAudioRecorder::durationChanged, this, &AudioRecorder::updateProgress); connect(m_audioRecorder, &QAudioRecorder::statusChanged, this, &AudioRecorder::updateStatus); connect(m_audioRecorder, &QAudioRecorder::stateChanged, this, &AudioRecorder::onStateChanged); connect(m_audioRecorder, QOverload<QMediaRecorder::Error>::of(&QAudioRecorder::error), this, &AudioRecorder::displayErrorMessage); } AudioRecorder::~AudioRecorder() { delete ui; delete m_mainWindow; } void AudioRecorder::updateProgress(qint64 duration) { if (m_audioRecorder->error() != QMediaRecorder::NoError || duration < 2000) return; //ui->statusbar->showMessage(tr("Recorded %1 sec").arg(duration / 1000)); m_mainWindow->statusbar().showMessage(tr("Recorded %1 sec").arg(duration / 1000)); } void AudioRecorder::updateStatus(QMediaRecorder::Status status) { QString statusMessage; switch (status) { case QMediaRecorder::RecordingStatus: statusMessage = tr("Recording to %1").arg(m_audioRecorder->actualLocation().toString()); break; case QMediaRecorder::PausedStatus: clearAudioLevels(); statusMessage = tr("Paused"); break; case QMediaRecorder::UnloadedStatus: case QMediaRecorder::LoadedStatus: clearAudioLevels(); statusMessage = tr("Stopped"); default: break; } if (m_audioRecorder->error() == QMediaRecorder::NoError) //ui->statusbar->showMessage(statusMessage); m_mainWindow->statusbar().showMessage(statusMessage); } void AudioRecorder::onStateChanged(QMediaRecorder::State state) { switch (state) { case QMediaRecorder::RecordingState: //ui->recordButton->setText(tr("Stop")); //ui->pauseButton->setText(tr("Pause")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Pause")); break; case QMediaRecorder::PausedState: //ui->recordButton->setText(tr("Stop")); //ui->pauseButton->setText(tr("Resume")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Resume")); break; case QMediaRecorder::StoppedState: //ui->recordButton->setText(tr("Record")); //ui->pauseButton->setText(tr("Pause")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Pause")); break; } //ui->pauseButton->setEnabled(m_audioRecorder->state() != QMediaRecorder::StoppedState); m_mainWindow->pauseButton().setEnabled(m_audioRecorder->state() != QMediaRecorder::StoppedState); } static QVariant boxValue(const QComboBox *box) { int idx = box->currentIndex(); if (idx == -1) return QVariant(); return box->itemData(idx); } void AudioRecorder::toggleRecord() { if (m_audioRecorder->state() == QMediaRecorder::StoppedState) { m_audioRecorder->setAudioInput(boxValue(ui->audioDeviceBox).toString()); QAudioEncoderSettings settings; settings.setCodec(boxValue(ui->audioCodecBox).toString()); settings.setSampleRate(boxValue(ui->sampleRateBox).toInt()); //settings.setBitRate(boxValue(ui->bitrateBox).toInt()); settings.setBitRate(boxValue(m_mainWindow->bitrateBox()).toInt()); settings.setChannelCount(boxValue(ui->channelsBox).toInt()); //settings.setQuality(QMultimedia::EncodingQuality(ui->qualitySlider->value())); settings.setQuality(QMultimedia::EncodingQuality(m_mainWindow->qualitySlider().value())); //settings.setEncodingMode(ui->constantQualityRadioButton->isChecked() ? // QMultimedia::ConstantQualityEncoding : // QMultimedia::ConstantBitRateEncoding); settings.setEncodingMode(m_mainWindow->constantQualityRadioButton().isChecked() ? QMultimedia::ConstantQualityEncoding : QMultimedia::ConstantBitRateEncoding); QString container = boxValue(ui->containerBox).toString(); m_audioRecorder->setEncodingSettings(settings, QVideoEncoderSettings(), container); m_audioRecorder->record(); } else { m_audioRecorder->stop(); } } void AudioRecorder::togglePause() { if (m_audioRecorder->state() != QMediaRecorder::PausedState) m_audioRecorder->pause(); else m_audioRecorder->record(); } void AudioRecorder::setOutputLocation() { #ifdef Q_OS_WINRT // UWP does not allow to store outside the sandbox const QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); if (!QDir().mkpath(cacheDir)) { qWarning() << "Failed to create cache directory"; return; } QString fileName = cacheDir + QLatin1String("/output.wav"); #else QString fileName = QFileDialog::getSaveFileName(); #endif m_audioRecorder->setOutputLocation(QUrl::fromLocalFile(fileName)); m_outputLocationSet = true; } void AudioRecorder::displayErrorMessage() { //ui->statusbar->showMessage(m_audioRecorder->errorString()); m_mainWindow->statusbar().showMessage(m_audioRecorder->errorString()); } void AudioRecorder::clearAudioLevels() { for (int i = 0; i < m_audioLevels.size(); ++i) m_audioLevels.at(i)->setLevel(0); } // This function returns the maximum possible sample value for a given audio format qreal getPeakValue(const QAudioFormat& format) { // Note: Only the most common sample formats are supported if (!format.isValid()) return qreal(0); if (format.codec() != "audio/pcm") return qreal(0); switch (format.sampleType()) { case QAudioFormat::Unknown: break; case QAudioFormat::Float: if (format.sampleSize() != 32) // other sample formats are not supported return qreal(0); return qreal(1.00003); case QAudioFormat::SignedInt: if (format.sampleSize() == 32) return qreal(INT_MAX); if (format.sampleSize() == 16) return qreal(SHRT_MAX); if (format.sampleSize() == 8) return qreal(CHAR_MAX); break; case QAudioFormat::UnSignedInt: if (format.sampleSize() == 32) return qreal(UINT_MAX); if (format.sampleSize() == 16) return qreal(USHRT_MAX); if (format.sampleSize() == 8) return qreal(UCHAR_MAX); break; } return qreal(0); } // returns the audio level for each channel QVector<qreal> getBufferLevels(const QAudioBuffer& buffer) { QVector<qreal> values; if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian) return values; if (buffer.format().codec() != "audio/pcm") return values; int channelCount = buffer.format().channelCount(); values.fill(0, channelCount); qreal peak_value = getPeakValue(buffer.format()); if (qFuzzyCompare(peak_value, qreal(0))) return values; switch (buffer.format().sampleType()) { case QAudioFormat::Unknown: case QAudioFormat::UnSignedInt: if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData<quint32>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData<quint16>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData<quint8>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] = qAbs(values.at(i) - peak_value / 2) / (peak_value / 2); break; case QAudioFormat::Float: if (buffer.format().sampleSize() == 32) { values = getBufferLevels(buffer.constData<float>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] /= peak_value; } break; case QAudioFormat::SignedInt: if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData<qint32>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData<qint16>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData<qint8>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] /= peak_value; break; } return values; } template <class T> QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels) { QVector<qreal> max_values; max_values.fill(0, channels); for (int i = 0; i < frames; ++i) { for (int j = 0; j < channels; ++j) { qreal value = qAbs(qreal(buffer[i * channels + j])); if (value > max_values.at(j)) max_values.replace(j, value); } } return max_values; } void AudioRecorder::processBuffer(const QAudioBuffer& buffer) { if (m_audioLevels.count() != buffer.format().channelCount()) { qDeleteAll(m_audioLevels); m_audioLevels.clear(); for (int i = 0; i < buffer.format().channelCount(); ++i) { AudioLevel *level = new AudioLevel(ui->centralwidget); m_audioLevels.append(level); //ui->levelsLayout->addWidget(level); m_mainWindow->levelsLayout().addWidget(level); } } QVector<qreal> levels = getBufferLevels(buffer); for (int i = 0; i < levels.count(); ++i) m_audioLevels.at(i)->setLevel(levels.at(i)); }
@MHage said in Zugriff auf Methoden anderer Klassen:
audiorecorder.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
Zusätzlich: Sie sollten nicht
mainwindow.h
, und sicher nichtui_mainwindow.h
, in jeder Datei außermainwindow.cpp
inkludieren.m_mainWindow->statusbar().showMessage(statusMessage);
Eine Möglichkeit besteht darin, stattdessen ein Signal zu senden.
-
Das lässt hoffen. Ich habe nämlich komplett den Faden verloren...
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QUrl> #include <QDir> #include <QFileDialog> #include <QStandardPaths> #include <QObject> #include <QMultimedia> #include <QAudioRecorder> #include <QMediaRecorder> #include <QSlider> #include <QRadioButton> #include "audiodevicesbase.h" #include "audiorecorder.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); public slots: QStatusBar statusbar(); QPushButton recordButton(); QPushButton pauseButton(); QComboBox *bitrateBox(); QVBoxLayout levelsLayout(); QSlider qualitySlider(); QRadioButton constantQualityRadioButton(); private slots: void on_pushButton_clicked(); void on_actionAufnahmeparameter_triggered(); private: Ui::MainWindow *ui; AudioRecorder *m_audioRecorder = nullptr; void clearAudioLevels(); }; #endif // MAINWINDOW_H
mainwoindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); m_audioRecorder = new QAudioRecorder(AudioRecorder); //connect(ui->constantQualityRadioButton, &QRadioButton::toggled, this, &MainWindow::setEnabled); //connect(ui->constantBitrateRadioButton, &QRadioButton::toggled, this, &MainWindow::setEnabled); //Q_ASSUME(connect(ui->actionSpeichern, &QMenuBar::triggered, audioRecorder, &AudioRecorder::setOutputLocation)); Q_ASSUME(connect(ui->outputButton, &QPushButton::clicked, m_audioRecorder, &AudioRecorder::setOutputLocation)); Q_ASSUME(connect(ui->recordButton, &QPushButton::clicked, m_audioRecorder, &AudioRecorder::toggleRecord)); Q_ASSUME(connect(ui->pauseButton, &QPushButton::clicked, m_audioRecorder, &AudioRecorder::togglePause)); //quality ui->qualitySlider->setRange(0, int(QMultimedia::VeryHighQuality)); ui->qualitySlider->setValue(int(QMultimedia::NormalQuality)); //bitrates: ui->bitrateBox->addItem(tr("Default"), QVariant(0)); ui->bitrateBox->addItem(QStringLiteral("32000"), QVariant(32000)); ui->bitrateBox->addItem(QStringLiteral("64000"), QVariant(64000)); ui->bitrateBox->addItem(QStringLiteral("96000"), QVariant(96000)); ui->bitrateBox->addItem(QStringLiteral("128000"), QVariant(128000)); } MainWindow::~MainWindow() { delete ui; delete m_audioRecorder; }
audiorecorder.h
#ifndef AUDIORECORDER_H #define AUDIORECORDER_H #include <QUrl> #include <QAudioProbe> #include <QAudioRecorder> #include <QMediaRecorder> #include <QDir> #include <QFileDialog> #include <QStandardPaths> #include <QDialog> #include "mainwindow.h" QT_BEGIN_NAMESPACE namespace Ui { class AudioRecorder; } class QAudioRecorder; class QAudioProbe; class QAudioBuffer; QT_END_NAMESPACE class AudioLevel; class AudioRecorder : public QDialog { Q_OBJECT public: explicit AudioRecorder(QWidget *parent = nullptr); ~AudioRecorder(); public slots: void processBuffer(const QAudioBuffer&); void setOutputLocation(); void togglePause(); void toggleRecord(); void updateStatus(QMediaRecorder::Status); void onStateChanged(QMediaRecorder::State); void updateProgress(qint64 pos); void displayErrorMessage(); private slots: private: QAudioRecorder *m_audioRecorder = nullptr; bool m_outputLocationSet = false; QAudioProbe *m_probe = nullptr; QList<AudioLevel*> m_audioLevels; void clearAudioLevels(); MainWindow *m_mainWindow = nullptr; Ui::AudioRecorder *ui = nullptr; }; #endif // AUDIORECORDER_H
audiorecorder.cpp
#include "audiorecorder.h" #include "audiolevel.h" #include "ui_audiorecorder.h" #include "mainwindow.h" #include "ui_mainwindow.h" static qreal getPeakValue(const QAudioFormat &format); static QVector<qreal> getBufferLevels(const QAudioBuffer &buffer); template <class T> static QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels); AudioRecorder::AudioRecorder(QWidget *parent) : QDialog(parent), ui(new Ui::AudioRecorder) { ui->setupUi(this); m_audioRecorder = new QAudioRecorder(this); m_probe = new QAudioProbe(this); connect(m_probe, &QAudioProbe::audioBufferProbed, this, &AudioRecorder::processBuffer); m_probe->setSource(m_audioRecorder); m_mainWindow = new QMainWindow(MainWindow); //audio devices ui->audioDeviceBox->addItem(tr("Default"), QVariant(QString())); for (auto &device: m_audioRecorder->audioInputs()) { ui->audioDeviceBox->addItem(device, QVariant(device)); } //audio codecs ui->audioCodecBox->addItem(tr("Default"), QVariant(QString())); for (auto &codecName: m_audioRecorder->supportedAudioCodecs()) { ui->audioCodecBox->addItem(codecName, QVariant(codecName)); } //containers ui->containerBox->addItem(tr("Default"), QVariant(QString())); for (auto &containerName: m_audioRecorder->supportedContainers()) { ui->containerBox->addItem(containerName, QVariant(containerName)); } //sample rate ui->sampleRateBox->addItem(tr("Default"), QVariant(0)); for (int sampleRate: m_audioRecorder->supportedAudioSampleRates()) { ui->sampleRateBox->addItem(QString::number(sampleRate), QVariant( sampleRate)); } //channels ui->channelsBox->addItem(tr("Default"), QVariant(-1)); ui->channelsBox->addItem(QStringLiteral("1"), QVariant(1)); ui->channelsBox->addItem(QStringLiteral("2"), QVariant(2)); ui->channelsBox->addItem(QStringLiteral("4"), QVariant(4)); /* //quality ui->qualitySlider->setRange(0, int(QMultimedia::VeryHighQuality)); ui->qualitySlider->setValue(int(QMultimedia::NormalQuality)); //bitrates: ui->bitrateBox->addItem(tr("Default"), QVariant(0)); ui->bitrateBox->addItem(QStringLiteral("32000"), QVariant(32000)); ui->bitrateBox->addItem(QStringLiteral("64000"), QVariant(64000)); ui->bitrateBox->addItem(QStringLiteral("96000"), QVariant(96000)); ui->bitrateBox->addItem(QStringLiteral("128000"), QVariant(128000)); */ connect(m_audioRecorder, &QAudioRecorder::durationChanged, this, &AudioRecorder::updateProgress); connect(m_audioRecorder, &QAudioRecorder::durationChanged, this, &AudioRecorder::updateProgress); connect(m_audioRecorder, &QAudioRecorder::statusChanged, this, &AudioRecorder::updateStatus); connect(m_audioRecorder, &QAudioRecorder::stateChanged, this, &AudioRecorder::onStateChanged); connect(m_audioRecorder, QOverload<QMediaRecorder::Error>::of(&QAudioRecorder::error), this, &AudioRecorder::displayErrorMessage); } AudioRecorder::~AudioRecorder() { delete ui; delete m_mainWindow; } void AudioRecorder::updateProgress(qint64 duration) { if (m_audioRecorder->error() != QMediaRecorder::NoError || duration < 2000) return; //ui->statusbar->showMessage(tr("Recorded %1 sec").arg(duration / 1000)); m_mainWindow->statusbar().showMessage(tr("Recorded %1 sec").arg(duration / 1000)); } void AudioRecorder::updateStatus(QMediaRecorder::Status status) { QString statusMessage; switch (status) { case QMediaRecorder::RecordingStatus: statusMessage = tr("Recording to %1").arg(m_audioRecorder->actualLocation().toString()); break; case QMediaRecorder::PausedStatus: clearAudioLevels(); statusMessage = tr("Paused"); break; case QMediaRecorder::UnloadedStatus: case QMediaRecorder::LoadedStatus: clearAudioLevels(); statusMessage = tr("Stopped"); default: break; } if (m_audioRecorder->error() == QMediaRecorder::NoError) //ui->statusbar->showMessage(statusMessage); m_mainWindow->statusbar().showMessage(statusMessage); } void AudioRecorder::onStateChanged(QMediaRecorder::State state) { switch (state) { case QMediaRecorder::RecordingState: //ui->recordButton->setText(tr("Stop")); //ui->pauseButton->setText(tr("Pause")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Pause")); break; case QMediaRecorder::PausedState: //ui->recordButton->setText(tr("Stop")); //ui->pauseButton->setText(tr("Resume")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Resume")); break; case QMediaRecorder::StoppedState: //ui->recordButton->setText(tr("Record")); //ui->pauseButton->setText(tr("Pause")); m_mainWindow->recordButton().setText(tr("Stop")); m_mainWindow->pauseButton().setText(tr("Pause")); break; } //ui->pauseButton->setEnabled(m_audioRecorder->state() != QMediaRecorder::StoppedState); m_mainWindow->pauseButton().setEnabled(m_audioRecorder->state() != QMediaRecorder::StoppedState); } static QVariant boxValue(const QComboBox *box) { int idx = box->currentIndex(); if (idx == -1) return QVariant(); return box->itemData(idx); } void AudioRecorder::toggleRecord() { if (m_audioRecorder->state() == QMediaRecorder::StoppedState) { m_audioRecorder->setAudioInput(boxValue(ui->audioDeviceBox).toString()); QAudioEncoderSettings settings; settings.setCodec(boxValue(ui->audioCodecBox).toString()); settings.setSampleRate(boxValue(ui->sampleRateBox).toInt()); //settings.setBitRate(boxValue(ui->bitrateBox).toInt()); settings.setBitRate(boxValue(m_mainWindow->bitrateBox()).toInt()); settings.setChannelCount(boxValue(ui->channelsBox).toInt()); //settings.setQuality(QMultimedia::EncodingQuality(ui->qualitySlider->value())); settings.setQuality(QMultimedia::EncodingQuality(m_mainWindow->qualitySlider().value())); //settings.setEncodingMode(ui->constantQualityRadioButton->isChecked() ? // QMultimedia::ConstantQualityEncoding : // QMultimedia::ConstantBitRateEncoding); settings.setEncodingMode(m_mainWindow->constantQualityRadioButton().isChecked() ? QMultimedia::ConstantQualityEncoding : QMultimedia::ConstantBitRateEncoding); QString container = boxValue(ui->containerBox).toString(); m_audioRecorder->setEncodingSettings(settings, QVideoEncoderSettings(), container); m_audioRecorder->record(); } else { m_audioRecorder->stop(); } } void AudioRecorder::togglePause() { if (m_audioRecorder->state() != QMediaRecorder::PausedState) m_audioRecorder->pause(); else m_audioRecorder->record(); } void AudioRecorder::setOutputLocation() { #ifdef Q_OS_WINRT // UWP does not allow to store outside the sandbox const QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); if (!QDir().mkpath(cacheDir)) { qWarning() << "Failed to create cache directory"; return; } QString fileName = cacheDir + QLatin1String("/output.wav"); #else QString fileName = QFileDialog::getSaveFileName(); #endif m_audioRecorder->setOutputLocation(QUrl::fromLocalFile(fileName)); m_outputLocationSet = true; } void AudioRecorder::displayErrorMessage() { //ui->statusbar->showMessage(m_audioRecorder->errorString()); m_mainWindow->statusbar().showMessage(m_audioRecorder->errorString()); } void AudioRecorder::clearAudioLevels() { for (int i = 0; i < m_audioLevels.size(); ++i) m_audioLevels.at(i)->setLevel(0); } // This function returns the maximum possible sample value for a given audio format qreal getPeakValue(const QAudioFormat& format) { // Note: Only the most common sample formats are supported if (!format.isValid()) return qreal(0); if (format.codec() != "audio/pcm") return qreal(0); switch (format.sampleType()) { case QAudioFormat::Unknown: break; case QAudioFormat::Float: if (format.sampleSize() != 32) // other sample formats are not supported return qreal(0); return qreal(1.00003); case QAudioFormat::SignedInt: if (format.sampleSize() == 32) return qreal(INT_MAX); if (format.sampleSize() == 16) return qreal(SHRT_MAX); if (format.sampleSize() == 8) return qreal(CHAR_MAX); break; case QAudioFormat::UnSignedInt: if (format.sampleSize() == 32) return qreal(UINT_MAX); if (format.sampleSize() == 16) return qreal(USHRT_MAX); if (format.sampleSize() == 8) return qreal(UCHAR_MAX); break; } return qreal(0); } // returns the audio level for each channel QVector<qreal> getBufferLevels(const QAudioBuffer& buffer) { QVector<qreal> values; if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian) return values; if (buffer.format().codec() != "audio/pcm") return values; int channelCount = buffer.format().channelCount(); values.fill(0, channelCount); qreal peak_value = getPeakValue(buffer.format()); if (qFuzzyCompare(peak_value, qreal(0))) return values; switch (buffer.format().sampleType()) { case QAudioFormat::Unknown: case QAudioFormat::UnSignedInt: if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData<quint32>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData<quint16>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData<quint8>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] = qAbs(values.at(i) - peak_value / 2) / (peak_value / 2); break; case QAudioFormat::Float: if (buffer.format().sampleSize() == 32) { values = getBufferLevels(buffer.constData<float>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] /= peak_value; } break; case QAudioFormat::SignedInt: if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData<qint32>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData<qint16>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData<qint8>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] /= peak_value; break; } return values; } template <class T> QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels) { QVector<qreal> max_values; max_values.fill(0, channels); for (int i = 0; i < frames; ++i) { for (int j = 0; j < channels; ++j) { qreal value = qAbs(qreal(buffer[i * channels + j])); if (value > max_values.at(j)) max_values.replace(j, value); } } return max_values; } void AudioRecorder::processBuffer(const QAudioBuffer& buffer) { if (m_audioLevels.count() != buffer.format().channelCount()) { qDeleteAll(m_audioLevels); m_audioLevels.clear(); for (int i = 0; i < buffer.format().channelCount(); ++i) { AudioLevel *level = new AudioLevel(ui->centralwidget); m_audioLevels.append(level); //ui->levelsLayout->addWidget(level); m_mainWindow->levelsLayout().addWidget(level); } } QVector<qreal> levels = getBufferLevels(buffer); for (int i = 0; i < levels.count(); ++i) m_audioLevels.at(i)->setLevel(levels.at(i)); }
So wie ich das sehe, gibt es auch gar keinen Grund
m_mainWindow
inAudioRecorder
zu haben. Wie @JonB sagte, für alle Zugriffe auf die UI bzw. dasMainWindow
besser Signale senden und die Änderung in deinerMainWindow
Klasse durchführen.Deine Destruktoren sind nämlich auch nicht ganz so toll :)
MainWindow::~MainWindow() { delete ui; delete m_audioRecorder; }
AudioRecorder::~AudioRecorder() { delete ui; delete m_mainWindow; }
Das sollte, je nach Parent-Child-Struktur, eigentlich immer irgendwann zu einem Crash führen.