Overte C++ Documentation
AudioIOStats.h
1 //
2 // AudioIOStats.h
3 // interface/src/audio
4 //
5 // Created by Stephen Birarda on 2014-12-16.
6 // Copyright 2014 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 
12 #ifndef hifi_AudioIOStats_h
13 #define hifi_AudioIOStats_h
14 
15 #include "MovingMinMaxAvg.h"
16 
17 #include <QObject>
18 #include <QtCore/QSharedPointer>
19 
20 #include <AudioStreamStats.h>
21 #include <Node.h>
22 #include <NLPacket.h>
23 
24 class MixedProcessedAudioStream;
25 
26 #define AUDIO_PROPERTY(TYPE, NAME) \
27  Q_PROPERTY(TYPE NAME READ NAME NOTIFY NAME##Changed) \
28  public: \
29  TYPE NAME() const { return _##NAME; } \
30  void NAME(TYPE value) { \
31  if (_##NAME != value) { \
32  _##NAME = value; \
33  emit NAME##Changed(value); \
34  } \
35  } \
36  Q_SIGNAL void NAME##Changed(TYPE value); \
37  private: \
38  TYPE _##NAME{ (TYPE)0 };
39 
40 class AudioStreamStatsInterface : public QObject {
41  Q_OBJECT
42 
43  /*@jsdoc
44  * Statistics for an audio stream.
45  *
46  * <p>Provided in properties of the {@link AudioStats} API.</p>
47  *
48  * @class AudioStats.AudioStreamStats
49  * @hideconstructor
50  *
51  * @hifi-interface
52  * @hifi-client-entity
53  * @hifi-avatar
54  *
55  * @property {number} dropCount - The number of silent or old audio frames dropped.
56  * <em>Read-only.</em>
57  * @property {number} framesAvailable - The number of audio frames containing data available.
58  * <em>Read-only.</em>
59  * @property {number} framesAvailableAvg - The time-weighted average of audio frames containing data available.
60  * <em>Read-only.</em>
61  * @property {number} framesDesired - The desired number of audio frames for the jitter buffer.
62  * <em>Read-only.</em>
63  * @property {number} lastStarveDurationCount - The most recent number of consecutive times that audio frames have not been
64  * available for processing.
65  * <em>Read-only.</em>
66  * @property {number} lossCount - The total number of audio packets lost.
67  * <em>Read-only.</em>
68  * @property {number} lossCountWindow - The number of audio packets lost since the previous statistic.
69  * <em>Read-only.</em>
70  * @property {number} lossRate - The ratio of the total number of audio packets lost to the total number of audio packets
71  * expected.
72  * <em>Read-only.</em>
73  * @property {number} lossRateWindow - The ratio of the number of audio packets lost to the number of audio packets
74  * expected since the previous statistic.
75  * <em>Read-only.</em>
76  * @property {number} overflowCount - The number of times that the audio ring buffer has overflowed.
77  * <em>Read-only.</em>
78  * @property {number} starveCount - The total number of times that audio frames have not been available for processing.
79  * <em>Read-only.</em>
80  * @property {number} timegapMsAvg - The overall average time between data packets, in ms.
81  * <em>Read-only.</em>
82  * @property {number} timegapMsAvgWindow - The recent average time between data packets, in ms.
83  * <em>Read-only.</em>
84  * @property {number} timegapMsMax - The overall maximum time between data packets, in ms.
85  * <em>Read-only.</em>
86  * @property {number} timegapMsMaxWindow - The recent maximum time between data packets, in ms.
87  * <em>Read-only.</em>
88  * @property {number} unplayedMsMax - The duration of audio waiting to be played, in ms.
89  * <em>Read-only.</em>
90  */
91 
92  /*@jsdoc
93  * Triggered when the ratio of the total number of audio packets lost to the total number of audio packets expected changes.
94  * @function AudioStats.AudioStreamStats.lossRateChanged
95  * @param {number} lossRate - The ratio of the total number of audio packets lost to the total number of audio packets
96  * expected.
97  * @returns {Signal}
98  */
99  AUDIO_PROPERTY(float, lossRate)
100 
101  /*@jsdoc
102  * Triggered when the total number of audio packets lost changes.
103  * @function AudioStats.AudioStreamStats.lossCountChanged
104  * @param {number} lossCount - The total number of audio packets lost.
105  * @returns {Signal}
106  */
107  AUDIO_PROPERTY(float, lossCount)
108 
109  /*@jsdoc
110  * Triggered when the ratio of the number of audio packets lost to the number of audio packets expected since the previous
111  * statistic changes.
112  * @function AudioStats.AudioStreamStats.lossRateWindowChanged
113  * @param {number} lossRateWindow - The ratio of the number of audio packets lost to the number of audio packets expected
114  * since the previous statistic.
115  * @returns {Signal}
116  */
117  AUDIO_PROPERTY(float, lossRateWindow)
118 
119  /*@jsdoc
120  * Triggered when the number of audio packets lost since the previous statistic changes.
121  * @function AudioStats.AudioStreamStats.lossCountWindowChanged
122  * @param {number} lossCountWindow - The number of audio packets lost since the previous statistic.
123  * @returns {Signal}
124  */
125  AUDIO_PROPERTY(float, lossCountWindow)
126 
127  /*@jsdoc
128  * Triggered when the desired number of audio frames for the jitter buffer changes.
129  * @function AudioStats.AudioStreamStats.framesDesiredChanged
130  * @param {number} framesDesired - The desired number of audio frames for the jitter buffer.
131  * @returns {Signal}
132  */
133  AUDIO_PROPERTY(int, framesDesired)
134 
135  /*@jsdoc
136  * Triggered when the number of audio frames containing data available changes.
137  * @function AudioStats.AudioStreamStats.framesAvailableChanged
138  * @param {number} framesAvailable - The number of audio frames containing data available.
139  * @returns {Signal}
140  */
141  AUDIO_PROPERTY(int, framesAvailable)
142 
143  /*@jsdoc
144  * Triggered when the time-weighted average of audio frames containing data available changes.
145  * @function AudioStats.AudioStreamStats.framesAvailableAvgChanged
146  * @param {number} framesAvailableAvg - The time-weighted average of audio frames containing data available.
147  * @returns {Signal}
148  */
149  AUDIO_PROPERTY(int, framesAvailableAvg)
150 
151  /*@jsdoc
152  * Triggered when the duration of audio waiting to be played changes.
153  * @function AudioStats.AudioStreamStats.unplayedMsMaxChanged
154  * @param {number} unplayedMsMax - The duration of audio waiting to be played, in ms.
155  * @returns {Signal}
156  */
157  AUDIO_PROPERTY(float, unplayedMsMax)
158 
159  /*@jsdoc
160  * Triggered when the total number of times that audio frames have not been available for processing changes.
161  * @function AudioStats.AudioStreamStats.starveCountChanged
162  * @param {number} starveCount - The total number of times that audio frames have not been available for processing.
163  * @returns {Signal}
164  */
165  AUDIO_PROPERTY(int, starveCount)
166 
167  /*@jsdoc
168  * Triggered when the most recenbernumber of consecutive times that audio frames have not been available for processing
169  * changes.
170  * @function AudioStats.AudioStreamStats.lastStarveDurationCountChanged
171  * @param {number} lastStarveDurationCount - The most recent number of consecutive times that audio frames have not been
172  * available for processing.
173  * @returns {Signal}
174  */
175  AUDIO_PROPERTY(int, lastStarveDurationCount)
176 
177  /*@jsdoc
178  * Triggered when the number of silent or old audio frames dropped changes.
179  * @function AudioStats.AudioStreamStats.dropCountChanged
180  * @param {number} dropCount - The number of silent or old audio frames dropped.
181  * @returns {Signal}
182  */
183  AUDIO_PROPERTY(int, dropCount)
184 
185  /*@jsdoc
186  * Triggered when the number of times that the audio ring buffer has overflowed changes.
187  * @function AudioStats.AudioStreamStats.overflowCountChanged
188  * @param {number} overflowCount - The number of times that the audio ring buffer has overflowed.
189  * @returns {Signal}
190  */
191  AUDIO_PROPERTY(int, overflowCount)
192 
193  /*@jsdoc
194  * Triggered when the overall maximum time between data packets changes.
195  * @function AudioStats.AudioStreamStats.timegapMsMaxChanged
196  * @param {number} timegapMsMax - The overall maximum time between data packets, in ms.
197  * @returns {Signal}
198  */
199  AUDIO_PROPERTY(quint64, timegapMsMax)
200 
201  /*@jsdoc
202  * Triggered when the overall average time between data packets changes.
203  * @function AudioStats.AudioStreamStats.timegapMsAvgChanged
204  * @param {number} timegapMsAvg - The overall average time between data packets, in ms.
205  * @returns {Signal}
206  */
207  AUDIO_PROPERTY(quint64, timegapMsAvg)
208 
209  /*@jsdoc
210  * Triggered when the recent maximum time between data packets changes.
211  * @function AudioStats.AudioStreamStats.timegapMsMaxWindowChanged
212  * @param {number} timegapMsMaxWindow - The recent maximum time between data packets, in ms.
213  * @returns {Signal}
214  */
215  AUDIO_PROPERTY(quint64, timegapMsMaxWindow)
216 
217  /*@jsdoc
218  * Triggered when the recent average time between data packets changes.
219  * @function AudioStats.AudioStreamStats.timegapMsAvgWindowChanged
220  * @param {number} timegapMsAvgWindow - The recent average time between data packets, in ms.
221  * @returns {Signal}
222  */
223  AUDIO_PROPERTY(quint64, timegapMsAvgWindow)
224 
225 public:
226  void updateStream(const AudioStreamStats& stats);
227 
228 private:
229  friend class AudioStatsInterface;
230  AudioStreamStatsInterface(QObject* parent);
231 };
232 
233 class AudioStatsInterface : public QObject {
234  Q_OBJECT
235 
236  /*@jsdoc
237  * The <code>AudioStats</code> API provides statistics of the client and mixer audio.
238  *
239  * @namespace AudioStats
240  *
241  * @hifi-interface
242  * @hifi-client-entity
243  * @hifi-avatar
244  *
245  * @property {AudioStats.AudioStreamStats} clientStream - Statistics of the client's audio stream.
246  * <em>Read-only.</em>
247  * @property {number} inputReadMsMax - The maximum duration of a block of audio data recently read from the microphone, in
248  * ms.
249  * <em>Read-only.</em>
250  * @property {number} inputUnplayedMsMax - The maximum duration of microphone audio recently in the input buffer waiting to
251  * be played, in ms.
252  * <em>Read-only.</em>
253  * @property {AudioStats.AudioStreamStats} mixerStream - Statistics of the audio mixer's stream.
254  * <em>Read-only.</em>
255  * @property {number} outputUnplayedMsMax - The maximum duration of output audio recently in the output buffer waiting to
256  * be played, in ms.
257  * <em>Read-only.</em>
258  * @property {number} pingMs - The current ping time to the audio mixer, in ms.
259  * <em>Read-only.</em>
260  * @property {number} sentTimegapMsAvg - The overall average time between sending data packets to the audio mixer, in ms.
261  * <em>Read-only.</em>
262  * @property {number} sentTimegapMsAvgWindow - The recent average time between sending data packets to the audio mixer, in
263  * ms.
264  * <em>Read-only.</em>
265  * @property {number} sentTimegapMsMax - The overall maximum time between sending data packets to the audio mixer, in ms.
266  * <em>Read-only.</em>
267  * @property {number} sentTimegapMsMaxWindow - The recent maximum time between sending data packets to the audio mixer, in
268  * ms.
269  * <em>Read-only.</em>
270  */
271 
272  /*@jsdoc
273  * Triggered when the ping time to the audio mixer changes.
274  * @function AudioStats.pingMsChanged
275  * @param {number} pingMs - The ping time to the audio mixer, in ms.
276  * @returns {Signal}
277  */
278  AUDIO_PROPERTY(float, pingMs);
279 
280 
281  /*@jsdoc
282  * Triggered when the maximum duration of a block of audio data recently read from the microphone changes.
283  * @function AudioStats.inputReadMsMaxChanged
284  * @param {number} inputReadMsMax - The maximum duration of a block of audio data recently read from the microphone, in ms.
285  * @returns {Signal}
286  */
287  AUDIO_PROPERTY(float, inputReadMsMax);
288 
289  /*@jsdoc
290  * Triggered when the maximum duration of microphone audio recently in the input buffer waiting to be played changes.
291  * @function AudioStats.inputUnplayedMsMaxChanged
292  * @param {number} inputUnplayedMsMax - The maximum duration of microphone audio recently in the input buffer waiting to be
293  * played, in ms.
294  * @returns {Signal}
295  */
296  AUDIO_PROPERTY(float, inputUnplayedMsMax);
297 
298  /*@jsdoc
299  * Triggered when the maximum duration of output audio recently in the output buffer waiting to be played changes.
300  * @function AudioStats.outputUnplayedMsMaxChanged
301  * @param {number} outputUnplayedMsMax - The maximum duration of output audio recently in the output buffer waiting to be
302  * played, in ms.
303  * @returns {Signal}
304  */
305  AUDIO_PROPERTY(float, outputUnplayedMsMax);
306 
307 
308  /*@jsdoc
309  * Triggered when the overall maximum time between sending data packets to the audio mixer changes.
310  * @function AudioStats.sentTimegapMsMaxChanged
311  * @param {number} sentTimegapMsMax - The overall maximum time between sending data packets to the audio mixer, in ms.
312  * @returns {Signal}
313  */
314  AUDIO_PROPERTY(quint64, sentTimegapMsMax);
315 
316  /*@jsdoc
317  * Triggered when the overall average time between sending data packets to the audio mixer changes.
318  * @function AudioStats.sentTimegapMsAvgChanged
319  * @param {number} sentTimegapMsAvg - The overall average time between sending data packets to the audio mixer, in ms.
320  * @returns {Signal}
321  */
322  AUDIO_PROPERTY(quint64, sentTimegapMsAvg);
323 
324  /*@jsdoc
325  * Triggered when the recent maximum time between sending data packets to the audio mixer changes.
326  * @function AudioStats.sentTimegapMsMaxWindowChanged
327  * @param {number} sentTimegapMsMaxWindow - The recent maximum time between sending data packets to the audio mixer, in ms.
328  * @returns {Signal}
329  */
330  AUDIO_PROPERTY(quint64, sentTimegapMsMaxWindow);
331 
332  /*@jsdoc
333  * Triggered when the recent average time between sending data packets to the audio mixer changes.
334  * @function AudioStats.sentTimegapMsAvgWindowChanged
335  * @param {number} sentTimegapMsAvgWindow - The recent average time between sending data packets to the audio mixer, in
336  * ms.
337  * @returns {Signal}
338  */
339  AUDIO_PROPERTY(quint64, sentTimegapMsAvgWindow);
340 
341  Q_PROPERTY(AudioStreamStatsInterface* mixerStream READ getMixerStream NOTIFY mixerStreamChanged);
342  Q_PROPERTY(AudioStreamStatsInterface* clientStream READ getClientStream NOTIFY clientStreamChanged);
343 
344  // FIXME: The injectorStreams property isn't available in JavaScript but the notification signal is.
345  Q_PROPERTY(QObject* injectorStreams READ getInjectorStreams NOTIFY injectorStreamsChanged);
346 
347 public:
348  AudioStreamStatsInterface* getMixerStream() const { return _mixer; }
349  AudioStreamStatsInterface* getClientStream() const { return _client; }
350  QObject* getInjectorStreams() const { return _injectors; }
351 
352  void updateLocalBuffers(const MovingMinMaxAvg<float>& inputMsRead,
353  const MovingMinMaxAvg<float>& inputMsUnplayed,
354  const MovingMinMaxAvg<float>& outputMsUnplayed,
355  const MovingMinMaxAvg<quint64>& timegaps);
356  void updateMixerStream(const AudioStreamStats& stats) { _mixer->updateStream(stats); emit mixerStreamChanged(); }
357  void updateClientStream(const AudioStreamStats& stats) { _client->updateStream(stats); emit clientStreamChanged(); }
358  void updateInjectorStreams(const QHash<QUuid, AudioStreamStats>& stats);
359 
360 signals:
361 
362  /*@jsdoc
363  * Triggered when the mixer's stream statistics have been updated.
364  * @function AudioStats.mixerStreamChanged
365  * @returns {Signal}
366  */
367  void mixerStreamChanged();
368 
369  /*@jsdoc
370  * Triggered when the client's stream statisticss have been updated.
371  * @function AudioStats.clientStreamChanged
372  * @returns {Signal}
373  */
374  void clientStreamChanged();
375 
376  /*@jsdoc
377  * Triggered when the injector streams' statistics have been updated.
378  * <p><strong>Note:</strong> The injector streams' statistics are currently not provided.</p>
379  * @function AudioStats.injectorStreamsChanged
380  * @returns {Signal}
381  */
382  void injectorStreamsChanged();
383 
384 private:
385  friend class AudioIOStats;
386  AudioStatsInterface(QObject* parent);
387  AudioStreamStatsInterface* _client;
388  AudioStreamStatsInterface* _mixer;
389  QObject* _injectors;
390 };
391 
392 class AudioIOStats : public QObject {
393  Q_OBJECT
394 public:
395  AudioIOStats(MixedProcessedAudioStream* receivedAudioStream);
396 
397  void reset();
398 
399  AudioStatsInterface* data() const { return _interface; }
400 
401  void updateInputMsRead(float ms) const { _inputMsRead.update(ms); }
402  void updateInputMsUnplayed(float ms) const { _inputMsUnplayed.update(ms); }
403  void updateOutputMsUnplayed(float ms) const { _outputMsUnplayed.update(ms); }
404  void sentPacket() const;
405 
406  void publish();
407 
408 public slots:
409  void processStreamStatsPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
410 
411 private:
412  AudioStatsInterface* _interface;
413 
414  mutable MovingMinMaxAvg<float> _inputMsRead;
415  mutable MovingMinMaxAvg<float> _inputMsUnplayed;
416  mutable MovingMinMaxAvg<float> _outputMsUnplayed;
417 
418  mutable quint64 _lastSentPacketTime;
419  mutable MovingMinMaxAvg<quint64> _packetTimegaps;
420 
421  MixedProcessedAudioStream* _receivedAudioStream;
422  QHash<QUuid, AudioStreamStats> _injectorStreams;
423 };
424 
425 #endif // hifi_AudioIOStats_h