3 Copyright (C) 2008 jlh (jlh at gmx dot ch)
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2 of the License, version 3 of
8 the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 The GNU General Public License version 2 is included with the source of
20 this program under the file name COPYING. You can also get a copy on
24 // Note: this file doesn't include comments related to writing Ogg Vorbis
25 // files. it is mostly based on the examples/encoder_example.c from the vorbis
26 // library, so have a look there if you're curious about how this works.
28 // TODO: currently, this only writes tags while opening the file, but doesn't
29 // update them if they've been changed before close(). for now this is ok, as
36 #include <vorbis/vorbisenc.h>
38 #include "vorbiswriter.h"
40 #include "preferences.h"
42 struct VorbisWriterPrivateData
{
52 VorbisWriter::VorbisWriter() :
58 VorbisWriter::~VorbisWriter() {
60 debug("WARNING: VorbisWriter::~VorbisWriter(): File has not been closed, closing it now");
65 ogg_stream_clear(&pd
->os
);
66 vorbis_block_clear(&pd
->vb
);
67 vorbis_dsp_clear(&pd
->vd
);
68 vorbis_comment_clear(&pd
->vc
);
69 vorbis_info_clear(&pd
->vi
);
74 bool VorbisWriter::open(const QString
&fn
, long sr
, bool s
) {
75 bool b
= AudioFileWriter::open(fn
+ ".ogg", sr
, s
);
80 int quality
= preferences
.get(Pref::OutputFormatVorbisQuality
).toInt();
82 pd
= new VorbisWriterPrivateData
;
83 vorbis_info_init(&pd
->vi
);
85 if (vorbis_encode_init_vbr(&pd
->vi
, stereo
? 2 : 1, sampleRate
, (float)quality
/ 10.0f
) != 0) {
91 // TODO: the docs vaguely mention that stereo coupling can be disabled
92 // with vorbis_encode_ctl(), but I didn't find anything concrete
94 vorbis_comment_init(&pd
->vc
);
95 // vorbis_comment_add_tag() in libvorbis up to version 1.2.0
96 // incorrectly takes a char * instead of a const char *. to prevent
97 // compiler warnings we use const_cast<>(), since it's known that
98 // libvorbis does not change the arguments.
99 vorbis_comment_add_tag(&pd
->vc
, const_cast<char *>("COMMENT"), const_cast<char *>(tagComment
.toUtf8().constData()));
100 vorbis_comment_add_tag(&pd
->vc
, const_cast<char *>("DATE"), const_cast<char *>(tagTime
.toString("yyyy-MM-dd hh:mm").toAscii().constData()));
101 vorbis_comment_add_tag(&pd
->vc
, const_cast<char *>("GENRE"), const_cast<char *>("Speech (Skype Call)"));
103 vorbis_analysis_init(&pd
->vd
, &pd
->vi
);
104 vorbis_block_init(&pd
->vd
, &pd
->vb
);
106 std::srand(std::time(NULL
));
107 ogg_stream_init(&pd
->os
, std::rand());
110 ogg_packet header_comm
;
111 ogg_packet header_code
;
113 vorbis_analysis_headerout(&pd
->vd
, &pd
->vc
, &header
, &header_comm
, &header_code
);
114 ogg_stream_packetin(&pd
->os
, &header
);
115 ogg_stream_packetin(&pd
->os
, &header_comm
);
116 ogg_stream_packetin(&pd
->os
, &header_code
);
118 while (ogg_stream_flush(&pd
->os
, &pd
->og
) != 0) {
119 file
.write((const char *)pd
->og
.header
, pd
->og
.header_len
);
120 file
.write((const char *)pd
->og
.body
, pd
->og
.body_len
);
126 void VorbisWriter::close() {
127 if (!file
.isOpen()) {
128 debug("WARNING: VorbisWriter::close() called, but file not open");
133 debug("WARNING: VorbisWriter::close() called but no flush happened, flushing now");
134 QByteArray dummy1
, dummy2
;
135 write(dummy1
, dummy2
, 0, true);
138 AudioFileWriter::close();
141 bool VorbisWriter::write(QByteArray
&left
, QByteArray
&right
, long samples
, bool flush
) {
142 const long maxChunkSize
= 4096;
144 const qint16
*leftData
= (const qint16
*)left
.constData();
145 const qint16
*rightData
= stereo
? (const qint16
*)right
.constData() : NULL
;
147 long todoSamples
= samples
;
151 long chunkSize
= todoSamples
> maxChunkSize
? maxChunkSize
: todoSamples
;
152 todoSamples
-= chunkSize
;
154 if (chunkSize
== 0) {
158 vorbis_analysis_wrote(&pd
->vd
, 0);
160 float **buffer
= vorbis_analysis_buffer(&pd
->vd
, chunkSize
);
162 for (long i
= 0; i
< chunkSize
; i
++)
163 buffer
[0][i
] = (float)leftData
[i
] / 32768.0f
;
164 leftData
+= chunkSize
;
167 for (long i
= 0; i
< chunkSize
; i
++)
168 buffer
[1][i
] = (float)rightData
[i
] / 32768.0f
;
169 rightData
+= chunkSize
;
172 vorbis_analysis_wrote(&pd
->vd
, chunkSize
);
175 while (vorbis_analysis_blockout(&pd
->vd
, &pd
->vb
) == 1) {
176 vorbis_analysis(&pd
->vb
, NULL
);
177 vorbis_bitrate_addblock(&pd
->vb
);
179 while (vorbis_bitrate_flushpacket(&pd
->vd
, &pd
->op
)) {
180 ogg_stream_packetin(&pd
->os
, &pd
->op
);
182 while (!eos
&& ogg_stream_pageout(&pd
->os
, &pd
->og
) != 0) {
183 file
.write((const char *)pd
->og
.header
, pd
->og
.header_len
);
184 file
.write((const char *)pd
->og
.body
, pd
->og
.body_len
);
186 if (ogg_page_eos(&pd
->og
))
193 samplesWritten
+= samples
;
195 left
.remove(0, samples
* 2);
197 right
.remove(0, samples
* 2);