kwave  18.07.70
VorbisEncoder.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  VorbisEncoder.cpp - sub encoder base class for Vorbis in an Ogg container
3  -------------------
4  begin : Thu Jan 03 2013
5  copyright : (C) 2013 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 <string.h>
22 
23 #include <QTime>
24 #include <QtGlobal>
25 
26 #include <KLocalizedString>
27 
28 #include "libkwave/MessageBox.h"
30 #include "libkwave/Sample.h"
31 #include "libkwave/SampleArray.h"
32 #include "libkwave/Utils.h"
33 
34 #include "VorbisEncoder.h"
35 
37 #define BUFFER_SIZE 1024
38 
40 #define DEFAULT_BITRATE 64000
41 
42 /***************************************************************************/
44  :m_comments_map(), m_info()
45 {
46  memset(&m_os, 0x00, sizeof(m_os));
47  memset(&m_og, 0x00, sizeof(m_og));
48  memset(&m_op, 0x00, sizeof(m_op));
49 
50  memset(&m_vb, 0x00, sizeof(m_vb));
51  memset(&m_vc, 0x00, sizeof(m_vc));
52  memset(&m_vd, 0x00, sizeof(m_vd));
53  memset(&m_vi, 0x00, sizeof(m_vi));
54 }
55 
56 /***************************************************************************/
58 {
59  close();
60 }
61 
62 /***************************************************************************/
64 {
65  foreach (const QString &key, m_comments_map.keys()) {
66  Kwave::FileProperty property = m_comments_map[key];
67  if (!info.contains(property)) continue; // skip if not present
68 
69  // encode the property as string
70  vorbis_comment_add_tag(&m_vc,
71  UTF8(key),
72  UTF8(info.get(property).toString())
73  );
74  }
75 }
76 
77 /***************************************************************************/
78 bool Kwave::VorbisEncoder::open(QWidget *widget, const Kwave::FileInfo &info,
80 {
81  int ret = -1;
82 
83  Q_UNUSED(src);
84 
85  // get info: tracks, sample rate, bitrate(s)
86  m_info = info;
87  const unsigned int tracks = info.tracks();
88  const long int sample_rate = static_cast<const long int>(info.rate());
89 
90  if (tracks > 2) {
92  i18n("This codec supports only mono or stereo files, "
93  "%1 channels are not supported.", tracks));
94  return false;
95  }
96 
97  // ABR bitrates
98  int bitrate_nominal = info.contains(Kwave::INF_BITRATE_NOMINAL) ?
99  QVariant(info.get(Kwave::INF_BITRATE_NOMINAL)).toInt() : -1;
100  int bitrate_lower = info.contains(Kwave::INF_BITRATE_LOWER) ?
101  QVariant(info.get(Kwave::INF_BITRATE_LOWER)).toInt() : -1;
102  int bitrate_upper = info.contains(Kwave::INF_BITRATE_UPPER) ?
103  QVariant(info.get(Kwave::INF_BITRATE_UPPER)).toInt() : -1;
104 
105  // VBR quality
106  int vbr_quality = info.contains(Kwave::INF_VBR_QUALITY) ?
107  QVariant(info.get(Kwave::INF_VBR_QUALITY)).toInt() : -1;
108 
109  qDebug("OggEncoder: ABR=%d...%d...%d Bits/s, VBR=%d%%",
110  bitrate_lower,bitrate_nominal,bitrate_upper,vbr_quality);
111 
112  if ((vbr_quality < 0) && (bitrate_nominal <= 0)) {
113  // no quality and no bitrate given -> complain !
115  i18n("You have not selected any bitrate for the encoding. "
116  "Do you want to continue and encode with %1 kBit/s "
117  "or cancel and choose a different bitrate?",
118  DEFAULT_BITRATE / 1000)) != KMessageBox::Continue)
119  return false; // <- canceled
120 
121  bitrate_nominal = DEFAULT_BITRATE;
122  bitrate_lower = -1;
123  bitrate_upper = -1;
124  }
125 
126  // some checks first
127  Q_ASSERT(tracks < 255);
128  if (tracks > 255) return false;
129 
130  /********** Encode setup ************/
131  vorbis_info_init(&m_vi);
132 
133  if ((bitrate_lower > 0) || (bitrate_upper > 0)) {
134  // Encoding using ABR mode.
135  bitrate_nominal = (bitrate_upper + bitrate_lower) / 2;
136  ret = vorbis_encode_init(&m_vi, tracks, sample_rate,
137  bitrate_upper,
138  bitrate_nominal,
139  bitrate_lower);
140  qDebug("VorbisEncoder: ABR with %d...%d...%d Bits/s",
141  bitrate_lower, bitrate_nominal, bitrate_upper);
142  } else if ((vbr_quality < 0) && (bitrate_nominal > 0)) {
143  // Encoding using constant bitrate in ABR mode
144  ret = vorbis_encode_setup_managed(&m_vi, tracks, sample_rate,
145  -1, bitrate_nominal, -1);
146 
147  // If OV_ECTL_RATEMANAGE2_SET is not defined, then your
148  // libvorbis is just too old.
149  if (!ret) ret =
150  vorbis_encode_ctl(&m_vi, OV_ECTL_RATEMANAGE2_SET, Q_NULLPTR) ||
151  vorbis_encode_setup_init(&m_vi);
152 
153  qDebug("VorbisEncoder: CBR with %d Bits/s", bitrate_nominal);
154  } else if (vbr_quality >= 0) {
155  // Encoding using VBR mode.
156  ret = vorbis_encode_init_vbr(&m_vi, tracks, sample_rate,
157  static_cast<float>(vbr_quality) / 100.0f);
158  qDebug("OggEncoder: VBR with %d%%", vbr_quality);
159  } else {
160  // unknown setup !?
161  qWarning("unknown Ogg/Vorbis setup: VBR quality=%d%%, "
162  "ABR lower=%d, ABR highest=%d, ABR nominal=%d",
163  vbr_quality, bitrate_lower, bitrate_upper,
164  bitrate_nominal);
165  return false;
166  }
167 
168  /* do not continue if setup failed; this can happen if we ask for a
169  mode that libVorbis does not support (eg, too low a bitrate, etc,
170  will return 'OV_EIMPL') */
171  if (ret) {
172  Kwave::MessageBox::sorry(widget, i18n("One or more encoding "
173  "parameters are not supported. Please change the "
174  "settings and try again."));
175  return false;
176  }
177 
178  // add all supported properties as file comments
179  vorbis_comment_init(&m_vc);
180  encodeProperties(info);
181 
182  // set up the analysis state and auxiliary encoding storage
183  vorbis_analysis_init(&m_vd, &m_vi);
184  vorbis_block_init(&m_vd, &m_vb);
185 
186 
187  // set up our packet->stream encoder
188  // pick a random serial number; that way we can more likely build
189  // chained streams just by concatenation
190  qsrand(QTime::currentTime().msec() ^ qrand());
191  ogg_stream_init(&m_os, qrand());
192 
193  return true;
194 }
195 
196 /***************************************************************************/
198 {
199  // Vorbis streams begin with three headers; the initial header (with
200  // most of the codec setup parameters) which is mandated by the Ogg
201  // bitstream spec. The second header holds any comment fields. The
202  // third header holds the bitstream codebook. We merely need to
203  // make the headers, then pass them to libvorbis one at a time;
204  // libvorbis handles the additional Ogg bitstream constraints
205  ogg_packet header;
206  ogg_packet header_comm;
207  ogg_packet header_code;
208 
209  vorbis_analysis_headerout(&m_vd, &m_vc, &header, &header_comm,
210  &header_code);
211  // automatically placed in its own page
212  ogg_stream_packetin(&m_os, &header);
213  ogg_stream_packetin(&m_os, &header_comm);
214  ogg_stream_packetin(&m_os, &header_code);
215 
216  // This ensures the actual audio data will start on a
217  // new page, as per spec
218  while (ogg_stream_flush(&m_os, &m_og)) {
219  dst.write(reinterpret_cast<char *>(m_og.header),
220  m_og.header_len);
221  dst.write(reinterpret_cast<char *>(m_og.body),
222  m_og.body_len);
223  }
224 
225  return true;
226 }
227 
228 /***************************************************************************/
230  QIODevice &dst)
231 {
232  bool eos = false;
233  const unsigned int tracks = m_info.tracks();
234  const sample_index_t length = m_info.length();
235 
236  sample_index_t rest = length;
237  while (!eos && !src.isCanceled()) {
238  if (src.eof()) {
239  // end of file. this can be done implicitly in the mainline,
240  // but it's easier to see here in non-clever fashion.
241  // Tell the library we're at end of stream so that it can handle
242  // the last frame and mark end of stream in the output properly
243  vorbis_analysis_wrote(&m_vd, 0);
244  } else {
245  // data to encode
246 
247  // expose the buffer to submit data
248  float **buffer = vorbis_analysis_buffer(&m_vd, BUFFER_SIZE);
249  unsigned int pos = 0;
250  unsigned int len = (rest > BUFFER_SIZE) ? BUFFER_SIZE :
251  Kwave::toUint(rest);
253  for (unsigned int track = 0; track < tracks; ++track) {
254  float *p = buffer[track];
255  unsigned int l = src[track]->read(samples, 0, len);
256  const sample_t *s = samples.constData();
257 
258  const unsigned int block = 8;
259  pos = 0;
260  while (pos + block < l) {
261  for (unsigned int i = 0; i < block; ++i, ++pos)
262  p[pos] = sample2float(s[pos]);
263  }
264  while (pos < l) {
265  p[pos] = sample2float(s[pos]);
266  pos++;
267  }
268  while (pos < len)
269  p[pos++] = 0;
270  }
271 
272  // tell the library how much we actually submitted
273  vorbis_analysis_wrote(&m_vd, pos);
274  }
275 
276  // vorbis does some data preanalysis, then divvies up blocks for
277  // more involved (potentially parallel) processing. Get a single
278  // block for encoding now
279  while (vorbis_analysis_blockout(&m_vd, &m_vb) == 1) {
280  // analysis, assume we want to use bitrate management
281  vorbis_analysis(&m_vb, Q_NULLPTR);
282  vorbis_bitrate_addblock(&m_vb);
283 
284  while (vorbis_bitrate_flushpacket(&m_vd, &m_op)) {
285  // weld the packet into the bitstream
286  ogg_stream_packetin(&m_os, &m_op);
287 
288  // write out pages (if any)
289  while (!eos) {
290  int result = ogg_stream_pageout(&m_os, &m_og);
291  if (!result) break;
292  dst.write(reinterpret_cast<char*>(m_og.header),
293  m_og.header_len);
294  dst.write(reinterpret_cast<char *>(m_og.body),
295  m_og.body_len);
296 
297  // this could be set above, but for illustrative
298  // purposes, I do it here (to show that vorbis
299  // does know where the stream ends)
300  if (ogg_page_eos(&m_og)) eos = true;
301  }
302  }
303  }
304  }
305 
306  return true;
307 }
308 
309 /***************************************************************************/
311 {
312  ogg_stream_clear(&m_os);
313 
314  vorbis_block_clear(&m_vb);
315  vorbis_dsp_clear(&m_vd);
316  vorbis_comment_clear(&m_vc);
317  vorbis_info_clear(&m_vi); // <- must be called last
318 }
319 
320 /***************************************************************************/
321 /***************************************************************************/
virtual bool encode(Kwave::MultiTrackReader &src, QIODevice &dst) Q_DECL_OVERRIDE
bool contains(const FileProperty property) const
Definition: FileInfo.cpp:354
virtual bool open(QWidget *widget, const Kwave::FileInfo &info, Kwave::MultiTrackReader &src) Q_DECL_OVERRIDE
double rate() const
Definition: FileInfo.cpp:415
void encodeProperties(const Kwave::FileInfo &info)
#define DEFAULT_BITRATE
QVariant get(FileProperty key) const
Definition: FileInfo.cpp:372
static int sorry(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:85
ogg_stream_state m_os
Definition: VorbisEncoder.h:97
virtual ~VorbisEncoder() Q_DECL_OVERRIDE
quint64 sample_index_t
Definition: Sample.h:28
virtual void close() Q_DECL_OVERRIDE
Kwave::FileInfo m_info
Definition: VorbisEncoder.h:94
virtual bool eof() const
sample_index_t length() const
Definition: FileInfo.cpp:400
virtual bool writeHeader(QIODevice &dst) Q_DECL_OVERRIDE
int toInt(T x)
Definition: Utils.h:127
vorbis_comment m_vc
static float sample2float(const sample_t s)
Definition: Sample.h:65
static int warningContinueCancel(QWidget *widget, QString message, QString caption=QString(), const QString buttonContinue=QString(), const QString buttonCancel=QString(), const QString &dontAskAgainName=QString())
Definition: MessageBox.cpp:115
unsigned int tracks() const
Definition: FileInfo.cpp:445
const sample_t * constData() const
Definition: SampleArray.h:54
unsigned int toUint(T x)
Definition: Utils.h:109
Kwave::VorbisCommentMap m_comments_map
Definition: VorbisEncoder.h:91
FileProperty
Definition: FileInfo.h:45
#define BUFFER_SIZE
#define UTF8(qs)
Definition: String.h:48
qint32 sample_t
Definition: Sample.h:37
vorbis_dsp_state m_vd