11#include " encoder/encodermp3.h"
22
3- #include < limits.h>
4-
5- #include < QObject>
6- #include < QtDebug>
3+ #include < id3v2tag.h>
74
85#include " audio/types.h"
96#include " encoder/encodercallback.h"
107#include " encoder/encodermp3settings.h"
118
9+ namespace {
10+ constexpr size_t kHeaderPadding = 10000 ;
11+ } // namespace
12+
1213// Automatic thresholds for switching the encoder to mono
1314// They have been chosen by testing and to keep the same number
1415// of values for the slider.
@@ -134,9 +135,54 @@ void EncoderMp3::flush() {
134135 numBytes = lame_get_lametag_frame (
135136 m_lameFlags, m_bufferOut, m_bufferOutSize);
136137 }
137- // Write the lame/xing header.
138+
139+ TagLib::ID3v2::Tag id3Tag;
140+
141+ // Ideally, we should shift the file content forward to insert the header
142+ // (ID3v2 & Xing frame) dynamically, but `EncoderCallback` doesn't support
143+ // that so we use the static header padding and truncate the tracklist if
144+ // too long
145+ if (!m_metaDataTitle.isEmpty ()) {
146+ id3Tag.setTitle (QStringToTString (m_metaDataTitle));
147+ }
148+ if (!m_metaDataArtist.isEmpty ()) {
149+ id3Tag.setArtist (QStringToTString (m_metaDataArtist));
150+ }
151+ if (!m_metaDataAlbum.isEmpty ()) {
152+ id3Tag.setAlbum (QStringToTString (m_metaDataAlbum));
153+ }
154+ qsizetype tracklistMaxByteSize = kHeaderPadding -
155+ id3Tag.render ().size ();
156+ DEBUG_ASSERT (tracklistMaxByteSize > 0 );
157+
158+ TagLib::String trackList;
159+ qsizetype currentTracklistByteSize = 0 ;
160+
161+ for (const auto & track : getTrackList ()) {
162+ if (!trackList.isEmpty ()) {
163+ trackList += " \n " ;
164+ currentTracklistByteSize += 1 ;
165+ }
166+ auto tagTrack = QStringToTString (track);
167+ currentTracklistByteSize += tagTrack.data (TagLib::String::Type::UTF8 ).size ();
168+ // ellipse + 1 since we need a byte for the NULL terminator
169+ if (currentTracklistByteSize > tracklistMaxByteSize - 4 ) {
170+ trackList += " …" ;
171+ break ;
172+ } else {
173+ trackList += tagTrack;
174+ }
175+ }
176+ id3Tag.setComment (trackList);
177+
178+ TagLib::ByteVector id3Buffer = id3Tag.render ();
179+
138180 m_pCallback->seek (0 );
139- m_pCallback->write (nullptr , m_bufferOut, 0 , static_cast <int >(numBytes));
181+ // Write the lame/xing header.
182+ m_pCallback->write (reinterpret_cast <const unsigned char *>(id3Buffer.data ()),
183+ m_bufferOut,
184+ static_cast <int >(id3Buffer.size ()),
185+ static_cast <int >(numBytes));
140186}
141187
142188void EncoderMp3::encodeBuffer (const CSAMPLE * samples, const std::size_t bufferSize) {
@@ -176,6 +222,13 @@ void EncoderMp3::initStream() {
176222
177223 m_bufferIn[0 ] = (float *)malloc (m_bufferOutSize * sizeof (float ));
178224 m_bufferIn[1 ] = (float *)malloc (m_bufferOutSize * sizeof (float ));
225+
226+ // Add a static header padding, which will be filled one termination
227+ QByteArray headerPad (kHeaderPadding , 0 );
228+ m_pCallback->write (nullptr ,
229+ reinterpret_cast <const unsigned char *>(headerPad.constData ()),
230+ 0 ,
231+ headerPad.size ());
179232}
180233
181234int EncoderMp3::initEncoder (mixxx::audio::SampleRate sampleRate,
@@ -234,17 +287,7 @@ int EncoderMp3::initEncoder(mixxx::audio::SampleRate sampleRate,
234287
235288 lame_set_quality (m_lameFlags, 2 );
236289
237- // ID3 Tag if fields are not NULL
238- id3tag_init (m_lameFlags);
239- if (!m_metaDataTitle.isEmpty ()) {
240- id3tag_set_title (m_lameFlags, m_metaDataTitle.toLatin1 ().constData ());
241- }
242- if (!m_metaDataArtist.isEmpty ()) {
243- id3tag_set_artist (m_lameFlags, m_metaDataArtist.toLatin1 ().constData ());
244- }
245- if (!m_metaDataAlbum.isEmpty ()) {
246- id3tag_set_album (m_lameFlags,m_metaDataAlbum.toLatin1 ().constData ());
247- }
290+ // ID3 tag will be written during flushing
248291
249292 int ret = lame_init_params (m_lameFlags);
250293 if (ret < 0 ) {
@@ -257,8 +300,15 @@ int EncoderMp3::initEncoder(mixxx::audio::SampleRate sampleRate,
257300 return 0 ;
258301}
259302
260- void EncoderMp3::updateMetaData (const QString& artist, const QString& title, const QString& album) {
261- m_metaDataTitle = title;
262- m_metaDataArtist = artist;
263- m_metaDataAlbum = album;
303+ void EncoderMp3::updateMetaData (const QString& artist,
304+ const QString& title,
305+ const QString& album,
306+ std::chrono::seconds timecode) {
307+ if (m_bufferOut == nullptr ) {
308+ m_metaDataTitle = title;
309+ m_metaDataArtist = artist;
310+ m_metaDataAlbum = album;
311+ } else {
312+ addToTracklist (artist, title, timecode);
313+ }
264314}
0 commit comments