OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
write_loop_wav.hpp
1#pragma once
2#include <dr_wav.h>
3
4#include <algorithm>
5#include <cstdint>
6#include <filesystem>
7#include <fstream>
8#include <string_view>
9#include <vector>
10
11namespace ossia
12{
17{
19 double tempo{};
21 int beats{};
22 int meter_numerator{4};
23 int meter_denominator{4};
25 bool loop{true};
26};
27
33inline bool write_loop_wav(
34 std::string_view path_utf8, int channels, int rate, int64_t frames,
35 const float* const* data, const wav_loop_info& info)
36{
37 if(channels <= 0 || rate <= 0 || frames < 0 || path_utf8.empty())
38 return false;
39
40 const std::filesystem::path path{std::u8string_view{
41 reinterpret_cast<const char8_t*>(path_utf8.data()), path_utf8.size()}};
42 std::ofstream file{path, std::ios::binary | std::ios::trunc};
43 if(!file)
44 return false;
45
46 auto fail = [&] {
47 file.close();
48 std::error_code ec;
49 std::filesystem::remove(path, ec);
50 return false;
51 };
52
53 // RIFF sizes are 32 bits; the margin covers the header and metadata chunks.
54 constexpr uint64_t riff_max_data = 0xFFFFFFFFull - 65536;
55 const uint64_t data_bytes = uint64_t(frames) * uint64_t(channels) * sizeof(float);
56
57 drwav_data_format format{};
58 format.container
59 = data_bytes > riff_max_data ? drwav_container_rf64 : drwav_container_riff;
60 format.format = DR_WAVE_FORMAT_IEEE_FLOAT;
61 format.channels = channels;
62 format.sampleRate = rate;
63 format.bitsPerSample = 32;
64
65 drwav_metadata metadata[2]{};
66 drwav_uint32 metadata_count = 0;
67
68 // The sampler chunk counts in 32 bits even in RF64: a loop it cannot end
69 // where the file ends is left out rather than written wrong.
70 drwav_smpl_loop sampler_loop{};
71 if(info.loop && frames > 0 && uint64_t(frames - 1) <= 0xFFFFFFFFull)
72 {
73 sampler_loop.type = drwav_smpl_loop_type_forward;
74 sampler_loop.firstSampleOffset = 0;
75 sampler_loop.lastSampleOffset = drwav_uint32(frames - 1);
76
77 auto& smpl = metadata[metadata_count++];
78 smpl.type = drwav_metadata_type_smpl;
79 smpl.data.smpl.samplePeriodNanoseconds = drwav_uint32(1e9 / rate);
80 smpl.data.smpl.midiUnityNote = 60;
81 smpl.data.smpl.sampleLoopCount = 1;
82 smpl.data.smpl.pLoops = &sampler_loop;
83 }
84
85 if(info.tempo > 0.)
86 {
87 auto& acid = metadata[metadata_count++];
88 acid.type = drwav_metadata_type_acid;
89 acid.data.acid.flags = info.loop ? drwav_acid_flag_stretch : drwav_acid_flag_one_shot;
90 acid.data.acid.midiUnityNote = 60;
91 acid.data.acid.reserved1 = 0x80;
92 acid.data.acid.numBeats = drwav_uint32(info.beats > 0 ? info.beats : 0);
93 acid.data.acid.meterNumerator = drwav_uint16(info.meter_numerator);
94 acid.data.acid.meterDenominator = drwav_uint16(info.meter_denominator);
95 acid.data.acid.tempo = float(info.tempo);
96 }
97
98 auto on_write = [](void* user, const void* src, size_t bytes) -> size_t {
99 auto& f = *static_cast<std::ofstream*>(user);
100 f.write(static_cast<const char*>(src), std::streamsize(bytes));
101 return f ? bytes : 0;
102 };
103 auto on_seek = [](void* user, int offset, drwav_seek_origin origin) -> drwav_bool32 {
104 auto& f = *static_cast<std::ofstream*>(user);
105 switch(origin)
106 {
107 case DRWAV_SEEK_SET:
108 f.seekp(offset, std::ios::beg);
109 break;
110 case DRWAV_SEEK_CUR:
111 f.seekp(offset, std::ios::cur);
112 break;
113 default:
114 f.seekp(offset, std::ios::end);
115 break;
116 }
117 return bool(f);
118 };
119
120 drwav wav{};
121 if(!drwav_init_write_with_metadata(
122 &wav, &format, on_write, on_seek, &file, nullptr,
123 metadata_count > 0 ? metadata : nullptr, metadata_count))
124 return fail();
125
126 constexpr int64_t chunk = 4096;
127 std::vector<float> interleaved(std::size_t(chunk * channels));
128 int64_t written = 0;
129 while(written < frames)
130 {
131 const int64_t n = std::min(chunk, frames - written);
132 for(int64_t i = 0; i < n; i++)
133 for(int c = 0; c < channels; c++)
134 interleaved[std::size_t(i * channels + c)] = data[c][written + i];
135 if(drwav_write_pcm_frames(&wav, drwav_uint64(n), interleaved.data()) != drwav_uint64(n))
136 break;
137 written += n;
138 }
139
140 const bool finalized = drwav_uninit(&wav) == DRWAV_SUCCESS;
141 file.flush();
142 if(written != frames || !finalized || !file)
143 return fail();
144 file.close();
145 if(!file)
146 return fail();
147 return true;
148}
149}
Definition git_info.h:7
bool write_loop_wav(std::string_view path_utf8, int channels, int rate, int64_t frames, const float *const *data, const wav_loop_info &info)
Writes frames frames of 32-bit float audio, one pointer per channel, to a WAV file with the loop meta...
Definition write_loop_wav.hpp:33
What a host needs to play a file back as a loop in time: the ACID chunk (tempo, meter,...
Definition write_loop_wav.hpp:17
double tempo
Beats per minute of the material; zero writes no tempo.
Definition write_loop_wav.hpp:19
bool loop
A one shot is played once; a loop repeats.
Definition write_loop_wav.hpp:25
int beats
Length in beats of the meter's denominator.
Definition write_loop_wav.hpp:21