1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #ifndef WAVEFORM_H |
52 | #define WAVEFORM_H |
53 | |
54 | #include <QAudioFormat> |
55 | #include <QPixmap> |
56 | #include <QScopedPointer> |
57 | #include <QWidget> |
58 | |
59 | /** |
60 | * Widget which displays a section of the audio waveform. |
61 | * |
62 | * The waveform is rendered on a set of QPixmaps which form a group of tiles |
63 | * whose extent covers the widget. As the audio position is updated, these |
64 | * tiles are scrolled from left to right; when the left-most tile scrolls |
65 | * outside the widget, it is moved to the right end of the tile array and |
66 | * painted with the next section of the waveform. |
67 | */ |
68 | class Waveform : public QWidget |
69 | { |
70 | Q_OBJECT |
71 | |
72 | public: |
73 | explicit Waveform(QWidget *parent = 0); |
74 | ~Waveform(); |
75 | |
76 | // QWidget |
77 | void paintEvent(QPaintEvent *event) override; |
78 | void resizeEvent(QResizeEvent *event) override; |
79 | |
80 | void initialize(const QAudioFormat &format, qint64 audioBufferSize, qint64 windowDurationUs); |
81 | void reset(); |
82 | |
83 | void setAutoUpdatePosition(bool enabled); |
84 | |
85 | public slots: |
86 | void bufferChanged(qint64 position, qint64 length, const QByteArray &buffer); |
87 | void audioPositionChanged(qint64 position); |
88 | |
89 | private: |
90 | static const int NullIndex = -1; |
91 | |
92 | void deletePixmaps(); |
93 | |
94 | /* |
95 | * (Re)create all pixmaps, repaint and update the display. |
96 | * Triggers an update(); |
97 | */ |
98 | void createPixmaps(const QSize &newSize); |
99 | |
100 | /* |
101 | * Update window position. |
102 | * Triggers an update(). |
103 | */ |
104 | void setWindowPosition(qint64 position); |
105 | |
106 | /* |
107 | * Base position of tile |
108 | */ |
109 | qint64 tilePosition(int index) const; |
110 | |
111 | /* |
112 | * Structure which identifies a point within a given |
113 | * tile. |
114 | */ |
115 | struct TilePoint |
116 | { |
117 | TilePoint(int idx = 0, qint64 pos = 0, qint64 pix = 0) |
118 | : index(idx), positionOffset(pos), pixelOffset(pix) |
119 | { } |
120 | |
121 | // Index of tile |
122 | int index; |
123 | |
124 | // Number of bytes from start of tile |
125 | qint64 positionOffset; |
126 | |
127 | // Number of pixels from left of corresponding pixmap |
128 | int pixelOffset; |
129 | }; |
130 | |
131 | /* |
132 | * Convert position in m_buffer into a tile index and an offset in pixels |
133 | * into the corresponding pixmap. |
134 | * |
135 | * \param position Offset into m_buffer, in bytes |
136 | |
137 | * If position is outside the tile array, index is NullIndex and |
138 | * offset is zero. |
139 | */ |
140 | TilePoint tilePoint(qint64 position) const; |
141 | |
142 | /* |
143 | * Convert offset in bytes into a tile into an offset in pixels |
144 | * within that tile. |
145 | */ |
146 | int tilePixelOffset(qint64 positionOffset) const; |
147 | |
148 | /* |
149 | * Convert offset in bytes into the window into an offset in pixels |
150 | * within the widget rect(). |
151 | */ |
152 | int windowPixelOffset(qint64 positionOffset) const; |
153 | |
154 | /* |
155 | * Paint all tiles which can be painted. |
156 | * \return true iff update() was called |
157 | */ |
158 | bool paintTiles(); |
159 | |
160 | /* |
161 | * Paint the specified tile |
162 | * |
163 | * \pre Sufficient data is available to completely paint the tile, i.e. |
164 | * m_dataLength is greater than the upper bound of the tile. |
165 | */ |
166 | void paintTile(int index); |
167 | |
168 | /* |
169 | * Move the first n tiles to the end of the array, and mark them as not |
170 | * painted. |
171 | */ |
172 | void shuffleTiles(int n); |
173 | |
174 | /* |
175 | * Reset tile array |
176 | */ |
177 | void resetTiles(qint64 newStartPos); |
178 | |
179 | private: |
180 | qint64 m_bufferPosition; |
181 | qint64 m_bufferLength; |
182 | QByteArray m_buffer; |
183 | |
184 | qint64 m_audioPosition; |
185 | QAudioFormat m_format; |
186 | |
187 | bool m_active; |
188 | |
189 | QSize m_pixmapSize; |
190 | QVector<QPixmap*> m_pixmaps; |
191 | |
192 | struct Tile { |
193 | // Pointer into parent m_pixmaps array |
194 | QPixmap* pixmap; |
195 | |
196 | // Flag indicating whether this tile has been painted |
197 | bool painted; |
198 | }; |
199 | |
200 | QVector<Tile> m_tiles; |
201 | |
202 | // Length of audio data in bytes depicted by each tile |
203 | qint64 m_tileLength; |
204 | |
205 | // Position in bytes of the first tile, relative to m_buffer |
206 | qint64 m_tileArrayStart; |
207 | |
208 | qint64 m_windowPosition; |
209 | qint64 m_windowLength; |
210 | }; |
211 | |
212 | #endif // WAVEFORM_H |
213 | |