Audacity 3.2.0
Public Member Functions | Private Attributes | List of all members
LibImportExport::Test::LibsndfileTagger Class Referencefinal

When adding tags, the allocated memory must be preserved until the file is closed. This class handles that, beside the regular file opening and closing. More...

#include <LibsndfileTagger.h>

Collaboration diagram for LibImportExport::Test::LibsndfileTagger:
[legend]

Public Member Functions

 LibsndfileTagger (double duration=0., const std::string &filename="")
 
 ~LibsndfileTagger ()
 
 operator bool () const
 
void AddAcidizerTags (const Test::AcidizerTags &acidTags)
 
void AddDistributorInfo (const std::string &distributor)
 
SNDFILE & ReopenInReadMode ()
 

Private Attributes

const std::string mFilename
 
SNDFILE * mFile
 
std::unique_ptr< uint8_t[]> mAcidData
 
std::unique_ptr< uint8_t[]> mDistributorData
 

Detailed Description

When adding tags, the allocated memory must be preserved until the file is closed. This class handles that, beside the regular file opening and closing.

Definition at line 59 of file LibsndfileTagger.h.

Constructor & Destructor Documentation

◆ LibsndfileTagger()

LibImportExport::Test::LibsndfileTagger::LibsndfileTagger ( double  duration = 0.,
const std::string &  filename = "" 
)

Definition at line 16 of file LibsndfileTagger.cpp.

17 : mFilename { filename.empty() ? std::tmpnam(nullptr) : filename }
18{
19 SF_INFO sfInfo;
20 std::memset(&sfInfo, 0, sizeof(sfInfo));
21 sfInfo.samplerate = 44100;
22 sfInfo.channels = 1;
23 sfInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
24 sfInfo.sections = 1;
25 sfInfo.seekable = 1;
26 mFile = sf_open(mFilename.c_str(), SFM_WRITE, &sfInfo);
27 assert(mFile != nullptr);
28 if (duration > 0)
29 {
30 // Write zeros using sf_write_float
31 sfInfo.frames =
32 static_cast<sf_count_t>(std::round(duration * sfInfo.samplerate));
33 const auto numItems = sfInfo.channels * sfInfo.frames;
34 std::unique_ptr<short[]> zeros { new short[numItems] };
35 std::fill(zeros.get(), zeros.get() + numItems, 0);
36 const auto written = sf_write_short(mFile, zeros.get(), numItems);
37 if (written != numItems)
38 throw std::runtime_error("Failed to write audio to file");
39 }
40}
static wxCharBuffer mFilename
Definition: SelectFile.cpp:38
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
Definition: fast_float.h:2512

References mFile, mFilename, and fast_float::round().

Here is the call graph for this function:

◆ ~LibsndfileTagger()

LibImportExport::Test::LibsndfileTagger::~LibsndfileTagger ( )

Definition at line 42 of file LibsndfileTagger.cpp.

43{
44 sf_close(mFile);
45}

References mFile.

Member Function Documentation

◆ AddAcidizerTags()

void LibImportExport::Test::LibsndfileTagger::AddAcidizerTags ( const Test::AcidizerTags acidTags)

Definition at line 68 of file LibsndfileTagger.cpp.

69{
70 // Adapted from the ACID chunk readout code in libsndfile and its comment:
71 // clang-format off
72 /*
73 ** The acid chunk goes a little something like this:
74 **
75 ** 4 bytes 'acid'
76 ** 4 bytes (int) length of chunk starting at next byte
77 **
78 ** 4 bytes (int) type of file:
79 ** this appears to be a bit mask,however some combinations
80 ** are probably impossible and/or qualified as "errors"
81 **
82 ** 0x01 On: One Shot Off: Loop
83 ** 0x02 On: Root note is Set Off: No root
84 ** 0x04 On: Stretch is On, Off: Strech is OFF
85 ** 0x08 On: Disk Based Off: Ram based
86 ** 0x10 On: ?????????? Off: ????????? (Acidizer puts that ON)
87 **
88 ** 2 bytes (short) root note
89 ** if type 0x10 is OFF : [C,C#,(...),B] -> [0x30 to 0x3B]
90 ** if type 0x10 is ON : [C,C#,(...),B] -> [0x3C to 0x47]
91 ** (both types fit on same MIDI pitch albeit different octaves, so who cares)
92 **
93 ** 2 bytes (short) ??? always set to 0x8000
94 ** 4 bytes (float) ??? seems to be always 0
95 ** 4 bytes (int) number of beats
96 ** 2 bytes (short) meter denominator //always 4 in SF/ACID
97 ** 2 bytes (short) meter numerator //always 4 in SF/ACID
98 ** //are we sure about the order?? usually its num/denom
99 ** 4 bytes (float) tempo
100 **
101 */
102 // clang-format on
103
104 SF_LOOP_INFO loopInfo {};
105 loopInfo.bpm = acidTags.bpm.value_or(0.);
106 loopInfo.loop_mode = acidTags.isOneShot ? SF_LOOP_NONE : SF_LOOP_FORWARD;
107
108 SF_CHUNK_INFO chunk;
109 std::memset(&chunk, 0, sizeof(chunk));
110 std::snprintf(chunk.id, sizeof(chunk.id), "acid");
111 chunk.id_size = 4;
112 // All sizes listed above except the first two:
113 chunk.datalen = 4 + 2 + 2 + 4 + 4 + 2 + 2 + 4;
114 mAcidData = std::make_unique<uint8_t[]>(chunk.datalen);
115 std::memset(mAcidData.get(), 0, chunk.datalen);
116 chunk.data = mAcidData.get();
117
118 // The type has 4 bytes, of which we may only set the 1st bit to 1 if the
119 // loop is one-shot:
120 if (acidTags.isOneShot)
121 {
122 auto type = reinterpret_cast<uint32_t*>(mAcidData.get());
123 *type |= 0x00000001;
124 }
125 else if (acidTags.beats.has_value())
126 {
127 auto numBeats = reinterpret_cast<uint32_t*>(mAcidData.get() + 12);
128 *numBeats = *acidTags.beats;
129 }
130 else
131 {
132 assert(acidTags.bpm.has_value());
133 auto tempo = reinterpret_cast<float*>(mAcidData.get() + 20);
134 *tempo = *acidTags.bpm;
135 }
136
137 // Set the meter denominator 2 bytes to 4:
138 auto numerator = reinterpret_cast<uint16_t*>(mAcidData.get() + 16);
139 *numerator |= 0x0004;
140 auto denominator = reinterpret_cast<uint16_t*>(mAcidData.get() + 18);
141 *denominator |= 0x0004;
142
143 const auto result = sf_set_chunk(mFile, &chunk);
144 assert(result == SF_ERR_NO_ERROR);
145}
std::unique_ptr< uint8_t[]> mAcidData

References LibImportExport::Test::AcidizerTags::beats, LibFileFormats::AcidizerTags::bpm, LibFileFormats::AcidizerTags::isOneShot, mAcidData, and mFile.

Referenced by LibImportExport::TEST_CASE().

Here is the caller graph for this function:

◆ AddDistributorInfo()

void LibImportExport::Test::LibsndfileTagger::AddDistributorInfo ( const std::string &  distributor)

Definition at line 147 of file LibsndfileTagger.cpp.

148{
149 const uint32_t distributorSize = distributor.size();
150 // Why we didn't use `auto` the line above:
151 static_assert(sizeof(distributorSize) == 4);
152 SF_CHUNK_INFO chunk;
153 std::snprintf(chunk.id, sizeof(chunk.id), "LIST");
154 chunk.id_size = 4;
155 constexpr std::array<char, 4> listTypeID = { 'I', 'N', 'F', 'O' };
156 constexpr std::array<char, 4> distributorTypeID = { 'I', 'D', 'S', 'T' };
157 chunk.datalen = sizeof(listTypeID) + sizeof(distributorTypeID) +
158 sizeof(distributorSize) + distributorSize;
159 // A trick taken from libsndfile's source code, probably to ensure that
160 // the rest of the data stays word-aligned:
161 while (chunk.datalen & 3)
162 ++chunk.datalen;
163 mDistributorData = std::make_unique<uint8_t[]>(chunk.datalen);
164 chunk.data = mDistributorData.get();
165 auto data = mDistributorData.get();
166 std::memset(chunk.data, 0, chunk.datalen);
167 auto pos = 0;
168
169 std::memcpy(data + pos, listTypeID.data(), sizeof(listTypeID));
170
171 pos += sizeof(listTypeID);
172 std::memcpy(data + pos, distributorTypeID.data(), sizeof(distributorTypeID));
173
174 pos += sizeof(distributorTypeID);
175 std::memcpy(data + pos, &distributorSize, sizeof(distributorSize));
176
177 pos += sizeof(distributorSize);
178 std::memcpy(data + pos, distributor.data(), distributorSize);
179
180 const auto result = sf_set_chunk(mFile, &chunk);
181 assert(result == SF_ERR_NO_ERROR);
182}
std::unique_ptr< uint8_t[]> mDistributorData

References mDistributorData, and mFile.

Referenced by LibImportExport::TEST_CASE().

Here is the caller graph for this function:

◆ operator bool()

LibImportExport::Test::LibsndfileTagger::operator bool ( ) const

Definition at line 47 of file LibsndfileTagger.cpp.

48{
49 return mFile != nullptr;
50}

◆ ReopenInReadMode()

SNDFILE & LibImportExport::Test::LibsndfileTagger::ReopenInReadMode ( )

Definition at line 52 of file LibsndfileTagger.cpp.

53{
54 if (!mFile)
55 throw std::runtime_error("File is not open");
56
57 sf_close(mFile);
58 mDistributorData.reset();
59 mAcidData.reset();
60
61 SF_INFO sfInfo;
62 mFile = sf_open(mFilename.c_str(), SFM_READ, &sfInfo);
63 if (!mFile)
64 throw std::runtime_error("Failed to re-open file");
65 return *mFile;
66}

References mAcidData, mDistributorData, mFile, and mFilename.

Referenced by LibImportExport::TEST_CASE().

Here is the caller graph for this function:

Member Data Documentation

◆ mAcidData

std::unique_ptr<uint8_t[]> LibImportExport::Test::LibsndfileTagger::mAcidData
private

Definition at line 73 of file LibsndfileTagger.h.

Referenced by AddAcidizerTags(), and ReopenInReadMode().

◆ mDistributorData

std::unique_ptr<uint8_t[]> LibImportExport::Test::LibsndfileTagger::mDistributorData
private

Definition at line 74 of file LibsndfileTagger.h.

Referenced by AddDistributorInfo(), and ReopenInReadMode().

◆ mFile

SNDFILE* LibImportExport::Test::LibsndfileTagger::mFile
private

◆ mFilename

const std::string LibImportExport::Test::LibsndfileTagger::mFilename
private

Definition at line 71 of file LibsndfileTagger.h.

Referenced by LibsndfileTagger(), and ReopenInReadMode().


The documentation for this class was generated from the following files: