tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cubeb_audio_dump.cpp (5627B)


      1 /*
      2 * Copyright © 2023 Mozilla Foundation
      3 *
      4 * This program is made available under an ISC-style license.  See the
      5 * accompanying file LICENSE for details.
      6 */
      7 
      8 #define NOMINMAX
      9 
     10 #include "cubeb_audio_dump.h"
     11 #include "cubeb/cubeb.h"
     12 #include "cubeb_ringbuffer.h"
     13 #include <chrono>
     14 #include <limits>
     15 #include <thread>
     16 #include <vector>
     17 
     18 using std::thread;
     19 using std::vector;
     20 
     21 uint32_t
     22 bytes_per_sample(cubeb_stream_params params)
     23 {
     24  switch (params.format) {
     25  case CUBEB_SAMPLE_S16LE:
     26  case CUBEB_SAMPLE_S16BE:
     27    return sizeof(int16_t);
     28  case CUBEB_SAMPLE_FLOAT32LE:
     29  case CUBEB_SAMPLE_FLOAT32BE:
     30    return sizeof(float);
     31  };
     32 }
     33 
     34 struct cubeb_audio_dump_stream {
     35 public:
     36  explicit cubeb_audio_dump_stream(cubeb_stream_params params)
     37      : sample_size(bytes_per_sample(params)),
     38        ringbuffer(
     39            static_cast<int>(params.rate * params.channels * sample_size))
     40  {
     41  }
     42 
     43  int open(const char * name)
     44  {
     45    file = fopen(name, "wb");
     46    if (!file) {
     47      return CUBEB_ERROR;
     48    }
     49    return CUBEB_OK;
     50  }
     51  int close()
     52  {
     53    if (fclose(file)) {
     54      return CUBEB_ERROR;
     55    }
     56    return CUBEB_OK;
     57  }
     58 
     59  // Directly write to the file. Useful to write the header.
     60  size_t write(uint8_t * data, uint32_t count)
     61  {
     62    return fwrite(data, count, 1, file);
     63  }
     64 
     65  size_t write_all()
     66  {
     67    size_t written = 0;
     68    const int buf_sz = 16 * 1024;
     69    uint8_t buf[buf_sz];
     70    while (int rv = ringbuffer.dequeue(buf, buf_sz)) {
     71      written += fwrite(buf, rv, 1, file);
     72    }
     73    return written;
     74  }
     75  int dump(void * samples, uint32_t count)
     76  {
     77    int bytes = static_cast<int>(count * sample_size);
     78    int rv = ringbuffer.enqueue(static_cast<uint8_t *>(samples), bytes);
     79    return rv == bytes;
     80  }
     81 
     82 private:
     83  uint32_t sample_size;
     84  FILE * file{};
     85  lock_free_queue<uint8_t> ringbuffer;
     86 };
     87 
     88 struct cubeb_audio_dump_session {
     89 public:
     90  cubeb_audio_dump_session() = default;
     91  ~cubeb_audio_dump_session()
     92  {
     93    assert(streams.empty());
     94    session_thread.join();
     95  }
     96  cubeb_audio_dump_session(const cubeb_audio_dump_session &) = delete;
     97  cubeb_audio_dump_session &
     98  operator=(const cubeb_audio_dump_session &) = delete;
     99  cubeb_audio_dump_session & operator=(cubeb_audio_dump_session &&) = delete;
    100 
    101  cubeb_audio_dump_stream_t create_stream(cubeb_stream_params params,
    102                                          const char * name)
    103  {
    104    if (running) {
    105      return nullptr;
    106    }
    107    auto * stream = new cubeb_audio_dump_stream(params);
    108    streams.push_back(stream);
    109    int rv = stream->open(name);
    110    if (rv != CUBEB_OK) {
    111      delete stream;
    112      return nullptr;
    113    }
    114 
    115    struct riff_header {
    116      char chunk_id[4] = {'R', 'I', 'F', 'F'};
    117      int32_t chunk_size = 0;
    118      char format[4] = {'W', 'A', 'V', 'E'};
    119 
    120      char subchunk_id_1[4] = {'f', 'm', 't', 0x20};
    121      int32_t subchunk_1_size = 16;
    122      int16_t audio_format{};
    123      int16_t num_channels{};
    124      int32_t sample_rate{};
    125      int32_t byte_rate{};
    126      int16_t block_align{};
    127      int16_t bits_per_sample{};
    128 
    129      char subchunk_id_2[4] = {'d', 'a', 't', 'a'};
    130      int32_t subchunkd_2_size = std::numeric_limits<int32_t>::max();
    131    };
    132 
    133    riff_header header;
    134    // 1 is integer PCM, 3 is float PCM
    135    header.audio_format = bytes_per_sample(params) == 2 ? 1 : 3;
    136    header.num_channels = params.channels;
    137    header.sample_rate = params.rate;
    138    header.byte_rate = bytes_per_sample(params) * params.rate * params.channels;
    139    header.block_align = params.channels * bytes_per_sample(params);
    140    header.bits_per_sample = bytes_per_sample(params) * 8;
    141 
    142    stream->write(reinterpret_cast<uint8_t *>(&header), sizeof(riff_header));
    143 
    144    return stream;
    145  }
    146  int delete_stream(cubeb_audio_dump_stream * stream)
    147  {
    148    assert(!running);
    149    stream->close();
    150    streams.erase(std::remove(streams.begin(), streams.end(), stream),
    151                  streams.end());
    152    delete stream;
    153    return CUBEB_OK;
    154  }
    155  int start()
    156  {
    157    assert(!running);
    158    running = true;
    159    session_thread = std::thread([this] {
    160      while (running) {
    161        for (auto * stream : streams) {
    162          stream->write_all();
    163        }
    164        const int DUMP_INTERVAL = 10;
    165        std::this_thread::sleep_for(std::chrono::milliseconds(DUMP_INTERVAL));
    166      }
    167    });
    168    return CUBEB_OK;
    169  }
    170  int stop()
    171  {
    172    assert(running);
    173    running = false;
    174    return CUBEB_OK;
    175  }
    176 
    177 private:
    178  thread session_thread;
    179  vector<cubeb_audio_dump_stream_t> streams{};
    180  std::atomic<bool> running = false;
    181 };
    182 
    183 int
    184 cubeb_audio_dump_init(cubeb_audio_dump_session_t * session)
    185 {
    186  *session = new cubeb_audio_dump_session;
    187  return CUBEB_OK;
    188 }
    189 
    190 int
    191 cubeb_audio_dump_shutdown(cubeb_audio_dump_session_t session)
    192 {
    193  delete session;
    194  return CUBEB_OK;
    195 }
    196 
    197 int
    198 cubeb_audio_dump_stream_init(cubeb_audio_dump_session_t session,
    199                             cubeb_audio_dump_stream_t * stream,
    200                             cubeb_stream_params stream_params,
    201                             const char * name)
    202 {
    203  *stream = session->create_stream(stream_params, name);
    204  return CUBEB_OK;
    205 }
    206 
    207 int
    208 cubeb_audio_dump_stream_shutdown(cubeb_audio_dump_session_t session,
    209                                 cubeb_audio_dump_stream_t stream)
    210 {
    211  return session->delete_stream(stream);
    212 }
    213 
    214 int
    215 cubeb_audio_dump_start(cubeb_audio_dump_session_t session)
    216 {
    217  return session->start();
    218 }
    219 
    220 int
    221 cubeb_audio_dump_stop(cubeb_audio_dump_session_t session)
    222 {
    223  return session->stop();
    224 }
    225 
    226 int
    227 cubeb_audio_dump_write(cubeb_audio_dump_stream_t stream, void * audio_samples,
    228                       uint32_t count)
    229 {
    230  stream->dump(audio_samples, count);
    231  return CUBEB_OK;
    232 }