summaryrefslogtreecommitdiff
path: root/src/audio/jittered_audio_backend.cc
blob: 5561c11cfb88d797d949900e153bfe0f844dd544 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// This file is part of the 64k demo project.
// It implements a test backend that simulates jittered audio consumption.

#if !defined(STRIP_ALL)

#include "jittered_audio_backend.h"
#include "audio.h"
#include "ring_buffer.h"
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <random>
#include <thread>

JitteredAudioBackend::JitteredAudioBackend()
    : running_(false),
      should_stop_(false),
      jitter_ms_(5.0f),
      base_interval_ms_(10.0f),
      min_chunk_frames_(256),
      max_chunk_frames_(1024),
      total_frames_consumed_(0),
      underrun_count_(0) {
}

JitteredAudioBackend::~JitteredAudioBackend() {
  shutdown();
}

void JitteredAudioBackend::init() {
  // Nothing to initialize
}

void JitteredAudioBackend::start() {
  if (running_.load()) return;

  should_stop_.store(false);
  running_.store(true);

  // Start audio thread
  audio_thread_ = std::thread(&JitteredAudioBackend::audio_thread_loop, this);
}

void JitteredAudioBackend::shutdown() {
  if (!running_.load()) return;

  should_stop_.store(true);

  if (audio_thread_.joinable()) {
    audio_thread_.join();
  }

  running_.store(false);
}

void JitteredAudioBackend::set_jitter_amount(float jitter_ms) {
  jitter_ms_ = jitter_ms;
}

void JitteredAudioBackend::set_base_interval(float interval_ms) {
  base_interval_ms_ = interval_ms;
}

void JitteredAudioBackend::set_chunk_size_range(int min_frames, int max_frames) {
  min_chunk_frames_ = min_frames;
  max_chunk_frames_ = max_frames;
}

void JitteredAudioBackend::audio_thread_loop() {
  AudioRingBuffer* ring_buffer = audio_get_ring_buffer();
  if (ring_buffer == nullptr) return;

  // Random number generator for jitter
  std::random_device rd;
  std::mt19937 gen(rd());
  std::uniform_real_distribution<float> jitter_dist(-jitter_ms_, jitter_ms_);
  std::uniform_int_distribution<int> chunk_dist(min_chunk_frames_, max_chunk_frames_);

  while (!should_stop_.load()) {
    // Calculate jittered wait time
    const float jitter = jitter_dist(gen);
    const float wait_ms = base_interval_ms_ + jitter;
    const int wait_us = (int)(wait_ms * 1000.0f);

    if (wait_us > 0) {
      std::this_thread::sleep_for(std::chrono::microseconds(wait_us));
    }

    // Random chunk size
    const int chunk_frames = chunk_dist(gen);
    const int chunk_samples = chunk_frames * 2;  // Stereo

    // Read from ring buffer
    float* temp_buffer = new float[chunk_samples];
    const int read_samples = ring_buffer->read(temp_buffer, chunk_samples);

    // Check for underrun
    if (read_samples < chunk_samples) {
      underrun_count_.fetch_add(1);
    }

    // Track consumption
    total_frames_consumed_.fetch_add(read_samples / 2);

    // Notify of frames rendered (for tracking)
    on_frames_rendered(read_samples / 2);

    delete[] temp_buffer;
  }
}

#endif /* !defined(STRIP_ALL) */