Skip to content

Commit

Permalink
[drwav] Add a wrapper to write wav files. Improve interleaving & alsa…
Browse files Browse the repository at this point in the history
… code
  • Loading branch information
jcelerier committed Apr 8, 2024
1 parent 800a6f9 commit f695698
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 16 deletions.
2 changes: 1 addition & 1 deletion 3rdparty/dr_libs
Submodule dr_libs updated 4 files
+13 −6 dr_flac.h
+25 −8 dr_mp3.h
+178 −31 dr_wav.h
+2 −2 tests/wav/dr_wav_common.c
36 changes: 27 additions & 9 deletions src/ossia/audio/alsa_protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class libasound
decltype(&::snd_pcm_hw_params_any) pcm_hw_params_any{};
decltype(&::snd_pcm_hw_params_sizeof) pcm_hw_params_sizeof{};
decltype(&::snd_pcm_hw_params_set_access) pcm_hw_params_set_access{};
decltype(&::snd_pcm_hw_params_get_format) pcm_hw_params_get_format{};
decltype(&::snd_pcm_hw_params_set_format) pcm_hw_params_set_format{};
decltype(&::snd_pcm_hw_params_set_channels) pcm_hw_params_set_channels{};
decltype(&::snd_pcm_hw_params_set_period_size) pcm_hw_params_set_period_size{};
Expand Down Expand Up @@ -96,6 +97,8 @@ class libasound
"snd_pcm_hw_params_sizeof");
pcm_hw_params_set_access = library.symbol<decltype(&::snd_pcm_hw_params_set_access)>(
"snd_pcm_hw_params_set_access");
pcm_hw_params_get_format = library.symbol<decltype(&::snd_pcm_hw_params_get_format)>(
"snd_pcm_hw_params_set_format");
pcm_hw_params_set_format = library.symbol<decltype(&::snd_pcm_hw_params_set_format)>(
"snd_pcm_hw_params_set_format");
pcm_hw_params_set_channels
Expand Down Expand Up @@ -192,6 +195,7 @@ class libasound
assert(pcm_hw_params_any);
assert(pcm_hw_params_sizeof);
assert(pcm_hw_params_set_access);
assert(pcm_hw_params_get_format);
assert(pcm_hw_params_set_format);
assert(pcm_hw_params_set_channels);
assert(pcm_hw_params_set_period_size);
Expand Down Expand Up @@ -251,16 +255,19 @@ snd_interleave(const float* const* in, char* out, int channels, int bs)
switch(Format)
{
case SND_PCM_FORMAT_S16_LE:
return ossia::interleave<int16_t, 16>(
return ossia::interleave<int16_t, 16, 2>(
in, reinterpret_cast<int16_t*>(out), channels, bs);
case SND_PCM_FORMAT_S24_LE:
return ossia::interleave<int32_t, 24>(
return ossia::interleave<int32_t, 24, 4>(
in, reinterpret_cast<int32_t*>(out), channels, bs);
case SND_PCM_FORMAT_S24_3LE:
return ossia::interleave<int32_t, 24, 3>(
in, reinterpret_cast<int32_t*>(out), channels, bs);
case SND_PCM_FORMAT_S32_LE:
return ossia::interleave<int32_t, 32>(
return ossia::interleave<int32_t, 32, 4>(
in, reinterpret_cast<int32_t*>(out), channels, bs);
case SND_PCM_FORMAT_FLOAT_LE:
return ossia::interleave<float, 32>(
return ossia::interleave<float, 32, 4>(
in, reinterpret_cast<float*>(out), channels, bs);
default:
return;
Expand All @@ -279,6 +286,9 @@ snd_convert(const float* const* in, char* out, int channels, int bs)
case SND_PCM_FORMAT_S24_LE:
return ossia::convert<int32_t, 24>(
in, reinterpret_cast<int32_t*>(out), channels, bs);
case SND_PCM_FORMAT_S24_3LE:
return ossia::convert<int32_t, 24>(
in, reinterpret_cast<int32_t*>(out), channels, bs);
case SND_PCM_FORMAT_S32_LE:
return ossia::convert<int32_t, 32>(
in, reinterpret_cast<int32_t*>(out), channels, bs);
Expand All @@ -296,6 +306,8 @@ static constexpr int snd_bytes_per_sample()
{
case SND_PCM_FORMAT_S16_LE:
return 2;
case SND_PCM_FORMAT_S24_3LE:
return 3;
case SND_PCM_FORMAT_S24_LE:
return 4;
case SND_PCM_FORMAT_S32_LE:
Expand Down Expand Up @@ -352,8 +364,11 @@ class alsa_engine final : public audio_engine
if(int ret = snd.pcm_hw_params_set_format(m_client, hwparams, format);
ret < 0)
{
snd.pcm_hw_params_get_format(hwparams, &format);

ossia::logger().error(
"alsa_engine: can't set format: {}", snd.strerror(ret));
"alsa_engine: can't set format: {} => got {}", snd.strerror(ret),
(int)format);
}
}
}
Expand Down Expand Up @@ -410,9 +425,9 @@ class alsa_engine final : public audio_engine
unsigned int tmp_periods{};
snd.pcm_hw_params_get_periods(hwparams, &tmp_periods, 0);

fmt::print("Device: {}\n", card_out);
fmt::print("Expected: {} : {} : {}\n", outputs, rate, bs);
fmt::print(
ossia::logger().error("Device: {}\n", card_out);
ossia::logger().error("Expected: {} : {} : {}\n", outputs, rate, bs);
ossia::logger().error(
"Got: {} : {} : {} => {} ; {}\n", effective_outputs, effective_sample_rate,
effective_buffer_size, tmp_bufsize, tmp_periods);
}
Expand Down Expand Up @@ -570,7 +585,10 @@ class alsa_engine final : public audio_engine
{
data += ret * channels * sample_size_in_bytes;
samples -= ret;
snd.pcm_start(m_client);
ret = snd.pcm_start(m_client);

if(ret < 0)
ossia::logger().error("alsa_engine: snd_pcm_start: {}", snd.strerror(ret));
}
}
return true;
Expand Down
6 changes: 4 additions & 2 deletions src/ossia/audio/drwav_handle.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#pragma once
#include <ossia-config.hpp>
#define DR_WAV_NO_STDIO
#include <ossia/dataflow/float_to_sample.hpp>

#include <dr_wav.h>

#include <ossia-config.hpp>

namespace ossia
{
#pragma pack(push, 1)
Expand Down
84 changes: 84 additions & 0 deletions src/ossia/audio/drwav_write_handle.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#pragma once
#include <ossia/dataflow/float_to_sample.hpp>

#include <boost/container/vector.hpp>

#include <dr_wav.h>

#include <ossia-config.hpp>
namespace ossia
{

struct drwav_write_handle
{
drwav_data_format format;
static constexpr auto static_cutoff = 4096 * 2;

public:
drwav_write_handle()
: impl{new drwav}
{
}
void open(std::string_view path, int channels, int rate, int bits)
{
format.container = drwav_container_riff;
format.format = DR_WAVE_FORMAT_PCM;
format.channels = channels;
format.sampleRate = rate;
format.bitsPerSample = bits;

bool ok = drwav_init_file_write(impl, path.data(), &format, nullptr);

buffer.reserve(channels * 8192 * (bits / 8));
started = ok;
}

void close()
{
if(impl)
{
if(started)
{
drwav_uninit(impl);
}
}
started = false;
}

drwav_uint64 write_pcm_frames(drwav_uint64 frames, const double* const* in)
{
this->buffer.clear();
this->buffer.resize(
frames * format.channels * (format.bitsPerSample / 8),
boost::container::default_init);

switch(format.bitsPerSample)
{
case 16:
interleave<int16_t, 16, 2>(
in, reinterpret_cast<int16_t*>(buffer.data()), format.channels, frames);

return drwav_write_raw(impl, frames * 2 * format.channels, buffer.data());
case 24:
interleave<int32_t, 24, 3>(
in, reinterpret_cast<int32_t*>(buffer.data()), format.channels, frames);

return drwav_write_raw(impl, frames * 3 * format.channels, buffer.data());
case 32:
interleave<int32_t, 32, 4>(
in, reinterpret_cast<int32_t*>(buffer.data()), format.channels, frames);

return drwav_write_raw(impl, frames * 4 * format.channels, buffer.data());
break;
}
return 0;
}

bool is_open() const noexcept { return started; }

private:
::drwav* impl{};
ossia::pod_vector<char> buffer;
bool started{};
};
}
30 changes: 26 additions & 4 deletions src/ossia/dataflow/float_to_sample.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,44 @@ constexpr float float_to_sample<float, 32>(float sample) noexcept
#define OSSIA_RESTRICT __restrict__
#endif

template <typename SampleFormat, int N>
template <typename SampleFormat, int N, int ByteIncrement, typename InputFormat>
requires(sizeof(SampleFormat) == ByteIncrement)
inline void interleave(
const float* const* OSSIA_RESTRICT in, SampleFormat* OSSIA_RESTRICT out,
const InputFormat* const* OSSIA_RESTRICT in, SampleFormat* OSSIA_RESTRICT out,
int channels, int bs)
{
for(int c = 0; c < channels; c++)
{
auto* in_channel = in[c];
for(int k = 0; k < bs; k++)
{
out[k * channels + c] = float_to_sample<SampleFormat, N>(in_channel[k]);
}
}
}

template <typename SampleFormat, int N>
template <typename SampleFormat, int N, int ByteIncrement, typename InputFormat>
requires(sizeof(SampleFormat) != ByteIncrement)
inline void interleave(
const InputFormat* const* OSSIA_RESTRICT in, SampleFormat* out, int channels, int bs)
{
for(int c = 0; c < channels; c++)
{
auto* in_channel = in[c];
for(int k = 0; k < bs; k++)
{
// Case packed 24-bit: we have to go through raw char*
char* out_raw = reinterpret_cast<char*>(out);
auto mem
= reinterpret_cast<SampleFormat*>(out_raw[(k * channels + c) * ByteIncrement]);
*mem = float_to_sample<SampleFormat, N>(in_channel[k]);
}
}
}

template <typename SampleFormat, int N, typename InputFormat>
inline void convert(
const float* const* OSSIA_RESTRICT in, SampleFormat* OSSIA_RESTRICT out,
const InputFormat* const* OSSIA_RESTRICT in, SampleFormat* OSSIA_RESTRICT out,
int channels, int bs)
{
for(int c = 0; c < channels; c++)
Expand Down

0 comments on commit f695698

Please sign in to comment.