Skip to content

Commit 4901b93

Browse files
committed
Release v0.2.0
- Bump version to 0.2.0 - Add ID3v2.2 support, unsynchronization, frame flags, FLAC MD5 - Add picture support for WAV and AIFF - Add SaveToFile(path) convenience overloads - Update test count from 1528 to 1658 (+130 tests)
1 parent e6ca522 commit 4901b93

File tree

12 files changed

+89
-67
lines changed

12 files changed

+89
-67
lines changed

CHANGELOG.md

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,47 @@ All notable changes to TagLibSharp2 will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## [0.2.0] - 2025-12-29
99

1010
### Added
1111

12+
#### ID3v2.2 Legacy Support
13+
- Full ID3v2.2 (3-character frame ID) parsing support
14+
- Complete v2.2 → v2.3/v2.4 frame ID mapping (66 mappings)
15+
- Proper 3-byte big-endian size handling for v2.2 frames
16+
17+
#### ID3v2 Unsynchronization
18+
- Global unsynchronization support (tag-level, v2.3/v2.4)
19+
- Per-frame unsynchronization support (v2.4 frame flag)
20+
- Proper 0xFF 0x00 byte sequence removal during parsing
21+
- Two-pass algorithm for efficient memory usage
22+
23+
#### ID3v2 Frame Flags Processing
24+
- Compression support with zlib decompression (v2.3 and v2.4)
25+
- Grouping identity flag handling (skips group ID byte)
26+
- Data length indicator parsing (v2.4 syncsafe 4-byte prefix)
27+
- Encryption flag detection (content preserved as-is)
28+
29+
#### FLAC MD5 Audio Signature
30+
- `FlacFile.AudioMd5Signature` property for 128-bit MD5 hash of unencoded audio
31+
- `FlacFile.AudioMd5SignatureHex` for hex string representation
32+
- `FlacFile.HasAudioMd5Signature` to detect if encoder computed the hash
33+
- Essential for bit-perfect archive verification
34+
35+
#### Picture Support for WAV and AIFF
36+
- `WavFile.Pictures` and `WavFile.HasPictures` via embedded ID3v2 tag
37+
- `AiffFile.Pictures` and `AiffFile.HasPictures` via embedded ID3v2 tag
38+
- `CoverArt` convenience property for primary album art
39+
- Full PictureType support (FrontCover, BackCover, etc.)
40+
41+
#### SaveToFile Convenience Overloads
42+
- `Mp3File.SaveToFile(path)` - saves to specified path
43+
- `FlacFile.SaveToFile(path)` - saves to specified path
44+
- `OggVorbisFile.SaveToFile(path)` - saves to specified path
45+
- `WavFile.SaveToFile(path)` - saves to specified path
46+
- `AiffFile.SaveToFile(path)` - saves to specified path
47+
- All with async variants accepting CancellationToken
48+
1249
#### AIFF Write Support
1350
- `AiffFile.Render` for serializing AIFF files to binary data
1451
- `AiffFile.SaveToFile` and `SaveToFileAsync` for atomic file saves
@@ -64,7 +101,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
64101
- Defense-in-depth checks added to AiffChunk, RiffChunk, and RiffInfoTag
65102

66103
### Changed
67-
- Test count increased from 1528 to 1564
104+
- Test count increased from 1528 to 1658 (+130 tests)
68105

69106
## [0.1.0] - 2025-12-26
70107

@@ -226,4 +263,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
226263
### Changed
227264
- `BinaryData(byte[])` constructor now copies the array to ensure true immutability
228265

266+
[0.2.0]: https://github.com/decriptor/TagLibSharp2/releases/tag/v0.2.0
229267
[0.1.0]: https://github.com/decriptor/TagLibSharp2/releases/tag/v0.1.0

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<Copyright>Copyright (c) 2025 Stephen Shaw and contributors</Copyright>
99

1010
<!-- Version: Set via CI or use default for local builds -->
11-
<ReleaseVersion Condition="'$(ReleaseVersion)' == ''">0.1.0</ReleaseVersion>
11+
<ReleaseVersion Condition="'$(ReleaseVersion)' == ''">0.2.0</ReleaseVersion>
1212
<VersionSuffix Condition="'$(VersionSuffix)' == '' And '$(CI)' == ''">dev</VersionSuffix>
1313

1414
<!-- Repository Info -->

src/TagLibSharp2/Aiff/AiffFile.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,13 @@ bool Parse (BinaryData data)
178178
void ProcessChunk (AiffChunk chunk)
179179
{
180180
switch (chunk.FourCC) {
181-
case "COMM":
182-
ParseCommChunk (chunk);
183-
break;
184-
case "ID3 ":
185-
case "ID3":
186-
ParseId3Chunk (chunk);
187-
break;
181+
case "COMM":
182+
ParseCommChunk (chunk);
183+
break;
184+
case "ID3 ":
185+
case "ID3":
186+
ParseId3Chunk (chunk);
187+
break;
188188
}
189189
}
190190

src/TagLibSharp2/Id3/Id3v2/Id3v2Tag.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2383,7 +2383,7 @@ static ReadOnlySpan<byte> ProcessFrameFlags (ReadOnlySpan<byte> content, byte fo
23832383
if (content.Length < offset + 4)
23842384
return content;
23852385
decompressedSize = (content[offset] << 24) | (content[offset + 1] << 16) |
2386-
(content[offset + 2] << 8) | content[offset + 3];
2386+
(content[offset + 2] << 8) | content[offset + 3];
23872387
offset += 4;
23882388
}
23892389

@@ -2413,9 +2413,9 @@ static ReadOnlySpan<byte> ProcessFrameFlags (ReadOnlySpan<byte> content, byte fo
24132413
static int ReadSyncsafeInt32 (ReadOnlySpan<byte> data)
24142414
{
24152415
return ((data[0] & 0x7F) << 21) |
2416-
((data[1] & 0x7F) << 14) |
2417-
((data[2] & 0x7F) << 7) |
2418-
(data[3] & 0x7F);
2416+
((data[1] & 0x7F) << 14) |
2417+
((data[2] & 0x7F) << 7) |
2418+
(data[3] & 0x7F);
24192419
}
24202420

24212421
/// <summary>

src/TagLibSharp2/Mpeg/MpegFrame.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -224,20 +224,20 @@ static int CalculateFrameSize (MpegLayer layer, int bitrate, int sampleRate, boo
224224
int coefficient;
225225

226226
switch (layer) {
227-
case MpegLayer.Layer1:
228-
// Layer 1: frame size = (12 * bitrate / sampleRate + padding) * 4
229-
paddingSize = hasPadding ? 4 : 0;
230-
return ((12 * bitrate * 1000 / sampleRate) + (hasPadding ? 1 : 0)) * 4;
231-
232-
case MpegLayer.Layer2:
233-
case MpegLayer.Layer3:
234-
// Layer 2/3: frame size = 144 * bitrate / sampleRate + padding
235-
paddingSize = hasPadding ? 1 : 0;
236-
coefficient = 144;
237-
return (coefficient * bitrate * 1000 / sampleRate) + paddingSize;
238-
239-
default:
240-
return 0;
227+
case MpegLayer.Layer1:
228+
// Layer 1: frame size = (12 * bitrate / sampleRate + padding) * 4
229+
paddingSize = hasPadding ? 4 : 0;
230+
return ((12 * bitrate * 1000 / sampleRate) + (hasPadding ? 1 : 0)) * 4;
231+
232+
case MpegLayer.Layer2:
233+
case MpegLayer.Layer3:
234+
// Layer 2/3: frame size = 144 * bitrate / sampleRate + padding
235+
paddingSize = hasPadding ? 1 : 0;
236+
coefficient = 144;
237+
return (coefficient * bitrate * 1000 / sampleRate) + paddingSize;
238+
239+
default:
240+
return 0;
241241
}
242242
}
243243

src/TagLibSharp2/Riff/RiffInfoTag.cs

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -104,25 +104,21 @@ public class RiffInfoTag : Tag
104104
public override TagTypes TagType => TagTypes.RiffInfo;
105105

106106
/// <inheritdoc />
107-
public override string? Title
108-
{
107+
public override string? Title {
109108
get => GetField (INAM);
110109
set => SetField (INAM, value);
111110
}
112111

113112
/// <inheritdoc />
114-
public override string? Artist
115-
{
113+
public override string? Artist {
116114
get => GetField (IART);
117115
set => SetField (IART, value);
118116
}
119117

120118
/// <inheritdoc />
121119
#pragma warning disable CA1819 // Properties should not return arrays - TagLib# API compatibility
122-
public override string[] Performers
123-
{
124-
get
125-
{
120+
public override string[] Performers {
121+
get {
126122
var artist = GetField (IART);
127123
return string.IsNullOrEmpty (artist)
128124
? []
@@ -133,32 +129,27 @@ public override string[] Performers
133129
#pragma warning restore CA1819
134130

135131
/// <inheritdoc />
136-
public override string? Album
137-
{
132+
public override string? Album {
138133
get => GetField (IPRD);
139134
set => SetField (IPRD, value);
140135
}
141136

142137
/// <inheritdoc />
143-
public override string? Comment
144-
{
138+
public override string? Comment {
145139
get => GetField (ICMT);
146140
set => SetField (ICMT, value);
147141
}
148142

149143
/// <inheritdoc />
150-
public override string? Genre
151-
{
144+
public override string? Genre {
152145
get => GetField (IGNR);
153146
set => SetField (IGNR, value);
154147
}
155148

156149
/// <inheritdoc />
157150
#pragma warning disable CA1819 // Properties should not return arrays - TagLib# API compatibility
158-
public override string[] Genres
159-
{
160-
get
161-
{
151+
public override string[] Genres {
152+
get {
162153
var genre = GetField (IGNR);
163154
return string.IsNullOrEmpty (genre)
164155
? []
@@ -169,10 +160,8 @@ public override string[] Genres
169160
#pragma warning restore CA1819
170161

171162
/// <inheritdoc />
172-
public override string? Year
173-
{
174-
get
175-
{
163+
public override string? Year {
164+
get {
176165
var icrd = GetField (ICRD);
177166
if (string.IsNullOrEmpty (icrd))
178167
return null;
@@ -184,10 +173,8 @@ public override string? Year
184173
}
185174

186175
/// <inheritdoc />
187-
public override uint? Track
188-
{
189-
get
190-
{
176+
public override uint? Track {
177+
get {
191178
var track = GetField (ITRK);
192179
if (string.IsNullOrEmpty (track))
193180
return null;
@@ -198,17 +185,15 @@ public override uint? Track
198185
}
199186

200187
/// <inheritdoc />
201-
public override string? Copyright
202-
{
188+
public override string? Copyright {
203189
get => GetField (ICOP);
204190
set => SetField (ICOP, value);
205191
}
206192

207193
/// <summary>
208194
/// Gets or sets the software field.
209195
/// </summary>
210-
public string? Software
211-
{
196+
public string? Software {
212197
get => GetField (ISFT);
213198
set => SetField (ISFT, value);
214199
}

tests/TagLibSharp2.Tests/Aiff/AiffFilePictureTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ static BinaryData CreateMinimalAiff ()
2727
builder.AddUInt16BE (2); // channels
2828
builder.AddUInt32BE (0); // sample frames
2929
builder.AddUInt16BE (16); // bits per sample
30-
// 80-bit extended sample rate (44100 Hz)
30+
// 80-bit extended sample rate (44100 Hz)
3131
builder.Add (new byte[] { 0x40, 0x0E, 0xAC, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
3232

3333
// SSND chunk (empty)

tests/TagLibSharp2.Tests/Core/SaveToFileConvenienceTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
using TagLibSharp2.Core;
44
using TagLibSharp2.Mpeg;
5-
using TagLibSharp2.Xiph;
65
using TagLibSharp2.Ogg;
6+
using TagLibSharp2.Xiph;
77

88
namespace TagLibSharp2.Tests.Core;
99

@@ -50,7 +50,7 @@ static byte[] CreateMinimalFlac ()
5050
builder.Add (new byte[] { 0x10, 0x00 }); // max block size
5151
builder.Add (new byte[] { 0x00, 0x00, 0x00 }); // min frame size
5252
builder.Add (new byte[] { 0x00, 0x00, 0x00 }); // max frame size
53-
// sample rate (20 bits) = 44100, channels (3 bits) = 2-1, bps (5 bits) = 16-1, samples (36 bits)
53+
// sample rate (20 bits) = 44100, channels (3 bits) = 2-1, bps (5 bits) = 16-1, samples (36 bits)
5454
builder.Add (new byte[] { 0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00 });
5555
builder.AddZeros (16); // MD5
5656

tests/TagLibSharp2.Tests/Mpeg/MpegAudioPropertiesTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ static BinaryData CreateDataWithId3v2Prefix (int id3Size, BinaryData mp3Data)
296296
builder.Add (0x49, 0x44, 0x33); // "ID3"
297297
builder.Add (0x04, 0x00); // Version 2.4
298298
builder.Add (0x00); // Flags
299-
// Syncsafe size (without header)
299+
// Syncsafe size (without header)
300300
var payloadSize = id3Size - 10;
301301
builder.AddSyncSafeUInt32 ((uint)payloadSize);
302302

tests/TagLibSharp2.Tests/Riff/BextTagTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public void Umid_Version1_ParsedCorrectly ()
146146
{
147147
var data = CreateMinimalBextChunk ();
148148
data[346] = 0x01; // Version 1
149-
// UMID at offset 348, 64 bytes
149+
// UMID at offset 348, 64 bytes
150150
for (int i = 0; i < 64; i++)
151151
data[348 + i] = (byte)(i + 1);
152152

0 commit comments

Comments
 (0)