kwave  18.07.70
FlacEncoder.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  FlacEncoder.cpp - encoder for FLAC data
3  -------------------
4  begin : Tue Feb 28 2004
5  copyright : (C) 2004 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <math.h>
21 #include <stdlib.h>
22 
23 #include <QApplication>
24 #include <QByteArray>
25 #include <QList>
26 #include <QVarLengthArray>
27 
28 #include <KLocalizedString>
29 #include <QtGlobal>
30 
31 #include <vorbis/vorbisenc.h>
32 
33 #include "libkwave/FileInfo.h"
34 #include "libkwave/MessageBox.h"
35 #include "libkwave/MetaDataList.h"
37 #include "libkwave/Sample.h"
38 #include "libkwave/SampleReader.h"
39 #include "libkwave/String.h"
40 #include "libkwave/Utils.h"
41 
42 #include "FlacCodecPlugin.h"
43 #include "FlacEncoder.h"
44 
45 /***************************************************************************/
47  :Kwave::Encoder(), FLAC::Encoder::Stream(),
48  m_vorbis_comment_map(), m_dst(Q_NULLPTR)
49 {
52 }
53 
54 /***************************************************************************/
56 {
57 }
58 
59 /***************************************************************************/
61 {
62  return new Kwave::FlacEncoder();
63 }
64 
65 /***************************************************************************/
66 QList<Kwave::FileProperty> Kwave::FlacEncoder::supportedProperties()
67 {
68  return m_vorbis_comment_map.values();
69 }
70 
71 /***************************************************************************/
72 ::FLAC__StreamEncoderWriteStatus Kwave::FlacEncoder::write_callback(
73  const FLAC__byte buffer[], size_t bytes,
74  unsigned /* samples */, unsigned /* current_frame */)
75 {
76  Q_ASSERT(m_dst);
77  if (!m_dst) return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
78 
79  qint64 written = m_dst->write(
80  reinterpret_cast<const char *>(&(buffer[0])),
81  static_cast<qint64>(bytes)
82  );
83 
84  return (written == static_cast<qint64>(bytes)) ?
85  FLAC__STREAM_ENCODER_WRITE_STATUS_OK :
86  FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
87 }
88 
89 /***************************************************************************/
90 void Kwave::FlacEncoder::metadata_callback(const ::FLAC__StreamMetadata *)
91 {
92  /* we are not interested in the FLAC metadata */
93 }
94 
95 /***************************************************************************/
97  :m_vc(Q_NULLPTR)
98 {
99  m_vc = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
100  Q_ASSERT(m_vc);
101 }
102 
103 /***************************************************************************/
105 {
106  if (m_vc) {
107  // clean up all vorbis comments
108  }
109 }
110 
111 /***************************************************************************/
113  const QString &value)
114 {
115  Q_ASSERT(m_vc);
116  if (!m_vc) return;
117 
118  QString s;
119  s = tag + _("=") + value;
120 
121  // make a plain C string out of it, containing UTF-8
122  QByteArray val = s.toUtf8();
123 
124  // put it into a vorbis_comment_entry structure
125  FLAC__StreamMetadata_VorbisComment_Entry entry;
126  entry.length = val.length(); // in bytes, not characters !
127  entry.entry = reinterpret_cast<FLAC__byte *>(val.data());
128 
129  // insert the comment into the list
130  unsigned int count = m_vc->data.vorbis_comment.num_comments;
131  bool ok = FLAC__metadata_object_vorbiscomment_insert_comment(
132  m_vc, count, entry, true);
133 
134  Q_ASSERT(ok);
135  Q_UNUSED(ok);
136 }
137 
138 /***************************************************************************/
140 {
141  return m_vc;
142 }
143 
144 /***************************************************************************/
146  QVector<FLAC__StreamMetadata *> &flac_metadata)
147 {
148  // encode all Vorbis comments
149  VorbisCommentMap::ConstIterator it;
151  for (it = m_vorbis_comment_map.constBegin();
152  it != m_vorbis_comment_map.constEnd();
153  ++it)
154  {
155  if (!info.contains(it.value())) continue; // not present -> skip
156 
157  QString value = info.get(it.value()).toString();
158  vc.add(it.key(), value);
159  }
160  flac_metadata.append(vc.data());
161 
162  // todo: add cue sheet etc here...
163 
164 }
165 
166 /***************************************************************************/
167 bool Kwave::FlacEncoder::encode(QWidget *widget,
169  QIODevice &dst,
170  const Kwave::MetaDataList &meta_data)
171 {
172  bool result = true;
173 
174  qDebug("FlacEncoder::encode()");
175  const Kwave::FileInfo info(meta_data);
176  m_dst = &dst;
177 
178  // get info: tracks, sample rate
179  int tracks = info.tracks();
180  unsigned int bits = info.bits();
181  sample_index_t length = info.length();
182 
183  set_compression_level(5); // @todo make the FLAC compression configurable
184  set_channels(static_cast<unsigned>(tracks));
185  set_bits_per_sample(static_cast<unsigned>(bits));
186  set_sample_rate(static_cast<unsigned>(info.rate()));
187  set_total_samples_estimate(static_cast<FLAC__uint64>(length));
188  set_verify(false); // <- set to "true" for debugging
189 
190  // use mid-side stereo encoding if we have two channels
191  set_do_mid_side_stereo(tracks == 2);
192  set_loose_mid_side_stereo(tracks == 2);
193 
194  // encode meta data, most of them as vorbis comments
195  QVector<FLAC__StreamMetadata *> flac_metadata;
196  encodeMetaData(info, flac_metadata);
197 
198  // convert container to a list of pointers
199  unsigned int meta_count = flac_metadata.size();
200  if (meta_count) {
201  // WARNING: this only stores the pointer, it does not copy!
202  if (!set_metadata(flac_metadata.data(), meta_count)) {
203  qWarning("FlacEncoder: setting meta data failed !?");
204  }
205  }
206 
207  QVector<FLAC__int32 *> flac_buffer;
208  do {
209  // open the output device
210  if (!dst.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
212  i18n("Unable to open the file for saving."));
213  result = false;
214  break;
215  }
216 
217  // initialize the FLAC stream, this already writes some meta info
218  FLAC__StreamEncoderInitStatus init_state = init();
219  if (init_state != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
220  qWarning("state = %d", static_cast<int>(init_state));
222  i18n("Unable to open the FLAC encoder."));
223  result = false;
224  break;
225  }
226 
227  // allocate output buffers, with FLAC 32 bit format
228  unsigned int len = src.blockSize(); // samples
229  for (int track=0; track < tracks; track++)
230  {
231  FLAC__int32 *buffer =
232  static_cast<FLAC__int32 *>(malloc(sizeof(FLAC__int32) * len));
233  Q_ASSERT(buffer);
234  if (!buffer) break;
235  flac_buffer.append(buffer);
236  }
237 
238  // allocate input buffer, with Kwave's sample_t
239  Kwave::SampleArray in_buffer(len);
240  Q_ASSERT(in_buffer.size() == len);
241  Q_ASSERT(flac_buffer.size() == tracks);
242 
243  if ((in_buffer.size() < len) || (flac_buffer.size() < tracks))
244  {
245  Kwave::MessageBox::error(widget, i18n("Out of memory"));
246  result = false;
247  break;
248  }
249 
250  // calculate divisor for reaching the proper resolution
251  int shift = SAMPLE_BITS - bits;
252  if (shift < 0) shift = 0;
253  FLAC__int32 div = (1 << shift);
254  if (div == 1) div = 0;
255  const FLAC__int32 clip_min = -(1 << bits);
256  const FLAC__int32 clip_max = (1 << bits) - 1;
257 
258  sample_index_t rest = length;
259  while (rest && len && !src.isCanceled() && result) {
260  // limit to rest of signal
261  if (len > rest) len = Kwave::toUint(rest);
262  if (!in_buffer.resize(len)) {
263  Kwave::MessageBox::error(widget, i18n("Out of memory"));
264  result = false;
265  break;
266  }
267 
268  // add all samples to one single buffer
269  for (int track = 0; track < tracks; track++) {
270  Kwave::SampleReader *reader = src[track];
271  Q_ASSERT(reader);
272  if (!reader) break;
273 
274  (*reader) >> in_buffer; // read samples into in_buffer
275  unsigned int l = in_buffer.size();// in_buffer might be empty!
276  if (l < len) {
277  if (!in_buffer.resize(len)) {
278  Kwave::MessageBox::error(widget, i18n("Out of memory"));
279  result = false;
280  break;
281  }
282  while (l < len) in_buffer[l++] = 0;
283  }
284 
285  FLAC__int32 *buf = flac_buffer.at(track);
286  Q_ASSERT(buf);
287  if (!buf) break;
288 
289  const Kwave::SampleArray &in = in_buffer;
290  for (unsigned int in_pos = 0; in_pos < len; in_pos++) {
291  FLAC__int32 s = in[in_pos];
292  if (div) s /= div;
293  if (s > clip_max) s = clip_max;
294  if (s < clip_min) s = clip_min;
295  *buf = s;
296  buf++;
297  }
298  }
299  if (!result) break; // error occurred?
300 
301  // process all collected samples
302  FLAC__int32 **buffer = flac_buffer.data();
303  bool processed = process(buffer, static_cast<unsigned>(len));
304  if (!processed) {
305  result = false;
306  break;
307  }
308 
309  rest -= len;
310  }
311 
312  } while (false);
313 
314  // close the output device and the FLAC stream
315  finish();
316 
317  // clean up all FLAC metadata
318  while (!flac_metadata.isEmpty()) {
319  FLAC__StreamMetadata *m = flac_metadata[0];
320  if (m) FLAC__metadata_object_delete(m);
321  flac_metadata.remove(0);
322  }
323 
324  m_dst = Q_NULLPTR;
325  dst.close();
326 
327  while (!flac_buffer.isEmpty()) {
328  FLAC__int32 *buf = flac_buffer.first();
329  if (buf) free(buf);
330  flac_buffer.remove(0);
331  }
332 
333  return result;
334 }
335 
336 /***************************************************************************/
337 /***************************************************************************/
bool contains(const FileProperty property) const
Definition: FileInfo.cpp:354
virtual void metadata_callback(const ::FLAC__StreamMetadata *metadata) Q_DECL_OVERRIDE
Definition: FlacEncoder.cpp:90
QIODevice * m_dst
Definition: FlacEncoder.h:140
Definition: App.h:33
double rate() const
Definition: FileInfo.cpp:415
virtual bool encode(QWidget *widget, Kwave::MultiTrackReader &src, QIODevice &dst, const Kwave::MetaDataList &meta_data) Q_DECL_OVERRIDE
QVariant get(FileProperty key) const
Definition: FileInfo.cpp:372
quint64 sample_index_t
Definition: Sample.h:28
virtual Kwave::Encoder * instance() Q_DECL_OVERRIDE
Definition: FlacEncoder.cpp:60
virtual void encodeMetaData(const Kwave::FileInfo &info, QVector< FLAC__StreamMetadata *> &flac_metadata)
Kwave::VorbisCommentMap m_vorbis_comment_map
Definition: FlacEncoder.h:137
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
sample_index_t length() const
Definition: FileInfo.cpp:400
#define REGISTER_MIME_TYPES
unsigned int tracks() const
Definition: FileInfo.cpp:445
virtual QList< Kwave::FileProperty > supportedProperties() Q_DECL_OVERRIDE
Definition: FlacEncoder.cpp:66
virtual unsigned int blockSize() const
#define REGISTER_COMPRESSION_TYPES
#define _(m)
Definition: memcpy.c:66
unsigned int size() const
void add(const QString &tag, const QString &value)
unsigned int toUint(T x)
Definition: Utils.h:109
unsigned int bits() const
Definition: FileInfo.cpp:430
virtual ~FlacEncoder() Q_DECL_OVERRIDE
Definition: FlacEncoder.cpp:55
sample_t * data()
Definition: SampleArray.h:62
#define SAMPLE_BITS
Definition: Sample.h:43
virtual ::FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame) Q_DECL_OVERRIDE
Definition: FlacEncoder.cpp:72
bool resize(unsigned int size) Q_REQUIRED_RESULT