Audacity 3.2.0
GetAcidizerTags.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 GetAcidizerTags.cpp
7
8 Matthieu Hodgkinson
9
10**********************************************************************/
11#include "GetAcidizerTags.h"
12#include "AcidizerTags.h"
13
14#include <algorithm>
15#include <array>
16#include <cstring> // memset
17#include <memory>
18
20{
21std::optional<LibFileFormats::AcidizerTags> GetAcidizerTags(
22 SNDFILE& file, const std::vector<std::string>& trustedDistributors)
23{
24 SF_LOOP_INFO loopInfo;
25 if (
26 sf_command(&file, SFC_GET_LOOP_INFO, &loopInfo, sizeof(loopInfo)) ==
27 SF_FALSE)
28 return {};
29
30 if (
31 loopInfo.loop_mode == SF_LOOP_BACKWARD ||
32 loopInfo.loop_mode == SF_LOOP_ALTERNATING)
33 // Don't know what that is:
34 return {};
35
36 if (loopInfo.loop_mode == SF_LOOP_NONE)
38
39 if (loopInfo.num_beats != 0)
40 {
41 // Forward loop with number of beats set: all files like these I have seen
42 // so far were correctly tagged.
43 SF_INFO info;
44 std::memset(&info, 0, sizeof(info));
45 sf_command(&file, SFC_GET_CURRENT_SF_INFO, &info, sizeof(info));
46 if (info.samplerate == 0 || info.frames == 0)
47 return {};
48 const auto duration = 1. * info.frames / info.samplerate;
49 return LibFileFormats::AcidizerTags::Loop { 60. * loopInfo.num_beats /
50 duration };
51 }
52
53 // There is loop info, but in some unexpected combination. We don't trust it
54 // unless it is from a trusted distributor.
55 SF_CHUNK_INFO info;
56 constexpr std::array<char, 4> listId = { 'L', 'I', 'S', 'T' };
57 std::copy(listId.begin(), listId.end(), info.id);
58 std::fill(info.id + sizeof(listId), info.id + sizeof(info.id), '\0');
59 info.id_size = sizeof(listId);
60 auto chunkIt = sf_get_chunk_iterator(&file, &info);
61 while (chunkIt)
62 {
63 if (sf_get_chunk_size(chunkIt, &info) != SF_ERR_NO_ERROR)
64 break;
65 constexpr std::array<char, 4> INFO = { 'I', 'N', 'F', 'O' };
66 constexpr std::array<char, 4> IDST = { 'I', 'D', 'S', 'T' };
67 // Another 4 bytes after INFO and IDST that indicates the size of the data
68 constexpr auto dataPos = sizeof(INFO) + sizeof(IDST) + 4;
69 if (info.datalen < dataPos)
70 // Not the expected data
71 continue;
72 const auto chars = std::make_unique<char[]>(info.datalen);
73 info.data = chars.get();
74 if (sf_get_chunk_data(chunkIt, &info) != SF_ERR_NO_ERROR)
75 break;
76 chunkIt = sf_next_chunk_iterator(chunkIt);
77
78 auto pos = 0;
79 const auto firstFour =
80 std::string { chars.get() + pos, chars.get() + pos + sizeof(INFO) };
81 if (firstFour != std::string { INFO.data(), INFO.size() })
82 continue;
83
84 pos += sizeof(INFO);
85 const auto nextFour =
86 std::string { chars.get() + pos, chars.get() + pos + sizeof(IDST) };
87 if (nextFour != std::string { IDST.data(), IDST.size() })
88 continue;
89
90 // Ignore trailing nulls, which could be the result of byte-padding for
91 // word alignment:
92 const auto charsEnd = std::find_if(
93 chars.get() + dataPos, chars.get() + info.datalen,
94 [](const char c) { return c == '\0'; });
95 const auto distributor = std::string { chars.get() + 12, charsEnd };
96 const auto isTrusted =
97 std::find(
98 trustedDistributors.begin(), trustedDistributors.end(),
99 distributor) != trustedDistributors.end();
100 if (isTrusted)
101 // Later we may want to get the key, too, but for now we're only
102 // interested in BPM.
103 return LibFileFormats::AcidizerTags::Loop { loopInfo.bpm };
104 }
105
106 // No luck:
107 return {};
108}
109} // namespace LibImportExport
std::optional< LibFileFormats::AcidizerTags > GetAcidizerTags(SNDFILE &file, const std::vector< std::string > &trustedDistributors)
Get the Acidizer tags from a file if from a trusted distributor.
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40