JackTrip
AudioTester.h
Go to the documentation of this file.
1//*****************************************************************
2/*
3 JackTrip: A System for High-Quality Audio Network Performance
4 over the Internet
5
6 Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe.
7 SoundWIRE group at CCRMA, Stanford University.
8
9 Permission is hereby granted, free of charge, to any person
10 obtaining a copy of this software and associated documentation
11 files (the "Software"), to deal in the Software without
12 restriction, including without limitation the rights to use,
13 copy, modify, merge, publish, distribute, sublicense, and/or sell
14 copies of the Software, and to permit persons to whom the
15 Software is furnished to do so, subject to the following
16 conditions:
17
18 The above copyright notice and this permission notice shall be
19 included in all copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 OTHER DEALINGS IN THE SOFTWARE.
29*/
30//*****************************************************************
31
39#pragma once
40
41#include "jacktrip_types.h" // sample_t
42
43#include <iostream>
44//#include <ctime>
45#include <chrono>
46#include <cstdint>
47#include <cmath>
48#include <string>
49#include <map>
50
51#include <QVarLengthArray>
52
54{
55 bool enabled { false };
56 float printIntervalSec { 1.0f };
57 int sendChannel { 0 };
58
59 bool impulsePending { false };
60 int64_t lastPrintTimeUS { 0 };
61 int64_t impulseTimeUS { 0 };
62 int64_t impulseTimeSamples { 0 };
63 uint64_t sampleCountSinceImpulse { 1 }; // 0 not used
64 double roundTripMean { 0.0 };
65 double roundTripMeanSquare { 0.0 };
66 double roundTripCount { 0.0 };
67 const int bufferSkipStart { 100 };
68 int bufferSkip { bufferSkipStart };
69 const float impulseAmplitude { 0.1f };
70 const int numAmpCells { 10 };
71 const float ampCellHeight { impulseAmplitude/numAmpCells };
72
73 const double latencyHistogramCellWidth { 5.0 }; // latency range in ms covered one cell
74 const double latencyHistogramCellMin { 0.0 };
75 const double latencyHistogramCellMax { 19.0 }; // in cells, so 5x this is max latency in ms
76 const int latencyHistogramPrintCountMax { 72 }; // normalize when asterisks exceed this number
77
78 int pendingCell { 0 }; // 0 is not used
79 float sampleRate { 48000.0f };
80
81public:
83 ~AudioTester() = default;
84
85 void lookForReturnPulse(QVarLengthArray<sample_t*>& out_buffer,
86 unsigned int n_frames);
87
88 void writeImpulse(QVarLengthArray<sample_t*>& mInBufCopy,
89 unsigned int n_frames);
90
91 bool getEnabled() { return enabled; }
92 void setEnabled(bool e) { enabled = e; }
93 void setPrintIntervalSec(float s) { printIntervalSec = s; }
94 void setSendChannel(int c) { sendChannel = c; }
95 int getSendChannel() { return sendChannel; }
96 int getPendingCell() { return pendingCell; }
97 void setPendingCell(int pc) { pendingCell = pc; }
98 void setSampleRate(float fs) { sampleRate = fs; }
99 int getBufferSkip() { return bufferSkip; } // used for debugging breakpoints
100 void printHelp(char* command, char helpCase);
101
102private:
103
104 float getImpulseAmp() {
105 pendingCell += 1; // only called when no impulse is pending
106 if (pendingCell >= numAmpCells) {
107 pendingCell = 1; // wrap-around, not using zero
108 }
109 float imp = float(pendingCell) * (impulseAmplitude/float(numAmpCells));
110 return imp;
111 }
112
113 int getImpulseCellNum(float amp) {
114 float ch = ampCellHeight;
115 float cell = amp / ch;
116 int iCell = int(std::floor(0.5f + cell));
117 if (iCell > numAmpCells - 1) {
118 std::cerr << "*** AudioTester.h: getImpulseCellNum("<<amp<<"): Return pulse amplitude is beyond maximum expected\n";
119 iCell = numAmpCells-1;
120 } else if (iCell < 0) {
121 std::cerr << "*** AudioTester.h: getImpulseCellNum("<<amp<<"): Return pulse amplitude is below minimum expected\n";
122 iCell = 0;
123 }
124 return iCell;
125 }
126
127 uint64_t timeMicroSec() {
128#if 1
129 using namespace std::chrono;
130 // return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
131 return duration_cast<microseconds>(high_resolution_clock::now().time_since_epoch()).count();
132#else
133 clock_t tics_since_launch = std::clock();
134 double timeUS = double(tics_since_launch)/double(CLOCKS_PER_SEC);
135 return (uint64_t)timeUS;
136#endif
137 }
138
139 std::map<int, int> latencyHistogram;
140
141 std::map<int, int> getLatencyHistogram() {
142 return latencyHistogram;
143 }
144
145 void extendLatencyHistogram(double latencyMS) {
146 int latencyCell = static_cast<int>(floor(std::max(latencyHistogramCellMin,
147 std::min(latencyHistogramCellMax,
148 std::floor(latencyMS / latencyHistogramCellWidth)))));
149 latencyHistogram[latencyCell] += 1;
150 }
151
152 int latencyHistogramCountMax() {
153 int lhMax = 0;
154 int histStart = latencyHistogramFirstNonzeroCellIndex();
155 int histLast = latencyHistogramLastNonzeroCellIndex();
156 for (int i = histStart; i <= histLast; ++i) {
157 int lhi = latencyHistogram[i];
158 if (lhi > lhMax) {
159 lhMax = lhi;
160 }
161 }
162 return lhMax;
163 }
164
165 int latencyHistogramFirstNonzeroCellIndex() {
166 for (int i=latencyHistogramCellMin; i <= latencyHistogramCellMax; i++) {
167 if (latencyHistogram[i]>0) {
168 return i;
169 }
170 }
171 std::cerr << "*** AudioTester: LATENCY HISTOGRAM IS EMPTY!\n";
172 return -1;
173 }
174
175 int latencyHistogramLastNonzeroCellIndex() {
176 for (int i=latencyHistogramCellMax; i>=latencyHistogramCellMin; i--) {
177 if (latencyHistogram[i]>0) {
178 return i;
179 }
180 }
181 std::cerr << "*** AudioTester: LATENCY HISTOGRAM IS EMPTY!\n";
182 return -1;
183 }
184
185 std::string getLatencyHistogramString() {
186 int histStart = latencyHistogramFirstNonzeroCellIndex();
187 int histLast = latencyHistogramLastNonzeroCellIndex();
188 std::string marker = "*";
189 double histScale = 1.0;
190 int lhcm = latencyHistogramCountMax();
191 int lhpcm = latencyHistogramPrintCountMax;
192 bool normalizing = lhpcm < lhcm;
193 if (normalizing) {
194 marker = "#";
195 histScale = double(lhpcm) / double(lhcm);
196 }
197 std::string rows = "";
198 for (int i = histStart; i <= histLast; ++i) {
199 int hi = latencyHistogram[i];
200 int hin = int(std::round(histScale * double(hi)));
201 std::string istrm1 = std::to_string(int(latencyHistogramCellWidth * double(i)));
202 std::string istr = std::to_string(int(latencyHistogramCellWidth * double(i+1)));
203 // std::string histr = boost::format("%02d",hi);
204 std::string histr = std::to_string(hi);
205 while (histr.length()<3) {
206 histr = " " + histr;
207 }
208 std::string row = "["+istrm1+"-"+istr+"ms]="+histr+":";
209 for (int j=0; j<hin; j++) {
210 row += marker;
211 }
212 rows += row + "\n";
213 }
214 if (histLast == latencyHistogramCellMax) {
215 rows += " and above\n";
216 }
217 return rows;
218 }
219
220};
Definition: AudioTester.h:54
void setEnabled(bool e)
Definition: AudioTester.h:92
void setPrintIntervalSec(float s)
Definition: AudioTester.h:93
bool getEnabled()
Definition: AudioTester.h:91
AudioTester()
Definition: AudioTester.h:82
void writeImpulse(QVarLengthArray< sample_t * > &mInBufCopy, unsigned int n_frames)
Definition: AudioTester.cpp:135
void setSendChannel(int c)
Definition: AudioTester.h:94
void printHelp(char *command, char helpCase)
Definition: AudioTester.cpp:179
int getBufferSkip()
Definition: AudioTester.h:99
void setPendingCell(int pc)
Definition: AudioTester.h:97
~AudioTester()=default
void setSampleRate(float fs)
Definition: AudioTester.h:98
int getSendChannel()
Definition: AudioTester.h:95
void lookForReturnPulse(QVarLengthArray< sample_t * > &out_buffer, unsigned int n_frames)
Definition: AudioTester.cpp:43
int getPendingCell()
Definition: AudioTester.h:96