summaryrefslogtreecommitdiff
path: root/src/tangara/audio/processor.hpp
blob: 52bace955cee1ac53525caf58d60cf8ceda58502 (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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * Copyright 2023 jacqueline <me@jacqueline.id.au>
 *
 * SPDX-License-Identifier: GPL-3.0-only
 */

#pragma once

#include <stdint.h>
#include <cstdint>
#include <functional>
#include <list>
#include <memory>

#include "audio/audio_events.hpp"
#include "audio/audio_sink.hpp"
#include "audio/audio_source.hpp"
#include "audio/resample.hpp"
#include "codec.hpp"
#include "drivers/pcm_buffer.hpp"
#include "sample.hpp"

namespace audio {

/* Utility for managing buffering samples between digital filters. */
class Buffer {
 public:
  Buffer(std::span<sample::Sample> storage);
  Buffer();
  ~Buffer();

  /* Returns a span of the unused space within the buffer. */
  auto writeAcquire() -> std::span<sample::Sample>;
  /* Signals how many samples were just added to the writeAcquire span. */
  auto writeCommit(size_t) -> void;

  /* Returns a span of the samples stored within the buffer. */
  auto readAcquire() -> std::span<sample::Sample>;
  /* Signals how many samples from the readAcquire span were consumed. */
  auto readCommit(size_t) -> void;

  auto isEmpty() -> bool;
  auto clear() -> void;

  Buffer(const Buffer&) = delete;
  Buffer& operator=(const Buffer&) = delete;

 private:
  sample::Sample* storage_;
  std::span<sample::Sample> buffer_;
  std::span<sample::Sample> samples_in_buffer_;
};

/*
 * Handle to a persistent task that converts samples between formats (sample
 * rate, channels, bits per sample), in order to put samples in the preferred
 * format of the current output device. The resulting samples are forwarded
 * to the output device's sink stream.
 */
class SampleProcessor {
 public:
  SampleProcessor(drivers::PcmBuffer& sink);
  ~SampleProcessor();

  auto SetOutput(std::shared_ptr<IAudioOutput>) -> void;

  /*
   * Signals to the sample processor that a new discrete stream of audio is now
   * being sent. This will typically represent a new track being played.
   */
  auto beginStream(std::shared_ptr<TrackInfo>) -> void;

  /*
   * Sends a span of PCM samples to the processor. Returns a subspan of the
   * given span containing samples that were not able to be sent during this
   * call, e.g. because of congestion downstream from the processor.
   */
  auto continueStream(std::span<sample::Sample>) -> std::span<sample::Sample>;

  /*
   * Signals to the sample processor that the current stream is ending. This
   * can either be because the stream has naturally finished, or because it is
   * being interrupted.
   * If `cancelled` is false, the sample processor will ensure all previous
   * samples are processed and sent before communicating the end of the stream
   * onwards. If `cancelled` is true, any samples from the current stream that
   * have not yet been played will be discarded.
   */
  auto endStream(bool cancelled) -> void;

  SampleProcessor(const SampleProcessor&) = delete;
  SampleProcessor& operator=(const SampleProcessor&) = delete;

 private:
  auto Main() -> void;

  auto handleBeginStream(std::shared_ptr<TrackInfo>) -> void;
  auto handleEndStream(bool cancel) -> void;

  auto processSamples(bool finalise) -> bool;

  auto hasPendingWork() -> bool;
  auto flushOutputBuffer() -> bool;

  struct Args {
    std::shared_ptr<TrackInfo>* track;
    size_t samples_available;
    bool is_end_of_stream;
    bool clear_buffers;
  };
  QueueHandle_t commands_;
  std::list<Args> pending_commands_;

  auto discardCommand(Args& command) -> void;

  StreamBufferHandle_t source_;
  drivers::PcmBuffer& sink_;

  Buffer input_buffer_;
  Buffer resampled_buffer_;
  Buffer output_buffer_;

  std::unique_ptr<Resampler> resampler_;
  bool double_samples_;

  std::shared_ptr<IAudioOutput> output_;
  size_t unprocessed_samples_;
};

}  // namespace audio