summaryrefslogtreecommitdiffstats
path: root/examples/multimedia/spectrum/engine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/multimedia/spectrum/engine.cpp')
-rw-r--r--examples/multimedia/spectrum/engine.cpp329
1 files changed, 151 insertions, 178 deletions
diff --git a/examples/multimedia/spectrum/engine.cpp b/examples/multimedia/spectrum/engine.cpp
index bbbe08a67..cb7aeadcb 100644
--- a/examples/multimedia/spectrum/engine.cpp
+++ b/examples/multimedia/spectrum/engine.cpp
@@ -1,61 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "engine.h"
#include "tonegenerator.h"
#include "utils.h"
-#include <math.h>
-
-#include <QAudioSource>
#include <QAudioSink>
+#include <QAudioSource>
#include <QCoreApplication>
#include <QDebug>
#include <QFile>
@@ -63,48 +14,51 @@
#include <QSet>
#include <QThread>
+#if QT_CONFIG(permissions)
+ #include <QPermission>
+#endif
+
+#include <math.h>
+
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
-const qint64 BufferDurationUs = 10 * 1000000;
+const qint64 BufferDurationUs = 10 * 1000000;
// Size of the level calculation window in microseconds
-const int LevelWindowUs = 0.1 * 1000000;
+const int LevelWindowUs = 0.1 * 1000000;
//-----------------------------------------------------------------------------
// Constructor and destructor
//-----------------------------------------------------------------------------
Engine::Engine(QObject *parent)
- : QObject(parent)
- , m_mode(QAudioDevice::Input)
- , m_state(QAudio::StoppedState)
- , m_devices(new QMediaDevices(this))
- , m_generateTone(false)
- , m_file(nullptr)
- , m_analysisFile(nullptr)
- , m_availableAudioInputDevices(m_devices->audioInputs())
- , m_audioInputDevice(m_devices->defaultAudioInput())
- , m_audioInput(nullptr)
- , m_audioInputIODevice(nullptr)
- , m_recordPosition(0)
- , m_availableAudioOutputDevices(m_devices->audioOutputs())
- , m_audioOutputDevice(m_devices->defaultAudioOutput())
- , m_audioOutput(nullptr)
- , m_playPosition(0)
- , m_bufferPosition(0)
- , m_bufferLength(0)
- , m_dataLength(0)
- , m_levelBufferLength(0)
- , m_rmsLevel(0.0)
- , m_peakLevel(0.0)
- , m_spectrumBufferLength(0)
- , m_spectrumPosition(0)
- , m_count(0)
-{
- connect(&m_spectrumAnalyser, QOverload<const FrequencySpectrum&>::of(&SpectrumAnalyser::spectrumChanged),
- this, QOverload<const FrequencySpectrum&>::of(&Engine::spectrumChanged));
+ : QObject(parent),
+ m_mode(QAudioDevice::Input),
+ m_state(QAudio::StoppedState),
+ m_devices(new QMediaDevices(this)),
+ m_generateTone(false),
+ m_file(nullptr),
+ m_analysisFile(nullptr),
+ m_audioInput(nullptr),
+ m_audioInputIODevice(nullptr),
+ m_recordPosition(0),
+ m_audioOutput(nullptr),
+ m_playPosition(0),
+ m_bufferPosition(0),
+ m_bufferLength(0),
+ m_dataLength(0),
+ m_levelBufferLength(0),
+ m_rmsLevel(0.0),
+ m_peakLevel(0.0),
+ m_spectrumBufferLength(0),
+ m_spectrumPosition(0),
+ m_count(0)
+{
+ connect(&m_spectrumAnalyser,
+ QOverload<const FrequencySpectrum &>::of(&SpectrumAnalyser::spectrumChanged), this,
+ QOverload<const FrequencySpectrum &>::of(&Engine::spectrumChanged));
// This code might misinterpret things like "-something -category". But
// it's unlikely that that needs to be supported so we'll let it go.
@@ -114,6 +68,8 @@ Engine::Engine(QObject *parent)
break;
}
+ initAudioDevices();
+
initialize();
#ifdef DUMP_DATA
@@ -127,7 +83,6 @@ Engine::Engine(QObject *parent)
m_notifyTimer = new QTimer(this);
m_notifyTimer->setInterval(1000);
connect(m_notifyTimer, &QTimer::timeout, this, &Engine::audioNotify);
-
}
Engine::~Engine() = default;
@@ -143,7 +98,7 @@ bool Engine::loadFile(const QString &fileName)
Q_ASSERT(!m_generateTone);
Q_ASSERT(!m_file);
Q_ASSERT(!fileName.isEmpty());
- QIODevice* file = new QFile(fileName);
+ QIODevice *file = new QFile(fileName);
if (file->open(QIODevice::ReadOnly)) {
m_file = new QWaveDecoder(file, this);
if (m_file->open(QIODevice::ReadOnly)) {
@@ -151,10 +106,9 @@ bool Engine::loadFile(const QString &fileName)
result = initialize();
} else {
emit errorMessage(tr("Audio format not supported"),
- formatToString(m_file->audioFormat()));
+ formatToString(m_file->audioFormat()));
}
- }
- else
+ } else
emit errorMessage(tr("Could not open WAV decoder for file"), fileName);
} else {
emit errorMessage(tr("Could not open file"), fileName);
@@ -176,9 +130,8 @@ bool Engine::generateTone(const Tone &tone)
m_generateTone = true;
m_tone = tone;
ENGINE_DEBUG << "Engine::generateTone"
- << "startFreq" << m_tone.startFreq
- << "endFreq" << m_tone.endFreq
- << "amp" << m_tone.amplitude;
+ << "startFreq" << m_tone.startFreq << "endFreq" << m_tone.endFreq << "amp"
+ << m_tone.amplitude;
return initialize();
}
@@ -191,8 +144,7 @@ bool Engine::generateSweptTone(qreal amplitude)
m_tone.endFreq = 0;
m_tone.amplitude = amplitude;
ENGINE_DEBUG << "Engine::generateSweptTone"
- << "startFreq" << m_tone.startFreq
- << "amp" << m_tone.amplitude;
+ << "startFreq" << m_tone.startFreq << "amp" << m_tone.amplitude;
return initialize();
}
@@ -217,7 +169,6 @@ void Engine::setWindowFunction(WindowFunction type)
m_spectrumAnalyser.setWindowFunction(type);
}
-
//-----------------------------------------------------------------------------
// Public slots
//-----------------------------------------------------------------------------
@@ -225,26 +176,23 @@ void Engine::setWindowFunction(WindowFunction type)
void Engine::startRecording()
{
if (m_audioInput) {
- if (QAudioDevice::Input == m_mode &&
- QAudio::SuspendedState == m_state) {
+ if (QAudioDevice::Input == m_mode && QAudio::SuspendedState == m_state) {
m_audioInput->resume();
} else {
m_spectrumAnalyser.cancelCalculation();
- spectrumChanged(0, 0, FrequencySpectrum());
+ emit spectrumChanged(0, 0, FrequencySpectrum());
m_buffer.fill(0);
setRecordPosition(0, true);
stopPlayback();
m_mode = QAudioDevice::Input;
- connect(m_audioInput, &QAudioSource::stateChanged,
- this, &Engine::audioStateChanged);
+ connect(m_audioInput, &QAudioSource::stateChanged, this, &Engine::audioStateChanged);
m_count = 0;
m_dataLength = 0;
emit dataLengthChanged(0);
m_audioInputIODevice = m_audioInput->start();
- connect(m_audioInputIODevice, &QIODevice::readyRead,
- this, &Engine::audioDataReady);
+ connect(m_audioInputIODevice, &QIODevice::readyRead, this, &Engine::audioDataReady);
}
m_notifyTimer->start();
}
@@ -256,8 +204,7 @@ void Engine::startPlayback()
initialize();
if (m_audioOutput) {
- if (QAudioDevice::Output == m_mode &&
- QAudio::SuspendedState == m_state) {
+ if (QAudioDevice::Output == m_mode && QAudio::SuspendedState == m_state) {
#ifdef Q_OS_WIN
// The Windows backend seems to internally go back into ActiveState
// while still returning SuspendedState, so to ensure that it doesn't
@@ -267,12 +214,11 @@ void Engine::startPlayback()
m_audioOutput->resume();
} else {
m_spectrumAnalyser.cancelCalculation();
- spectrumChanged(0, 0, FrequencySpectrum());
+ emit spectrumChanged(0, 0, FrequencySpectrum());
setPlayPosition(0, true);
stopRecording();
m_mode = QAudioDevice::Output;
- connect(m_audioOutput, &QAudioSink::stateChanged,
- this, &Engine::audioStateChanged);
+ connect(m_audioOutput, &QAudioSink::stateChanged, this, &Engine::audioStateChanged);
m_count = 0;
if (m_file) {
@@ -293,8 +239,7 @@ void Engine::startPlayback()
void Engine::suspend()
{
- if (QAudio::ActiveState == m_state ||
- QAudio::IdleState == m_state) {
+ if (QAudio::ActiveState == m_state || QAudio::IdleState == m_state) {
switch (m_mode) {
case QAudioDevice::Input:
m_audioInput->suspend();
@@ -325,75 +270,99 @@ void Engine::setAudioOutputDevice(const QAudioDevice &device)
}
}
-
//-----------------------------------------------------------------------------
// Private slots
//-----------------------------------------------------------------------------
+void Engine::initAudioDevices()
+{
+#if QT_CONFIG(permissions)
+ QMicrophonePermission microphonePermission;
+ switch (qApp->checkPermission(microphonePermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(microphonePermission, this, &Engine::initAudioDevices);
+ return;
+ case Qt::PermissionStatus::Denied:
+ qWarning("Microphone permission is not granted!");
+ return;
+ case Qt::PermissionStatus::Granted:
+ break;
+ }
+#endif
+ m_availableAudioInputDevices = m_devices->audioInputs();
+ m_audioInputDevice = m_devices->defaultAudioInput();
+ m_availableAudioOutputDevices = m_devices->audioOutputs();
+ m_audioOutputDevice = m_devices->defaultAudioOutput();
+}
+
void Engine::audioNotify()
{
switch (m_mode) {
case QAudioDevice::Input: {
- const qint64 recordPosition = qMin(m_bufferLength, m_format.bytesForDuration(m_audioInput->processedUSecs()));
- setRecordPosition(recordPosition);
- const qint64 levelPosition = m_dataLength - m_levelBufferLength;
- if (levelPosition >= 0)
- calculateLevel(levelPosition, m_levelBufferLength);
- if (m_dataLength >= m_spectrumBufferLength) {
- const qint64 spectrumPosition = m_dataLength - m_spectrumBufferLength;
- calculateSpectrum(spectrumPosition);
- }
- emit bufferChanged(0, m_dataLength, m_buffer);
+ const qint64 recordPosition =
+ qMin(m_bufferLength, m_format.bytesForDuration(m_audioInput->processedUSecs()));
+ setRecordPosition(recordPosition);
+ const qint64 levelPosition = m_dataLength - m_levelBufferLength;
+ if (levelPosition >= 0)
+ calculateLevel(levelPosition, m_levelBufferLength);
+ if (m_dataLength >= m_spectrumBufferLength) {
+ const qint64 spectrumPosition = m_dataLength - m_spectrumBufferLength;
+ calculateSpectrum(spectrumPosition);
}
- break;
+ emit bufferChanged(0, m_dataLength, m_buffer);
+ } break;
case QAudioDevice::Output: {
- const qint64 playPosition = m_format.bytesForDuration(m_audioOutput->processedUSecs());
- setPlayPosition(qMin(bufferLength(), playPosition));
- const qint64 levelPosition = playPosition - m_levelBufferLength;
- const qint64 spectrumPosition = playPosition - m_spectrumBufferLength;
- if (m_file) {
- if (levelPosition > m_bufferPosition ||
- spectrumPosition > m_bufferPosition ||
- qMax(m_levelBufferLength, m_spectrumBufferLength) > m_dataLength) {
- m_bufferPosition = 0;
- m_dataLength = 0;
- // Data needs to be read into m_buffer in order to be analysed
- const qint64 readPos = qMax(qint64(0), qMin(levelPosition, spectrumPosition));
- const qint64 readEnd = qMin(m_analysisFile->getDevice()->size(), qMax(levelPosition + m_levelBufferLength, spectrumPosition + m_spectrumBufferLength));
- const qint64 readLen = readEnd - readPos + m_format.bytesForDuration(WaveformWindowDuration);
- qDebug() << "Engine::audioNotify [1]"
- << "analysisFileSize" << m_analysisFile->getDevice()->size()
- << "readPos" << readPos
- << "readLen" << readLen;
- if (m_analysisFile->seek(readPos + m_analysisFile->headerLength())) {
- m_buffer.resize(readLen);
- m_bufferPosition = readPos;
- m_dataLength = m_analysisFile->read(m_buffer.data(), readLen);
- qDebug() << "Engine::audioNotify [2]" << "bufferPosition" << m_bufferPosition << "dataLength" << m_dataLength;
- } else {
- qDebug() << "Engine::audioNotify [2]" << "file seek error";
- }
- emit bufferChanged(m_bufferPosition, m_dataLength, m_buffer);
+ const qint64 playPosition = m_format.bytesForDuration(m_audioOutput->processedUSecs());
+ setPlayPosition(qMin(bufferLength(), playPosition));
+ const qint64 levelPosition = playPosition - m_levelBufferLength;
+ const qint64 spectrumPosition = playPosition - m_spectrumBufferLength;
+ if (m_file) {
+ if (levelPosition > m_bufferPosition || spectrumPosition > m_bufferPosition
+ || qMax(m_levelBufferLength, m_spectrumBufferLength) > m_dataLength) {
+ m_bufferPosition = 0;
+ m_dataLength = 0;
+ // Data needs to be read into m_buffer in order to be analysed
+ const qint64 readPos = qMax(qint64(0), qMin(levelPosition, spectrumPosition));
+ const qint64 readEnd = qMin(m_analysisFile->getDevice()->size(),
+ qMax(levelPosition + m_levelBufferLength,
+ spectrumPosition + m_spectrumBufferLength));
+ const qint64 readLen =
+ readEnd - readPos + m_format.bytesForDuration(WaveformWindowDuration);
+ qDebug() << "Engine::audioNotify [1]"
+ << "analysisFileSize" << m_analysisFile->getDevice()->size() << "readPos"
+ << readPos << "readLen" << readLen;
+ if (m_analysisFile->seek(readPos + m_analysisFile->headerLength())) {
+ m_buffer.resize(readLen);
+ m_bufferPosition = readPos;
+ m_dataLength = m_analysisFile->read(m_buffer.data(), readLen);
+ qDebug() << "Engine::audioNotify [2]"
+ << "bufferPosition" << m_bufferPosition << "dataLength"
+ << m_dataLength;
+ } else {
+ qDebug() << "Engine::audioNotify [2]"
+ << "file seek error";
}
- } else {
- if (playPosition >= m_dataLength)
- stopPlayback();
+ emit bufferChanged(m_bufferPosition, m_dataLength, m_buffer);
}
- if (levelPosition >= 0 && levelPosition + m_levelBufferLength < m_bufferPosition + m_dataLength)
- calculateLevel(levelPosition, m_levelBufferLength);
- if (spectrumPosition >= 0 && spectrumPosition + m_spectrumBufferLength < m_bufferPosition + m_dataLength)
- calculateSpectrum(spectrumPosition);
+ } else {
+ if (playPosition >= m_dataLength)
+ stopPlayback();
}
+ if (levelPosition >= 0
+ && levelPosition + m_levelBufferLength < m_bufferPosition + m_dataLength)
+ calculateLevel(levelPosition, m_levelBufferLength);
+ if (spectrumPosition >= 0
+ && spectrumPosition + m_spectrumBufferLength < m_bufferPosition + m_dataLength)
+ calculateSpectrum(spectrumPosition);
+ } break;
+ default:
break;
- default:
- break;
}
}
void Engine::audioStateChanged(QAudio::State state)
{
- ENGINE_DEBUG << "Engine::audioStateChanged from" << m_state
- << "to" << state;
+ ENGINE_DEBUG << "Engine::audioStateChanged from" << m_state << "to" << state;
if (QAudio::IdleState == state && m_file && m_file->pos() == m_file->getDevice()->size()) {
stopPlayback();
@@ -428,9 +397,8 @@ void Engine::audioDataReady()
const qint64 bytesSpace = m_buffer.size() - m_dataLength;
const qint64 bytesToRead = qMin(bytesReady, bytesSpace);
- const qint64 bytesRead = m_audioInputIODevice->read(
- m_buffer.data() + m_dataLength,
- bytesToRead);
+ const qint64 bytesRead =
+ m_audioInputIODevice->read(m_buffer.data() + m_dataLength, bytesToRead);
if (bytesRead) {
m_dataLength += bytesRead;
@@ -443,11 +411,11 @@ void Engine::audioDataReady()
void Engine::spectrumChanged(const FrequencySpectrum &spectrum)
{
- ENGINE_DEBUG << "Engine::spectrumChanged" << "pos" << m_spectrumPosition;
+ ENGINE_DEBUG << "Engine::spectrumChanged"
+ << "pos" << m_spectrumPosition;
emit spectrumChanged(m_spectrumPosition, m_spectrumBufferLength, spectrum);
}
-
//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------
@@ -526,17 +494,19 @@ bool Engine::initialize()
}
} else {
if (m_file)
- emit errorMessage(tr("Audio format not supported"),
- formatToString(m_format));
+ emit errorMessage(tr("Audio format not supported"), formatToString(m_format));
else if (m_generateTone)
emit errorMessage(tr("No suitable format found"), "");
else
emit errorMessage(tr("No common input / output format found"), "");
}
- ENGINE_DEBUG << "Engine::initialize" << "m_bufferLength" << m_bufferLength;
- ENGINE_DEBUG << "Engine::initialize" << "m_dataLength" << m_dataLength;
- ENGINE_DEBUG << "Engine::initialize" << "format" << m_format;
+ ENGINE_DEBUG << "Engine::initialize"
+ << "m_bufferLength" << m_bufferLength;
+ ENGINE_DEBUG << "Engine::initialize"
+ << "m_dataLength" << m_dataLength;
+ ENGINE_DEBUG << "Engine::initialize"
+ << "format" << m_format;
return result;
}
@@ -655,7 +625,7 @@ void Engine::calculateLevel(qint64 position, qint64 length)
const char *ptr = m_buffer.constData() + position - m_bufferPosition;
const char *const end = ptr + length;
while (ptr < end) {
- const qint16 value = *reinterpret_cast<const qint16*>(ptr);
+ const qint16 value = *reinterpret_cast<const qint16 *>(ptr);
const qreal fracValue = pcmToReal(value);
peakLevel = qMax(peakLevel, fracValue);
sum += fracValue * fracValue;
@@ -668,8 +638,9 @@ void Engine::calculateLevel(qint64 position, qint64 length)
rmsLevel = qMin(qreal(1.0), rmsLevel);
setLevel(rmsLevel, peakLevel, numSamples);
- ENGINE_DEBUG << "Engine::calculateLevel" << "pos" << position << "len" << length
- << "rms" << rmsLevel << "peak" << peakLevel;
+ ENGINE_DEBUG << "Engine::calculateLevel"
+ << "pos" << position << "len" << length << "rms" << rmsLevel << "peak"
+ << peakLevel;
#endif
}
@@ -683,13 +654,13 @@ void Engine::calculateSpectrum(qint64 position)
// QThread::currentThread is marked 'for internal use only', but
// we're only using it for debug output here, so it's probably OK :)
- ENGINE_DEBUG << "Engine::calculateSpectrum" << QThread::currentThread()
- << "count" << m_count << "pos" << position << "len" << m_spectrumBufferLength
+ ENGINE_DEBUG << "Engine::calculateSpectrum" << QThread::currentThread() << "count" << m_count
+ << "pos" << position << "len" << m_spectrumBufferLength
<< "spectrumAnalyser.isReady" << m_spectrumAnalyser.isReady();
if (m_spectrumAnalyser.isReady()) {
- m_spectrumBuffer = QByteArray::fromRawData(m_buffer.constData() + position - m_bufferPosition,
- m_spectrumBufferLength);
+ m_spectrumBuffer = QByteArray::fromRawData(
+ m_buffer.constData() + position - m_bufferPosition, m_spectrumBufferLength);
m_spectrumPosition = position;
m_spectrumAnalyser.calculate(m_spectrumBuffer, m_format);
}
@@ -728,11 +699,11 @@ void Engine::emitError(QAudio::Error error)
break;
case QAudio::UnderrunError:
errorString = tr("UnderrunError: Audio data is not being fed"
- "to the audio device at a fast enough rate.");
+ "to the audio device at a fast enough rate.");
break;
case QAudio::FatalError:
errorString = tr("FatalError: A non-recoverable error has occurred,"
- "the audio device is not usable at this time.");
+ "the audio device is not usable at this time.");
break;
}
@@ -762,9 +733,9 @@ void Engine::dumpData()
QFile txtFile(txtFileName);
txtFile.open(QFile::WriteOnly | QFile::Text);
QTextStream stream(&txtFile);
- const qint16 *ptr = reinterpret_cast<const qint16*>(m_buffer.constData());
+ const qint16 *ptr = reinterpret_cast<const qint16 *>(m_buffer.constData());
const int numSamples = m_dataLength / (2 * m_format.channels());
- for (int i=0; i<numSamples; ++i) {
+ for (int i = 0; i < numSamples; ++i) {
stream << i << "\t" << *ptr << "\n";
ptr += m_format.channels();
}
@@ -775,3 +746,5 @@ void Engine::dumpData()
pcmFile.write(m_buffer.constData(), m_dataLength);
}
#endif // DUMP_AUDIO
+
+#include "moc_engine.cpp"