summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@digia.com>2013-10-09 11:25:00 +0300
committerMiikka Heikkinen <miikka.heikkinen@digia.com>2013-10-09 13:20:20 +0300
commit45bdd538baae5d41ab772a0430917080503ad89b (patch)
treeea07fd33fad7c98822a221385f6114b354971292
parent5f02a038891db0cfd6a9fa08d7576141485e5d71 (diff)
Improve and document audiolevels example
Proper screenshot still missing Task-number: QTRD-2394 Change-Id: I389655773f0095a32238d3850d40b88135092858 Reviewed-by: Tomi Korpipää <tomi.korpipaa@digia.com>
-rw-r--r--examples/audiolevels/audiolevels.cpp17
-rw-r--r--examples/audiolevels/audiolevels.h4
-rw-r--r--examples/audiolevels/audiolevelsiodevice.cpp68
-rw-r--r--examples/audiolevels/audiolevelsiodevice.h4
-rw-r--r--examples/audiolevels/doc/src/audiolevels.qdoc61
-rw-r--r--examples/audiolevels/main.cpp2
6 files changed, 126 insertions, 30 deletions
diff --git a/examples/audiolevels/audiolevels.cpp b/examples/audiolevels/audiolevels.cpp
index 5171492f..d2038630 100644
--- a/examples/audiolevels/audiolevels.cpp
+++ b/examples/audiolevels/audiolevels.cpp
@@ -31,22 +31,22 @@ QT_DATAVISUALIZATION_USE_NAMESPACE
AudioLevels::AudioLevels(Q3DBars *graph, QObject *parent)
: QObject(parent),
- m_device(0),
m_graph(graph),
+ m_device(0),
m_audioInput(0)
{
// Set up the graph
- QBarDataRow *row = new QBarDataRow;
- m_graph->activeDataProxy()->addRow(row);
- m_graph->setBarThickness(0.04);
- m_graph->setBarSpacing(QSizeF(0.0, 0.0));
+ m_graph->setBarThickness(0.5);
+ m_graph->setBarSpacing(QSizeF(0.0, 1.0));
m_graph->setGridVisible(false);
m_graph->setBackgroundVisible(false);
- m_graph->valueAxis()->setRange(0.0, 1.0);
+ m_graph->valueAxis()->setRange(0.0, 2.0);
m_graph->setShadowQuality(QDataVis::ShadowQualityNone);
- m_graph->scene()->activeCamera()->setCameraPosition(-20.0, 10.0, 10);
+ m_graph->scene()->activeCamera()->setCameraPosition(-20.0, 10.0, 20);
m_graph->setTheme(QDataVis::ThemeIsabelle);
+ m_graph->setBarType(QDataVis::MeshStyleBars);
+ //! [0]
QAudioFormat formatAudio;
formatAudio.setSampleRate(8000);
formatAudio.setChannelCount(1);
@@ -56,12 +56,13 @@ AudioLevels::AudioLevels(Q3DBars *graph, QObject *parent)
formatAudio.setSampleType(QAudioFormat::UnSignedInt);
QAudioDeviceInfo inputDevices = QAudioDeviceInfo::defaultInputDevice();
- m_audioInput = new QAudioInput(inputDevices,formatAudio, this);
+ m_audioInput = new QAudioInput(inputDevices, formatAudio, this);
m_device = new AudioLevelsIODevice(m_graph->activeDataProxy(), this);
m_device->open(QIODevice::WriteOnly);
m_audioInput->start(m_device);
+ //! [0]
}
AudioLevels::~AudioLevels()
diff --git a/examples/audiolevels/audiolevels.h b/examples/audiolevels/audiolevels.h
index 90141850..db1d8936 100644
--- a/examples/audiolevels/audiolevels.h
+++ b/examples/audiolevels/audiolevels.h
@@ -35,9 +35,11 @@ public:
~AudioLevels();
private:
- AudioLevelsIODevice *m_device;
+ //! [0]
Q3DBars *m_graph;
+ AudioLevelsIODevice *m_device;
QAudioInput *m_audioInput;
+ //! [0]
};
#endif
diff --git a/examples/audiolevels/audiolevelsiodevice.cpp b/examples/audiolevels/audiolevelsiodevice.cpp
index c3a5cfd0..11cba5aa 100644
--- a/examples/audiolevels/audiolevelsiodevice.cpp
+++ b/examples/audiolevels/audiolevelsiodevice.cpp
@@ -21,10 +21,26 @@
QT_DATAVISUALIZATION_USE_NAMESPACE
+//! [1]
+static const int resolution = 8;
+static const int rowSize = 800;
+static const int rowCount = 7; // Must be odd number
+static const int middleRow = rowCount / 2;
+//! [1]
+
AudioLevelsIODevice::AudioLevelsIODevice(QBarDataProxy *proxy, QObject *parent)
: QIODevice(parent),
- m_proxy(proxy)
+ m_proxy(proxy),
+ m_array(new QBarDataArray)
{
+ // We reuse the existing array for maximum performance, so construct the array here
+ //! [0]
+ m_array->reserve(rowCount);
+ for (int i = 0; i < rowCount; i++)
+ m_array->append(new QBarDataRow(rowSize));
+ //! [0]
+
+ qDebug() << "Total of" << (rowSize * rowCount) << "items in the array.";
}
// Implementation required for this pure virtual function
@@ -35,32 +51,52 @@ qint64 AudioLevelsIODevice::readData(char *data, qint64 maxSize)
return -1;
}
+//! [2]
qint64 AudioLevelsIODevice::writeData(const char *data, qint64 maxSize)
{
- static const int resolution = 8;
- static const int maxRowSize = 1000;
-
+ // The amount of new data available.
int newDataSize = maxSize / resolution;
- const QBarDataRow *oldRow = m_proxy->rowAt(0);
-
- int rowSize = qMin((newDataSize + oldRow->size()), maxRowSize);
+ // If we get more data than array size, we need to adjust the start index for new data.
+ int newDataStartIndex = qMax(0, (newDataSize - rowSize));
- QBarDataRow *row = new QBarDataRow(rowSize);
- int bottom = qMax(0, (newDataSize - rowSize));
+ // Move the old data ahead in the rows (only do first half of rows + middle one now).
+ // If the amount of new data was larger than row size, skip copying.
+ if (!newDataStartIndex) {
+ for (int i = 0; i <= middleRow; i++) {
+ QBarDataItem *srcPos = m_array->at(i)->data();
+ QBarDataItem *dstPos = srcPos + newDataSize;
+ memmove(dstPos, srcPos, (rowSize - newDataSize) * sizeof(QBarDataItem));
+ }
+ }
- // Insert data in reverse order, so that newest data is always at the front of the row
+ // Insert data in reverse order, so that newest data is always at the front of the row.
int index = 0;
- for (int i = newDataSize - 1; i >= bottom; i--)
- (*row)[index++].setValue(((quint8)data[resolution * i] - 128) / 2.0);
+ for (int i = newDataSize - 1; i >= newDataStartIndex; i--) {
+ // Add 0.01 to the value to avoid gaps in the graph (i.e. zero height bars).
+ qreal value = qreal(quint8(data[resolution * i]) - 128) / 2.0 + 0.01;
+ (*m_array->at(middleRow))[index].setValue(value);
+ // Insert a fractional value into front half of the rows.
+ for (int j = 1; j <= middleRow; j++) {
+ qreal fractionalValue = value / qreal(j + 1);
+ (*m_array->at(middleRow - j))[index].setValue(fractionalValue);
+ }
+ index++;
+ }
- // Append old data to new row
- for (int i = newDataSize; i < rowSize; i++)
- (*row)[i].setValue(oldRow->at(i - newDataSize).value());
+ // Copy the front half of rows to the back half for symmetry.
+ index = 0;
+ for (int i = rowCount - 1; i > middleRow; i--) {
+ QBarDataItem *srcPos = m_array->at(index++)->data();
+ QBarDataItem *dstPos = m_array->at(i)->data();
+ memcpy(dstPos, srcPos, rowSize * sizeof(QBarDataItem));
+ }
- m_proxy->setRow(0, row);
+ // Reset the proxy array now that data has been updated to trigger a redraw.
+ m_proxy->resetArray(m_array);
return maxSize;
}
+//! [2]
diff --git a/examples/audiolevels/audiolevelsiodevice.h b/examples/audiolevels/audiolevelsiodevice.h
index cc2929f8..8d665fe8 100644
--- a/examples/audiolevels/audiolevelsiodevice.h
+++ b/examples/audiolevels/audiolevelsiodevice.h
@@ -35,8 +35,10 @@ protected:
qint64 writeData(const char *data, qint64 maxSize);
private:
+ //! [0]
QBarDataProxy *m_proxy;
- int m_visibleCount;
+ QBarDataArray *m_array;
+ //! [0]
};
#endif
diff --git a/examples/audiolevels/doc/src/audiolevels.qdoc b/examples/audiolevels/doc/src/audiolevels.qdoc
index 0d8144a6..8737c242 100644
--- a/examples/audiolevels/doc/src/audiolevels.qdoc
+++ b/examples/audiolevels/doc/src/audiolevels.qdoc
@@ -22,9 +22,64 @@
\ingroup qtdatavisualization_examples
\brief Simple application showing real time audio data.
- The audiolevels example shows how feed dynamic data to a graph using Q3DBars.
+ The audiolevels example shows how feed real-time dynamic data to a graph using Q3DBars.
- \image audiolevels-example.png
+ This example reads the audio levels from a microphone and displays those levels
+ in a bar graph. To increase the load for demonstration purposes, and to make the
+ graph little fancier, slightly modified data is used to fill multiple rows.
+
+ The interesting stuff happens in AudioLevels and AudioLevelsIODevice classes, so we
+ concentrate on those and skip explaining the basic Q3DBars functionality - for that see
+ \l{Bars Example}.
+
+ AudioLevelsIODevice subclasses QIODevice and is given as input device for QAudioInput
+ class, so it receives microphone data.
+
+ In the header file for QAudioInput class we declare necessary members:
+
+ \snippet ../examples/audiolevels/audiolevels.h 0
+
+ And initialize the microphone listening in the source:
+
+ \snippet ../examples/audiolevels/audiolevels.cpp 0
+
+ In the header file for AudioLevelsIODevice class we store pointers to the data proxy and
+ also the data array we give to the proxy, because we reuse the same array to keep memory
+ reallocations to the minimum:
+
+ \snippet ../examples/audiolevels/audiolevelsiodevice.h 0
+
+ In the source file we define some static constants to define size of the data array and
+ the middle row index, as well as the resolution of the visualization. You may need to adjust
+ these values to get decent performance in low-end devices:
- TODO
+ \snippet ../examples/audiolevels/audiolevelsiodevice.cpp 1
+
+ The \c resolution constant indicates the sample rate, e.g. value 8 means every eighth
+ byte from audio input is visualized. This is necessary to make the data readable, as it would
+ otherwise make the graph scroll too fast.
+
+ In the AudioLevelsIODevice class constructor we initialize the data array:
+
+ \snippet ../examples/audiolevels/audiolevelsiodevice.cpp 0
+
+ The AudioLevelsIODevice::writeData function is called whenever there is new audio data
+ available to be visualized. There we move the old data along the rows and insert new
+ data in the beginning of the rows:
+
+ \snippet ../examples/audiolevels/audiolevelsiodevice.cpp 2
+
+ We use a couple of techniques here to improve performance. First off, we reuse
+ the existing data array, as this allows us to avoid any extra memory allocations in our
+ application code. This also means the data array dimensions do not change, which further
+ improves efficiency in the bar graph renderer.
+ Secondly, since the rows are QVectors of QBarDataItems, which do not allocate any data that needs
+ deletion, we can utilize memmove and memcpy functions to quickly move and copy data around.
+
+ \note In the future versions of Qt Data Visualization, QBarDataItem might get extended so that
+ it does allocate some memory to store other optional bar properties besides the value.
+ In use cases where those optional properties are used, using memmove and memcpy would lead to
+ memory leaks, so use them with care.
+
+ \image audiolevels-example.png
*/
diff --git a/examples/audiolevels/main.cpp b/examples/audiolevels/main.cpp
index ea8acd6b..9d16610c 100644
--- a/examples/audiolevels/main.cpp
+++ b/examples/audiolevels/main.cpp
@@ -29,7 +29,7 @@ int main(int argc, char *argv[])
Q3DBars window;
window.resize(1024, 768);
- window.setTitle("QtDataVisualization microphone audio levels visualizer");
+ window.setTitle("Qt Data Visualization - Microphone audio levels visualizer");
window.show();
AudioLevels audioLevels(&window);