summaryrefslogtreecommitdiff
path: root/src/audio/ring_buffer.cc
blob: 25cf85375250b195ed9fb7cb54023ef7abd81321 (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
// This file is part of the 64k demo project.
// It implements a lock-free ring buffer for audio streaming.

#include "ring_buffer.h"
#include <algorithm>
#include <cstring>

AudioRingBuffer::AudioRingBuffer()
    : capacity_(RING_BUFFER_CAPACITY_SAMPLES),
      write_pos_(0),
      read_pos_(0),
      total_read_(0) {
  memset(buffer_, 0, sizeof(buffer_));
}

AudioRingBuffer::~AudioRingBuffer() {
  // Nothing to clean up (static buffer)
}

int AudioRingBuffer::available_write() const {
  const int write = write_pos_.load(std::memory_order_acquire);
  const int read = read_pos_.load(std::memory_order_acquire);

  if (write >= read) {
    return capacity_ - (write - read) - 1;  // -1 to avoid full/empty ambiguity
  } else {
    return read - write - 1;
  }
}

int AudioRingBuffer::available_read() const {
  const int write = write_pos_.load(std::memory_order_acquire);
  const int read = read_pos_.load(std::memory_order_acquire);

  if (write >= read) {
    return write - read;
  } else {
    return capacity_ - (read - write);
  }
}

int AudioRingBuffer::write(const float* samples, int count) {
  const int avail = available_write();
  const int to_write = std::min(count, avail);

  if (to_write <= 0) {
    return 0;
  }

  const int write = write_pos_.load(std::memory_order_acquire);
  const int space_to_end = capacity_ - write;

  if (to_write <= space_to_end) {
    // Write in one chunk
    memcpy(&buffer_[write], samples, to_write * sizeof(float));
    write_pos_.store((write + to_write) % capacity_, std::memory_order_release);
  } else {
    // Write in two chunks (wrap around)
    memcpy(&buffer_[write], samples, space_to_end * sizeof(float));
    const int remainder = to_write - space_to_end;
    memcpy(&buffer_[0], samples + space_to_end, remainder * sizeof(float));
    write_pos_.store(remainder, std::memory_order_release);
  }

  return to_write;
}

int AudioRingBuffer::read(float* samples, int count) {
  const int avail = available_read();
  const int to_read = std::min(count, avail);

  if (to_read > 0) {
    const int read = read_pos_.load(std::memory_order_acquire);
    const int space_to_end = capacity_ - read;

    if (to_read <= space_to_end) {
      // Read in one chunk
      memcpy(samples, &buffer_[read], to_read * sizeof(float));
      read_pos_.store((read + to_read) % capacity_, std::memory_order_release);
    } else {
      // Read in two chunks (wrap around)
      memcpy(samples, &buffer_[read], space_to_end * sizeof(float));
      const int remainder = to_read - space_to_end;
      memcpy(samples + space_to_end, &buffer_[0], remainder * sizeof(float));
      read_pos_.store(remainder, std::memory_order_release);
    }

    total_read_.fetch_add(to_read, std::memory_order_release);
  }

  // Fill remainder with silence if not enough samples available
  if (to_read < count) {
    memset(samples + to_read, 0, (count - to_read) * sizeof(float));
  }

  return to_read;
}

void AudioRingBuffer::clear() {
  write_pos_.store(0, std::memory_order_release);
  read_pos_.store(0, std::memory_order_release);
  // Note: Don't reset total_read_ - it tracks absolute playback time
  memset(buffer_, 0, sizeof(buffer_));
}