File: FlacDecoder.cpp

package info (click to toggle)
kwave 25.04.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,272 kB
  • sloc: cpp: 56,173; xml: 817; perl: 688; sh: 57; makefile: 11
file content (336 lines) | stat: -rw-r--r-- 11,349 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*************************************************************************
        FlacDecoder.cpp  -  decoder for FLAC data
                             -------------------
    begin                : Tue Feb 28 2004
    copyright            : (C) 2004 by Thomas Eschenbacher
    email                : Thomas.Eschenbacher@gmx.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "config.h"

#include <new>

#include <QDateTime>
#include <QIODevice>

#include <KLocalizedString>

#include "libkwave/Compression.h"
#include "libkwave/MessageBox.h"
#include "libkwave/MultiWriter.h"
#include "libkwave/Sample.h"
#include "libkwave/String.h"
#include "libkwave/Writer.h"

#include "FlacCodecPlugin.h"
#include "FlacDecoder.h"

//***************************************************************************
Kwave::FlacDecoder::FlacDecoder()
    :Kwave::Decoder(),
     FLAC::Decoder::Stream(),
     m_source(nullptr),
     m_dest(nullptr),
     m_vorbis_comment_map()
{
    REGISTER_MIME_TYPES
    REGISTER_COMPRESSION_TYPES
}

//***************************************************************************
Kwave::FlacDecoder::~FlacDecoder()
{
    if (m_source) close();
}

//***************************************************************************
Kwave::Decoder *Kwave::FlacDecoder::instance()
{
    return new(std::nothrow) Kwave::FlacDecoder();
}

//***************************************************************************
::FLAC__StreamDecoderReadStatus Kwave::FlacDecoder::read_callback(
        FLAC__byte buffer[], size_t *bytes)
{
    Q_ASSERT(bytes);
    Q_ASSERT(m_source);
    if (!bytes || !m_source) return FLAC__STREAM_DECODER_READ_STATUS_ABORT;

    // check for EOF
    if (m_source->atEnd()) {
        *bytes = 0;
        return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
    }

    // read into application buffer
    *bytes = static_cast<unsigned>(m_source->read(
        reinterpret_cast<char *>(&(buffer[0])),
        static_cast<qint64>(*bytes)
    ));

    if (!*bytes) return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;

    return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}

//***************************************************************************
::FLAC__StreamDecoderWriteStatus Kwave::FlacDecoder::write_callback(
        const ::FLAC__Frame *frame,
        const FLAC__int32 * const buffer[])
{
    Q_ASSERT(buffer);
    Q_ASSERT(frame);
    Q_ASSERT(m_dest);
    if (!buffer || !frame || !m_dest)
        return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;

    const unsigned int samples = frame->header.blocksize;

    const unsigned int tracks  = Kwave::FileInfo(metaData()).tracks();
    Q_ASSERT(samples);
    Q_ASSERT(tracks);
    if (!samples || !tracks)
        return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;

    Kwave::SampleArray dst(samples);

    // expand the samples up to the correct number of bits
    int shift = SAMPLE_BITS - Kwave::FileInfo(metaData()).bits();
    if (shift < 0) shift = 0;
    unsigned int mul = (1 << shift);

    // decode the samples into a temporary buffer and
    // flush it to the Writer(s), track by track
    for (unsigned int track=0; track < tracks; track++) {
        Kwave::Writer *writer = (*m_dest)[track];
        Q_ASSERT(writer);
        if (!writer) continue;
        const FLAC__int32 *src = buffer[track];
        sample_t *d = dst.data();

        for (unsigned int sample = 0; sample < samples; sample++) {
            // the following cast is only necessary if
            // sample_t is not equal to a quint32
            sample_t s  = static_cast<sample_t>(*src++);

            // correct precision
            if (shift) s *= mul;

            // write to destination buffer
            *d++ = s;
        }

        // flush the temporary buffer
        (*writer) << dst;
    }

    // at this point we check for a user-cancel
    return (m_dest->isCanceled()) ?
        FLAC__STREAM_DECODER_WRITE_STATUS_ABORT :
        FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

//***************************************************************************
void Kwave::FlacDecoder::parseStreamInfo(
    const FLAC::Metadata::StreamInfo &stream_info)
{
    qDebug("FLAC stream info");
    qDebug("\tmin_blocksize   = %u", stream_info.get_min_blocksize());
    qDebug("\tmax_blocksize   = %u", stream_info.get_max_blocksize());
    qDebug("\tmin_framesize   = %u", stream_info.get_min_framesize());
    qDebug("\tmax_framesize   = %u", stream_info.get_max_framesize());

    Kwave::FileInfo info(metaData());
    info.setRate(stream_info.get_sample_rate());
    info.setTracks(stream_info.get_channels());
    info.setBits(stream_info.get_bits_per_sample());
    info.setLength(stream_info.get_total_samples());
    metaData().replace(Kwave::MetaDataList(info));

    qDebug("Bitstream is %u channel, %uHz",
           stream_info.get_channels(),
           stream_info.get_sample_rate());
}

//***************************************************************************
void Kwave::FlacDecoder::parseVorbisComments(
        const FLAC::Metadata::VorbisComment &vorbis_comments)
{
    Kwave::FileInfo info(metaData());

    // first of all: the vendor string, specifying the software
    QString vendor = QString::fromUtf8(reinterpret_cast<const char *>(
        vorbis_comments.get_vendor_string()));
    if (vendor.length()) {
        info.set(Kwave::INF_SOFTWARE, vendor);
        qDebug("Encoded by: '%s'\n\n", DBG(vendor));
    }

    // parse all vorbis comments into Kwave file properties
    for (unsigned int i = 0; i < vorbis_comments.get_num_comments(); i++) {
        FLAC::Metadata::VorbisComment::Entry comment =
            vorbis_comments.get_comment(i);
        Q_ASSERT(comment.is_valid());
        if (!comment.is_valid()) continue;

        QString name = QString::fromUtf8(
            comment.get_field_name(), comment.get_field_name_length());
        QString value = QString::fromUtf8(
            comment.get_field_value(), comment.get_field_value_length());

        if (!m_vorbis_comment_map.contains(name)) continue;

        // we have a known vorbis tag
        Kwave::FileProperty prop = m_vorbis_comment_map[name];
        info.set(prop, value);
    }

    // convert the date property to a QDate
    if (info.contains(Kwave::INF_CREATION_DATE)) {
        QString str_date =
            QVariant(info.get(Kwave::INF_CREATION_DATE)).toString();
        QDate date;
        date = QDate::fromString(str_date, Qt::ISODate);
        if (!date.isValid()) {
            int year = str_date.toInt();
            date.setDate(year, 1, 1);
        }
        if (date.isValid()) info.set(Kwave::INF_CREATION_DATE, date);
     }

     metaData().replace(Kwave::MetaDataList(info));
}

//***************************************************************************
void Kwave::FlacDecoder::metadata_callback(
    const ::FLAC__StreamMetadata *metadata)
{
    Q_ASSERT(metadata);
    if (!metadata) return;

    switch (metadata->type) {
        case FLAC__METADATA_TYPE_STREAMINFO: {
            FLAC::Metadata::StreamInfo stream_info(
                const_cast< ::FLAC__StreamMetadata * >(metadata), true);
            parseStreamInfo(stream_info);
            break;
        }
        case FLAC__METADATA_TYPE_PADDING:
            // -> ignored
            break;
        case FLAC__METADATA_TYPE_APPLICATION:
            qDebug("FLAC metadata: application data");
            break;
        case FLAC__METADATA_TYPE_SEEKTABLE:
            qDebug("FLAC metadata: seektable - not supported yet");
            break;
        case FLAC__METADATA_TYPE_VORBIS_COMMENT: {
            FLAC::Metadata::VorbisComment vorbis_comments(
                const_cast< ::FLAC__StreamMetadata * >(metadata), true);
            parseVorbisComments(vorbis_comments);
            break;
        }
        case FLAC__METADATA_TYPE_CUESHEET:
            qDebug("FLAC metadata: cuesheet - not supported yet");
            break;
        case FLAC__METADATA_TYPE_UNDEFINED:
        default:
            qDebug("FLAC metadata: unknown/undefined type");
    }
}

//***************************************************************************
void Kwave::FlacDecoder::error_callback(::FLAC__StreamDecoderErrorStatus status)
{
    qDebug("FlacDecoder::error_callback: status=%d", status);
}

//***************************************************************************
bool Kwave::FlacDecoder::open(QWidget *widget, QIODevice &src)
{
    metaData().clear();
    Q_ASSERT(!m_source);
    if (m_source) qWarning("FlacDecoder::open(), already open !");

    // try to open the source
    if (!src.open(QIODevice::ReadOnly)) {
        qWarning("failed to open source !");
        return false;
    }

    // take over the source
    m_source = &src;

    /********** Decoder setup ************/
    qDebug("--- FlacDecoder::open() ---");
    set_metadata_respond_all();

    // initialize the stream
    FLAC__StreamDecoderInitStatus init_state = init();
    if (init_state > FLAC__STREAM_DECODER_INIT_STATUS_OK) {
        Kwave::MessageBox::error(widget, i18n(
           "Opening the FLAC bitstream failed."));
        return false;
    }

    // read in all metadata
    process_until_end_of_metadata();

    FLAC::Decoder::Stream::State state = get_state();
    if (state >= FLAC__STREAM_DECODER_END_OF_STREAM) {
        Kwave::MessageBox::error(widget, i18n(
           "Error while parsing the FLAC metadata. (%s)"),
           _(state.as_cstring()));
        return false;
    }

    // set some more standard properties
    Kwave::FileInfo info(metaData());
    info.set(Kwave::INF_MIMETYPE, _(DEFAULT_MIME_TYPE));
    info.set(Kwave::INF_COMPRESSION, Kwave::Compression::FLAC);
    metaData().replace(Kwave::MetaDataList(info));

    return true;
}

//***************************************************************************
bool Kwave::FlacDecoder::decode(QWidget * /* widget */,
                                Kwave::MultiWriter &dst)
{
    Q_ASSERT(m_source);
    if (!m_source) return false;

    m_dest = &dst;

    // read in all remaining data
    qDebug("FlacDecoder::decode(...)");
    process_until_end_of_stream();

    m_dest = nullptr;
    Kwave::FileInfo info(metaData());
    info.setLength(dst.last() ? (dst.last() + 1) : 0);
    metaData().replace(Kwave::MetaDataList(info));

    // return with a valid Signal, even if the user pressed cancel !
    return true;
}

//***************************************************************************
void Kwave::FlacDecoder::close()
{
    finish();
    m_source = nullptr;
}

//***************************************************************************
//***************************************************************************