1 /* 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #ifndef MODULES_AUDIO_PROCESSING_LOGGING_APM_DATA_DUMPER_H_ 12 #define MODULES_AUDIO_PROCESSING_LOGGING_APM_DATA_DUMPER_H_ 13 14 #include <stdint.h> 15 #include <stdio.h> 16 17 #if WEBRTC_APM_DEBUG_DUMP == 1 18 #include <memory> 19 #include <string> 20 #include <unordered_map> 21 #endif 22 23 #include "absl/strings/string_view.h" 24 #include "absl/types/optional.h" 25 #include "api/array_view.h" 26 #if WEBRTC_APM_DEBUG_DUMP == 1 27 #include "common_audio/wav_file.h" 28 #include "rtc_base/checks.h" 29 #include "rtc_base/string_utils.h" 30 #endif 31 32 // Check to verify that the define is properly set. 33 #if !defined(WEBRTC_APM_DEBUG_DUMP) || \ 34 (WEBRTC_APM_DEBUG_DUMP != 0 && WEBRTC_APM_DEBUG_DUMP != 1) 35 #error "Set WEBRTC_APM_DEBUG_DUMP to either 0 or 1" 36 #endif 37 38 namespace webrtc { 39 40 #if WEBRTC_APM_DEBUG_DUMP == 1 41 // Functor used to use as a custom deleter in the map of file pointers to raw 42 // files. 43 struct RawFileCloseFunctor { operatorRawFileCloseFunctor44 void operator()(FILE* f) const { fclose(f); } 45 }; 46 #endif 47 48 // Class that handles dumping of variables into files. 49 class ApmDataDumper { 50 public: 51 // Constructor that takes an instance index that may 52 // be used to distinguish data dumped from different 53 // instances of the code. 54 explicit ApmDataDumper(int instance_index); 55 56 ApmDataDumper() = delete; 57 ApmDataDumper(const ApmDataDumper&) = delete; 58 ApmDataDumper& operator=(const ApmDataDumper&) = delete; 59 60 ~ApmDataDumper(); 61 62 // Activates or deactivate the dumping functionality. SetActivated(bool activated)63 static void SetActivated(bool activated) { 64 #if WEBRTC_APM_DEBUG_DUMP == 1 65 recording_activated_ = activated; 66 #endif 67 } 68 69 // Returns whether dumping functionality is enabled/available. IsAvailable()70 static bool IsAvailable() { 71 #if WEBRTC_APM_DEBUG_DUMP == 1 72 return true; 73 #else 74 return false; 75 #endif 76 } 77 78 // Default dump set. 79 static constexpr size_t kDefaultDumpSet = 0; 80 81 // Specifies what dump set to use. All dump commands with a different dump set 82 // than the one specified will be discarded. If not specificed, all dump sets 83 // will be used. SetDumpSetToUse(int dump_set_to_use)84 static void SetDumpSetToUse(int dump_set_to_use) { 85 #if WEBRTC_APM_DEBUG_DUMP == 1 86 dump_set_to_use_ = dump_set_to_use; 87 #endif 88 } 89 90 // Set an optional output directory. SetOutputDirectory(absl::string_view output_dir)91 static void SetOutputDirectory(absl::string_view output_dir) { 92 #if WEBRTC_APM_DEBUG_DUMP == 1 93 RTC_CHECK_LT(output_dir.size(), kOutputDirMaxLength); 94 rtc::strcpyn(output_dir_, output_dir.size(), output_dir); 95 #endif 96 } 97 98 // Reinitializes the data dumping such that new versions 99 // of all files being dumped to are created. InitiateNewSetOfRecordings()100 void InitiateNewSetOfRecordings() { 101 #if WEBRTC_APM_DEBUG_DUMP == 1 102 ++recording_set_index_; 103 #endif 104 } 105 106 // Methods for performing dumping of data of various types into 107 // various formats. 108 void DumpRaw(absl::string_view name, 109 double v, 110 int dump_set = kDefaultDumpSet) { 111 #if WEBRTC_APM_DEBUG_DUMP == 1 112 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 113 return; 114 115 if (recording_activated_) { 116 FILE* file = GetRawFile(name); 117 fwrite(&v, sizeof(v), 1, file); 118 } 119 #endif 120 } 121 122 void DumpRaw(absl::string_view name, 123 size_t v_length, 124 const double* v, 125 int dump_set = kDefaultDumpSet) { 126 #if WEBRTC_APM_DEBUG_DUMP == 1 127 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 128 return; 129 130 if (recording_activated_) { 131 FILE* file = GetRawFile(name); 132 fwrite(v, sizeof(v[0]), v_length, file); 133 } 134 #endif 135 } 136 137 void DumpRaw(absl::string_view name, 138 rtc::ArrayView<const double> v, 139 int dump_set = kDefaultDumpSet) { 140 #if WEBRTC_APM_DEBUG_DUMP == 1 141 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 142 return; 143 144 if (recording_activated_) { 145 DumpRaw(name, v.size(), v.data()); 146 } 147 #endif 148 } 149 150 void DumpRaw(absl::string_view name, 151 float v, 152 int dump_set = kDefaultDumpSet) { 153 #if WEBRTC_APM_DEBUG_DUMP == 1 154 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 155 return; 156 157 if (recording_activated_) { 158 FILE* file = GetRawFile(name); 159 fwrite(&v, sizeof(v), 1, file); 160 } 161 #endif 162 } 163 164 void DumpRaw(absl::string_view name, 165 size_t v_length, 166 const float* v, 167 int dump_set = kDefaultDumpSet) { 168 #if WEBRTC_APM_DEBUG_DUMP == 1 169 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 170 return; 171 172 if (recording_activated_) { 173 FILE* file = GetRawFile(name); 174 fwrite(v, sizeof(v[0]), v_length, file); 175 } 176 #endif 177 } 178 179 void DumpRaw(absl::string_view name, 180 rtc::ArrayView<const float> v, 181 int dump_set = kDefaultDumpSet) { 182 #if WEBRTC_APM_DEBUG_DUMP == 1 183 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 184 return; 185 186 if (recording_activated_) { 187 DumpRaw(name, v.size(), v.data()); 188 } 189 #endif 190 } 191 192 void DumpRaw(absl::string_view name, bool v, int dump_set = kDefaultDumpSet) { 193 #if WEBRTC_APM_DEBUG_DUMP == 1 194 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 195 return; 196 197 if (recording_activated_) { 198 DumpRaw(name, static_cast<int16_t>(v)); 199 } 200 #endif 201 } 202 203 void DumpRaw(absl::string_view name, 204 size_t v_length, 205 const bool* v, 206 int dump_set = kDefaultDumpSet) { 207 #if WEBRTC_APM_DEBUG_DUMP == 1 208 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 209 return; 210 211 if (recording_activated_) { 212 FILE* file = GetRawFile(name); 213 for (size_t k = 0; k < v_length; ++k) { 214 int16_t value = static_cast<int16_t>(v[k]); 215 fwrite(&value, sizeof(value), 1, file); 216 } 217 } 218 #endif 219 } 220 221 void DumpRaw(absl::string_view name, 222 rtc::ArrayView<const bool> v, 223 int dump_set = kDefaultDumpSet) { 224 #if WEBRTC_APM_DEBUG_DUMP == 1 225 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 226 return; 227 228 if (recording_activated_) { 229 DumpRaw(name, v.size(), v.data()); 230 } 231 #endif 232 } 233 234 void DumpRaw(absl::string_view name, 235 int16_t v, 236 int dump_set = kDefaultDumpSet) { 237 #if WEBRTC_APM_DEBUG_DUMP == 1 238 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 239 return; 240 241 if (recording_activated_) { 242 FILE* file = GetRawFile(name); 243 fwrite(&v, sizeof(v), 1, file); 244 } 245 #endif 246 } 247 248 void DumpRaw(absl::string_view name, 249 size_t v_length, 250 const int16_t* v, 251 int dump_set = kDefaultDumpSet) { 252 #if WEBRTC_APM_DEBUG_DUMP == 1 253 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 254 return; 255 256 if (recording_activated_) { 257 FILE* file = GetRawFile(name); 258 fwrite(v, sizeof(v[0]), v_length, file); 259 } 260 #endif 261 } 262 263 void DumpRaw(absl::string_view name, 264 rtc::ArrayView<const int16_t> v, 265 int dump_set = kDefaultDumpSet) { 266 #if WEBRTC_APM_DEBUG_DUMP == 1 267 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 268 return; 269 270 if (recording_activated_) { 271 DumpRaw(name, v.size(), v.data()); 272 } 273 #endif 274 } 275 276 void DumpRaw(absl::string_view name, 277 int32_t v, 278 int dump_set = kDefaultDumpSet) { 279 #if WEBRTC_APM_DEBUG_DUMP == 1 280 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 281 return; 282 283 if (recording_activated_) { 284 FILE* file = GetRawFile(name); 285 fwrite(&v, sizeof(v), 1, file); 286 } 287 #endif 288 } 289 290 void DumpRaw(absl::string_view name, 291 size_t v_length, 292 const int32_t* v, 293 int dump_set = kDefaultDumpSet) { 294 #if WEBRTC_APM_DEBUG_DUMP == 1 295 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 296 return; 297 298 if (recording_activated_) { 299 FILE* file = GetRawFile(name); 300 fwrite(v, sizeof(v[0]), v_length, file); 301 } 302 #endif 303 } 304 305 void DumpRaw(absl::string_view name, 306 size_t v, 307 int dump_set = kDefaultDumpSet) { 308 #if WEBRTC_APM_DEBUG_DUMP == 1 309 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 310 return; 311 312 if (recording_activated_) { 313 FILE* file = GetRawFile(name); 314 fwrite(&v, sizeof(v), 1, file); 315 } 316 #endif 317 } 318 319 void DumpRaw(absl::string_view name, 320 size_t v_length, 321 const size_t* v, 322 int dump_set = kDefaultDumpSet) { 323 #if WEBRTC_APM_DEBUG_DUMP == 1 324 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 325 return; 326 327 if (recording_activated_) { 328 FILE* file = GetRawFile(name); 329 fwrite(v, sizeof(v[0]), v_length, file); 330 } 331 #endif 332 } 333 334 void DumpRaw(absl::string_view name, 335 rtc::ArrayView<const int32_t> v, 336 int dump_set = kDefaultDumpSet) { 337 #if WEBRTC_APM_DEBUG_DUMP == 1 338 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 339 return; 340 341 if (recording_activated_) { 342 DumpRaw(name, v.size(), v.data()); 343 } 344 #endif 345 } 346 347 void DumpRaw(absl::string_view name, 348 rtc::ArrayView<const size_t> v, 349 int dump_set = kDefaultDumpSet) { 350 #if WEBRTC_APM_DEBUG_DUMP == 1 351 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 352 return; 353 354 DumpRaw(name, v.size(), v.data()); 355 #endif 356 } 357 358 void DumpWav(absl::string_view name, 359 size_t v_length, 360 const float* v, 361 int sample_rate_hz, 362 int num_channels, 363 int dump_set = kDefaultDumpSet) { 364 #if WEBRTC_APM_DEBUG_DUMP == 1 365 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 366 return; 367 368 if (recording_activated_) { 369 WavWriter* file = GetWavFile(name, sample_rate_hz, num_channels, 370 WavFile::SampleFormat::kFloat); 371 file->WriteSamples(v, v_length); 372 } 373 #endif 374 } 375 376 void DumpWav(absl::string_view name, 377 rtc::ArrayView<const float> v, 378 int sample_rate_hz, 379 int num_channels, 380 int dump_set = kDefaultDumpSet) { 381 #if WEBRTC_APM_DEBUG_DUMP == 1 382 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) 383 return; 384 385 if (recording_activated_) { 386 DumpWav(name, v.size(), v.data(), sample_rate_hz, num_channels); 387 } 388 #endif 389 } 390 391 private: 392 #if WEBRTC_APM_DEBUG_DUMP == 1 393 static bool recording_activated_; 394 static absl::optional<int> dump_set_to_use_; 395 static constexpr size_t kOutputDirMaxLength = 1024; 396 static char output_dir_[kOutputDirMaxLength]; 397 const int instance_index_; 398 int recording_set_index_ = 0; 399 std::unordered_map<std::string, std::unique_ptr<FILE, RawFileCloseFunctor>> 400 raw_files_; 401 std::unordered_map<std::string, std::unique_ptr<WavWriter>> wav_files_; 402 403 FILE* GetRawFile(absl::string_view name); 404 WavWriter* GetWavFile(absl::string_view name, 405 int sample_rate_hz, 406 int num_channels, 407 WavFile::SampleFormat format); 408 #endif 409 }; 410 411 } // namespace webrtc 412 413 #endif // MODULES_AUDIO_PROCESSING_LOGGING_APM_DATA_DUMPER_H_ 414