libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
audiotestpattern.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_PROAV
13#include <cstdint>
14
15#include <promeki/namespace.h>
16#include <promeki/string.h>
17#include <promeki/error.h>
18#include <promeki/audiolevel.h>
19#include <promeki/audiodesc.h>
21#include <promeki/enumlist.h>
22#include <promeki/enums.h>
23#include <promeki/list.h>
24#include <promeki/map.h>
26#include <promeki/ltcencoder.h>
27
28PROMEKI_NAMESPACE_BEGIN
29
30class AudioGen;
31class Timecode;
32
93class AudioTestPattern {
94 public:
96 using UPtr = UniquePtr<AudioTestPattern>;
97
104 AudioTestPattern(const AudioDesc &desc);
105
107 ~AudioTestPattern();
108
109 AudioTestPattern(const AudioTestPattern &) = delete;
110 AudioTestPattern &operator=(const AudioTestPattern &) = delete;
111
113 const AudioDesc &desc() const { return _desc; }
114
116 const EnumList &channelModes() const { return _channelModes; }
117
125 void setChannelModes(const EnumList &modes) { _channelModes = modes; }
126
128 double toneFrequency() const { return _toneFreq; }
129
131 void setToneFrequency(double freq) { _toneFreq = freq; }
132
134 AudioLevel toneLevel() const { return _toneLevel; }
135
137 void setToneLevel(AudioLevel level) { _toneLevel = level; }
138
140 AudioLevel ltcLevel() const { return _ltcLevel; }
141
143 void setLtcLevel(AudioLevel level) { _ltcLevel = level; }
144
146 double channelIdBaseFreq() const { return _chanIdBaseFreq; }
147
154 void setChannelIdBaseFreq(double hz) { _chanIdBaseFreq = hz; }
155
157 double channelIdStepFreq() const { return _chanIdStepFreq; }
158
160 void setChannelIdStepFreq(double hz) { _chanIdStepFreq = hz; }
161
163 double chirpStartFreq() const { return _chirpStartFreq; }
164
166 void setChirpStartFreq(double hz) { _chirpStartFreq = hz; }
167
169 double chirpEndFreq() const { return _chirpEndFreq; }
170
172 void setChirpEndFreq(double hz) { _chirpEndFreq = hz; }
173
175 double chirpDurationSec() const { return _chirpDurationSec; }
176
178 void setChirpDurationSec(double sec) { _chirpDurationSec = sec; }
179
181 double dualToneFreq1() const { return _dualToneFreq1; }
182
184 void setDualToneFreq1(double hz) { _dualToneFreq1 = hz; }
185
187 double dualToneFreq2() const { return _dualToneFreq2; }
188
190 void setDualToneFreq2(double hz) { _dualToneFreq2 = hz; }
191
199 double dualToneRatio() const { return _dualToneRatio; }
200
202 void setDualToneRatio(double ratio) { _dualToneRatio = ratio; }
203
205 double sweepStartFreq() const { return _sweepStartFreq; }
206
208 void setSweepStartFreq(double hz) { _sweepStartFreq = hz; }
209
211 double sweepEndFreq() const { return _sweepEndFreq; }
212
214 void setSweepEndFreq(double hz) { _sweepEndFreq = hz; }
215
217 double sweepDurationSec() const { return _sweepDurationSec; }
218
220 void setSweepDurationSec(double sec) { _sweepDurationSec = sec; }
221
223 double polarityPulseHz() const { return _polarityPulseHz; }
224
226 void setPolarityPulseHz(double hz) { _polarityPulseHz = hz; }
227
229 double polarityPulseWidthSec() const { return _polarityPulseWidthSec; }
230
232 void setPolarityPulseWidthSec(double sec) { _polarityPulseWidthSec = sec; }
233
235 const List<double> &steppedToneFreqs() const { return _steppedToneFreqs; }
236
238 void setSteppedToneFreqs(const List<double> &freqs) { _steppedToneFreqs = freqs; }
239
241 double steppedToneStepSec() const { return _steppedToneStepSec; }
242
244 void setSteppedToneStepSec(double sec) { _steppedToneStepSec = sec; }
245
247 AudioLevel dialnormLevel() const { return _dialnormLevel; }
248
250 void setDialnormLevel(AudioLevel level) { _dialnormLevel = level; }
251
266 double noiseBufferSeconds() const { return _noiseBufferSeconds; }
267
269 void setNoiseBufferSeconds(double sec) { _noiseBufferSeconds = sec; }
270
279 uint32_t noiseSeed() const { return _noiseSeed; }
280
282 void setNoiseSeed(uint32_t seed) { _noiseSeed = seed; }
283
294 Error configure();
295
319 SharedPtr<PcmAudioPayload, true, PcmAudioPayload> createPayload(size_t samples,
320 const Timecode &tc) const;
321
328 SharedPtr<PcmAudioPayload, true, PcmAudioPayload> createPayload(size_t samples) const {
329 return createPayload(samples, Timecode());
330 }
331
336 uint8_t pcmMarkerStreamId() const { return _pcmMarkerStreamId; }
337
346 void setPcmMarkerStreamId(uint8_t streamId) { _pcmMarkerStreamId = streamId; }
347
356 uint64_t pcmMarkerFrameNumber() const { return _pcmMarkerCounter; }
357
368 void setPcmMarkerFrameNumber(uint64_t fn) { _pcmMarkerCounter = fn; }
369
382 static double channelIdFrequency(size_t channelIndex, double baseFreq, double stepFreq) {
383 return baseFreq + static_cast<double>(channelIndex) * stepFreq;
384 }
385
391 static constexpr double kSrcProbeFrequencyHz = 997.0;
392
394 static constexpr uint8_t kDefaultPcmMarkerStreamId = 0;
395
399 static constexpr uint64_t kPcmMarkerFrameMask = 0x0000ffffffffffffULL;
400
402 static const List<double> kDefaultSteppedToneFreqs;
403
405 static constexpr double kEbuLineupFreqHz = 1000.0;
406
408 static constexpr double kEbuLineupOnSec = 3.0;
409
411 static constexpr double kEbuLineupCycleSec = 5.0;
412
414 static constexpr double kBlitsFreqHz = 1000.0;
415
417 static constexpr double kBlitsSegmentSec = 1.0;
418
420 static constexpr size_t kIec60958StatusBits = 192;
421
423 static constexpr size_t kIec60958SamplesPerBit = 4;
424
425 private:
426 AudioDesc _desc;
427 EnumList _channelModes;
428 double _toneFreq = 1000.0;
429 AudioLevel _toneLevel = AudioLevel::fromDbfs(-20.0);
430 AudioLevel _ltcLevel = AudioLevel::fromDbfs(-20.0);
431 double _chanIdBaseFreq = 1000.0;
432 double _chanIdStepFreq = 100.0;
433
434 double _chirpStartFreq = 20.0;
435 double _chirpEndFreq = 20000.0;
436 double _chirpDurationSec = 1.0;
437
438 double _dualToneFreq1 = 60.0;
439 double _dualToneFreq2 = 7000.0;
440 double _dualToneRatio = 0.25;
441
442 double _sweepStartFreq = 20.0;
443 double _sweepEndFreq = 20000.0;
444 double _sweepDurationSec = 1.0;
445
446 double _polarityPulseHz = 1.0;
447 double _polarityPulseWidthSec = 0.001;
448
449 List<double> _steppedToneFreqs;
450 double _steppedToneStepSec = 1.0;
451
452 AudioLevel _dialnormLevel = AudioLevel::fromDbfs(-24.0);
453
454 double _noiseBufferSeconds = 10.0;
455 uint32_t _noiseSeed = 0x505244A4u; // 'PRDA' — TPG test seed
456
457 // Per-channel generators built in configure(). A nullptr
458 // entry means the channel is silenced (either because
459 // the mode list is shorter than the channel count, or
460 // because it carries a mode that does not need a
461 // persistent generator — e.g. LTC / AvSync / noise /
462 // chirp / PCM marker, which are synthesized per-call).
463 mutable List<AudioGen *> _chanGens;
464 LtcEncoder::UPtr _ltcEncoder;
465
466 // AvSync tone burst cache: built lazily per requested
467 // sample count so the cadenced rates (29.97, 59.94)
468 // which alternate between two sizes don't thrash the
469 // cache. Each entry is a one-shot mono tone burst
470 // (phase reset to zero so every marker frame is
471 // byte-identical).
472 mutable Map<size_t, List<float>> _avSyncToneCache;
473
474 // Cached noise buffers. One-dimensional arrays of
475 // samples that the per-channel noise dispatch indexes
476 // with a per-channel offset so different WhiteNoise /
477 // PinkNoise channels stay decorrelated while sharing
478 // the same underlying samples. Generated at most once
479 // per configure() call; empty until a noise channel
480 // requests them.
481 List<float> _whiteNoiseBuffer;
482 List<float> _pinkNoiseBuffer;
483
484 // Continuous-sweep cursor/phase for the Chirp
485 // generator. The cursor is the sample position inside
486 // the current sweep period (wraps at
487 // _chirpDurationSec * sampleRate). The phase is the
488 // running sin-argument; we accumulate it sample-by-
489 // sample instead of recomputing it from the closed-
490 // form integral so the waveform stays sample-exact
491 // continuous across both chunk boundaries and period
492 // wraps (the closed form resets to 0 at each period
493 // wrap, which causes an audible click).
494 mutable double _chirpSampleCursor = 0.0;
495 mutable double _chirpPhase = 0.0;
496
497 // Per-tone phase accumulators for the DualTone
498 // generator. Tracking these across create() calls is
499 // the only way to avoid a once-per-chunk phase-reset
500 // click; a 30 fps pipeline restarts the pattern 30
501 // times a second otherwise, which is plainly audible.
502 mutable double _dualTonePhase1 = 0.0;
503 mutable double _dualTonePhase2 = 0.0;
504
505 // Cumulative sample cursor for the cached noise
506 // buffers. Advanced once per create() call so
507 // successive frames read from successive regions of
508 // the buffer instead of re-reading the same slice —
509 // otherwise the noise would modulate at the frame
510 // rate and sound like a tone, not noise. All noise
511 // channels share this cursor, with a per-channel
512 // offset layered on top so multiple noise channels
513 // on the same stream stay decorrelated.
514 mutable size_t _noiseSampleCursor = 0;
515
516 // Stream ID stamped on every PcmMarker payload (top 8
517 // bits of the encoded word). Defaults to 0; the wrapper
518 // (e.g. TpgMediaIO) overrides via setPcmMarkerStreamId.
519 uint8_t _pcmMarkerStreamId = kDefaultPcmMarkerStreamId;
520
521 // Running monotonic counter used as the 48-bit frame
522 // number field of the PcmMarker payload. Auto-
523 // incremented on every create() call; an external
524 // wrapper may overwrite via setPcmMarkerFrameNumber to
525 // lock the audio marker to a video frame counter.
526 mutable uint64_t _pcmMarkerCounter = 0;
527
528 mutable double _sweepSampleCursor = 0.0;
529 mutable double _sweepPhase = 0.0;
530
531 mutable double _polarityCursor = 0.0;
532
533 mutable double _steppedToneSampleCursor = 0.0;
534 mutable double _steppedTonePhase = 0.0;
535
536 mutable size_t _blitsSampleCursor = 0;
537 mutable double _blitsPhase = 0.0;
538
539 mutable size_t _ebuLineupSampleCursor = 0;
540 mutable double _ebuLineupPhase = 0.0;
541
542 mutable size_t _iec60958SampleCursor = 0;
543
544 void clearGenerators();
545 AudioPattern modeForChannel(size_t channelIndex) const;
546 const List<float> &avSyncBurst(size_t samples) const;
547
548 // Shared inner loop: writes @p samples samples per
549 // channel of the configured pattern into @p out,
550 // advancing all phase and cursor state. Used by both
551 // @ref create (Audio allocation) and @ref createPayload
552 // (PcmAudioPayload allocation).
553 void writePattern(float *out, size_t samples, const Timecode &tc) const;
554 void buildWhiteNoiseBuffer();
555 void buildPinkNoiseBuffer();
556
557 void writeNoiseChannel(float *out, size_t channel, size_t channels, size_t samples,
558 const List<float> &buffer) const;
559 void writeChirpChannel(float *out, size_t channel, size_t channels, size_t samples) const;
560 void writeDualToneChannel(float *out, size_t channel, size_t channels, size_t samples) const;
571 void stampPcmMarkers(PcmAudioPayload &payload, uint64_t frameNumber) const;
572 void writeSweepChannel(float *out, size_t channel, size_t channels, size_t samples) const;
573 void writePolarityChannel(float *out, size_t channel, size_t channels, size_t samples) const;
574 void writeSteppedToneChannel(float *out, size_t channel, size_t channels, size_t samples) const;
575 void writeBlitsChannel(float *out, size_t channel, size_t channels, size_t totalChannels,
576 size_t samples) const;
577 void writeEbuLineupChannel(float *out, size_t channel, size_t channels, size_t samples) const;
578 void writeDialnormChannel(float *out, size_t channel, size_t channels, size_t samples) const;
579 void writeIec60958Channel(float *out, size_t channel, size_t channels, size_t samples) const;
580};
581
582PROMEKI_NAMESPACE_END
583
584#endif // PROMEKI_ENABLE_PROAV