Skip to content

Commit 11e3eb0

Browse files
authored
MP4: Add support for NI STEM (#1299)
1 parent 2c01b63 commit 11e3eb0

11 files changed

Lines changed: 411 additions & 22 deletions

File tree

taglib/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ if(WITH_MP4)
194194
mp4/mp4item.h
195195
mp4/mp4properties.h
196196
mp4/mp4coverart.h
197+
mp4/mp4stem.h
197198
mp4/mp4itemfactory.h
198199
)
199200
endif()
@@ -369,6 +370,7 @@ if(WITH_MP4)
369370
mp4/mp4item.cpp
370371
mp4/mp4properties.cpp
371372
mp4/mp4coverart.cpp
373+
mp4/mp4stem.cpp
372374
mp4/mp4itemfactory.cpp
373375
)
374376
endif()

taglib/mp4/mp4atom.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ namespace {
3737
constexpr std::array containers {
3838
"moov", "udta", "mdia", "meta", "ilst",
3939
"stbl", "minf", "moof", "traf", "trak",
40-
"stsd"
40+
"stsd", "stem"
4141
};
4242
} // namespace
4343

@@ -86,6 +86,11 @@ MP4::Atom::Atom(File *file)
8686

8787
d->name = header.mid(4, 4);
8888

89+
if(d->name == "stem") {
90+
file->seek(d->length - 8, File::Current);
91+
return;
92+
}
93+
8994
for(auto c : containers) {
9095
if(d->name == c) {
9196
if(d->name == "meta") {

taglib/mp4/mp4item.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class MP4::Item::ItemPrivate
4444
StringList m_stringList;
4545
ByteVectorList m_byteVectorList;
4646
MP4::CoverArtList m_coverArtList;
47+
MP4::Stem m_stem;
4748
};
4849

4950
MP4::Item::Item() :
@@ -130,6 +131,13 @@ MP4::Item::Item(const MP4::CoverArtList &value) :
130131
d->m_coverArtList = value;
131132
}
132133

134+
MP4::Item::Item(const MP4::Stem &value) :
135+
d(std::make_shared<ItemPrivate>())
136+
{
137+
d->type = Type::Stem;
138+
d->m_stem = value;
139+
}
140+
133141
void MP4::Item::setAtomDataType(MP4::AtomDataType type)
134142
{
135143
d->atomDataType = type;
@@ -194,6 +202,12 @@ MP4::Item::toCoverArtList() const
194202
return d->m_coverArtList;
195203
}
196204

205+
MP4::Stem
206+
MP4::Item::toStem() const
207+
{
208+
return d->m_stem;
209+
}
210+
197211
bool
198212
MP4::Item::isValid() const
199213
{
@@ -234,6 +248,8 @@ bool MP4::Item::operator==(const Item &other) const
234248
return toByteVectorList() == other.toByteVectorList();
235249
case Type::CoverArtList:
236250
return toCoverArtList() == other.toCoverArtList();
251+
case Type::Stem:
252+
return toStem() == other.toStem();
237253
}
238254
}
239255
return false;

taglib/mp4/mp4item.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "tstringlist.h"
3030
#include "taglib_export.h"
3131
#include "mp4coverart.h"
32+
#include "mp4stem.h"
3233

3334
namespace TagLib {
3435
namespace MP4 {
@@ -49,7 +50,8 @@ namespace TagLib {
4950
LongLong,
5051
StringList,
5152
ByteVectorList,
52-
CoverArtList
53+
CoverArtList,
54+
Stem,
5355
};
5456

5557
struct IntPair {
@@ -80,6 +82,7 @@ namespace TagLib {
8082
Item(const StringList &value);
8183
Item(const ByteVectorList &value);
8284
Item(const CoverArtList &value);
85+
Item(const Stem &value);
8386

8487
void setAtomDataType(AtomDataType type);
8588
AtomDataType atomDataType() const;
@@ -93,6 +96,7 @@ namespace TagLib {
9396
StringList toStringList() const;
9497
ByteVectorList toByteVectorList() const;
9598
CoverArtList toCoverArtList() const;
99+
Stem toStem() const;
96100

97101
bool isValid() const;
98102

taglib/mp4/mp4itemfactory.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ std::pair<String, Item> ItemFactory::parseItem(
8787
return parseGnre(atom, data);
8888
case ItemHandlerType::Covr:
8989
return parseCovr(atom, data);
90+
case ItemHandlerType::Stem:
91+
return parseStem(atom, data);
9092
case ItemHandlerType::TextImplicit:
9193
return parseText(atom, data, -1);
9294
case ItemHandlerType::Text:
@@ -128,6 +130,8 @@ ByteVector ItemFactory::renderItem(
128130
return renderInt(name, item);
129131
case ItemHandlerType::Covr:
130132
return renderCovr(name, item);
133+
case ItemHandlerType::Stem:
134+
return renderStem(name, item);
131135
case ItemHandlerType::TextImplicit:
132136
return renderText(name, item, TypeImplicit);
133137
case ItemHandlerType::Text:
@@ -175,8 +179,8 @@ std::pair<ByteVector, Item> ItemFactory::itemFromProperty(
175179
case ItemHandlerType::TextImplicit:
176180
case ItemHandlerType::Text:
177181
return {name, values};
178-
179182
case ItemHandlerType::Covr:
183+
case ItemHandlerType::Stem:
180184
debug("MP4: Invalid item \"" + name + "\" for property");
181185
break;
182186
case ItemHandlerType::Unknown:
@@ -222,6 +226,7 @@ std::pair<String, StringList> ItemFactory::itemToProperty(
222226
return {key, item.toStringList()};
223227

224228
case ItemHandlerType::Covr:
229+
case ItemHandlerType::Stem:
225230
debug("MP4: Invalid item \"" + itemName + "\" for property");
226231
break;
227232
case ItemHandlerType::Unknown:
@@ -303,6 +308,7 @@ ItemFactory::NameHandlerMap ItemFactory::nameHandlerMap() const
303308
{"akID", ItemHandlerType::Byte},
304309
{"gnre", ItemHandlerType::Gnre},
305310
{"covr", ItemHandlerType::Covr},
311+
{"stem", ItemHandlerType::Stem},
306312
{"purl", ItemHandlerType::TextImplicit},
307313
{"egid", ItemHandlerType::TextImplicit},
308314
};
@@ -633,6 +639,12 @@ std::pair<String, Item> ItemFactory::parseCovr(
633639
};
634640
}
635641

642+
std::pair<String, Item> ItemFactory::parseStem(
643+
const MP4::Atom *atom, const ByteVector &data)
644+
{
645+
return {atom->name(), Item(Stem(data))};
646+
}
647+
636648

637649
ByteVector ItemFactory::renderAtom(
638650
const ByteVector &name, const ByteVector &data)
@@ -742,6 +754,13 @@ ByteVector ItemFactory::renderCovr(
742754
return renderAtom(name, data);
743755
}
744756

757+
ByteVector ItemFactory::renderStem(
758+
const ByteVector &name, const MP4::Item &item)
759+
{
760+
auto data = item.toStem().data();
761+
return renderAtom(name, data);
762+
}
763+
745764
ByteVector ItemFactory::renderFreeForm(
746765
const String &name, const MP4::Item &item)
747766
{

taglib/mp4/mp4itemfactory.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ namespace TagLib {
142142
Byte,
143143
Gnre,
144144
Covr,
145+
Stem,
145146
TextImplicit,
146147
Text
147148
};
@@ -214,6 +215,8 @@ namespace TagLib {
214215
const MP4::Atom *atom, const ByteVector &bytes);
215216
static std::pair<String, Item> parseCovr(
216217
const MP4::Atom *atom, const ByteVector &data);
218+
static std::pair<String, Item> parseStem(
219+
const MP4::Atom *atom, const ByteVector &data);
217220

218221
// Functions used by renderItem() to render atom data for items.
219222
static ByteVector renderAtom(
@@ -242,6 +245,8 @@ namespace TagLib {
242245
const ByteVector &name, const MP4::Item &item);
243246
static ByteVector renderCovr(
244247
const ByteVector &name, const MP4::Item &item);
248+
static ByteVector renderStem(
249+
const ByteVector &name, const MP4::Item &item);
245250

246251
private:
247252
static ItemFactory factory;

taglib/mp4/mp4stem.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**************************************************************************
2+
copyright : (C) 2026 by Antoine Colombier
3+
email : antoine@mixxx.org
4+
**************************************************************************/
5+
6+
/***************************************************************************
7+
* This library is free software; you can redistribute it and/or modify *
8+
* it under the terms of the GNU Lesser General Public License version *
9+
* 2.1 as published by the Free Software Foundation. *
10+
* *
11+
* This library is distributed in the hope that it will be useful, but *
12+
* WITHOUT ANY WARRANTY; without even the implied warranty of *
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14+
* Lesser General Public License for more details. *
15+
* *
16+
* You should have received a copy of the GNU Lesser General Public *
17+
* License along with this library; if not, write to the Free Software *
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
19+
* 02110-1301 USA *
20+
* *
21+
* Alternatively, this file is available under the Mozilla Public *
22+
* License Version 1.1. You may obtain a copy of the License at *
23+
* http://www.mozilla.org/MPL/ *
24+
***************************************************************************/
25+
26+
#include "mp4stem.h"
27+
28+
using namespace TagLib;
29+
30+
MP4::Stem::Stem(const ByteVector &data) :
31+
d(std::make_shared<StemPrivate>())
32+
{
33+
d->data = data;
34+
}
35+
36+
MP4::Stem::Stem() = default;
37+
MP4::Stem::Stem(const Stem &) = default;
38+
MP4::Stem &MP4::Stem::operator=(const Stem &) = default;
39+
40+
void
41+
MP4::Stem::swap(Stem &item) noexcept
42+
{
43+
using std::swap;
44+
45+
swap(d, item.d);
46+
}
47+
48+
MP4::Stem::~Stem() = default;
49+
50+
ByteVector
51+
MP4::Stem::data() const
52+
{
53+
return d->data;
54+
}
55+
56+
bool MP4::Stem::operator==(const Stem &other) const
57+
{
58+
return data() == other.data();
59+
}
60+
61+
bool MP4::Stem::operator!=(const Stem &other) const
62+
{
63+
return !(*this == other);
64+
}

taglib/mp4/mp4stem.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**************************************************************************
2+
copyright : (C) 2026 by Antoine Colombier
3+
email : antoine@mixxx.org
4+
**************************************************************************/
5+
6+
/***************************************************************************
7+
* This library is free software; you can redistribute it and/or modify *
8+
* it under the terms of the GNU Lesser General Public License version *
9+
* 2.1 as published by the Free Software Foundation. *
10+
* *
11+
* This library is distributed in the hope that it will be useful, but *
12+
* WITHOUT ANY WARRANTY; without even the implied warranty of *
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14+
* Lesser General Public License for more details. *
15+
* *
16+
* You should have received a copy of the GNU Lesser General Public *
17+
* License along with this library; if not, write to the Free Software *
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
19+
* 02110-1301 USA *
20+
* *
21+
* Alternatively, this file is available under the Mozilla Public *
22+
* License Version 1.1. You may obtain a copy of the License at *
23+
* http://www.mozilla.org/MPL/ *
24+
***************************************************************************/
25+
26+
#ifndef TAGLIB_MP4STEM_H
27+
#define TAGLIB_MP4STEM_H
28+
29+
#include "tbytevector.h"
30+
#include "taglib_export.h"
31+
32+
namespace TagLib::MP4 {
33+
//! STEM
34+
class StemPrivate
35+
{
36+
public:
37+
ByteVector data;
38+
};
39+
40+
class TAGLIB_EXPORT Stem
41+
{
42+
public:
43+
Stem();
44+
explicit Stem(const ByteVector &data);
45+
~Stem();
46+
47+
Stem(const Stem &item);
48+
49+
/*!
50+
* Copies the contents of \a item into this Stem.
51+
*/
52+
Stem &operator=(const Stem &item);
53+
54+
/*!
55+
* Exchanges the content of the Stem with the content of \a item.
56+
*/
57+
void swap(Stem &item) noexcept;
58+
59+
//! The Stem data
60+
ByteVector data() const;
61+
62+
/*!
63+
* Returns \c true if the Stem and \a other contain the same data.
64+
*/
65+
bool operator==(const Stem &other) const;
66+
67+
/*!
68+
* Returns \c true if the Stem and \a other have different data.
69+
*/
70+
bool operator!=(const Stem &other) const;
71+
72+
private:
73+
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
74+
std::shared_ptr<StemPrivate> d;
75+
};
76+
} // namespace TagLib::MP4
77+
78+
#endif

0 commit comments

Comments
 (0)