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("output.format.vorbis.quality").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 // unfortunately, this takes a char * and not a const char *
96 vorbis_comment_add_tag(&pd
->vc
, "COMMENT", tagComment
.toUtf8().data());
97 vorbis_comment_add_tag(&pd
->vc
, "DATE", tagTime
.toString("yyyy-MM-dd hh:mm").toAscii().data());
98 vorbis_comment_add_tag(&pd
->vc
, "GENRE", "Speech (Skype Call)");
100 vorbis_analysis_init(&pd
->vd
, &pd
->vi
);
101 vorbis_block_init(&pd
->vd
, &pd
->vb
);
103 std::srand(std::time(NULL
));
104 ogg_stream_init(&pd
->os
, std::rand());
107 ogg_packet header_comm
;
108 ogg_packet header_code
;
110 vorbis_analysis_headerout(&pd
->vd
, &pd
->vc
, &header
, &header_comm
, &header_code
);
111 ogg_stream_packetin(&pd
->os
, &header
);
112 ogg_stream_packetin(&pd
->os
, &header_comm
);
113 ogg_stream_packetin(&pd
->os
, &header_code
);
115 while (ogg_stream_flush(&pd
->os
, &pd
->og
) != 0) {
116 file
.write((const char *)pd
->og
.header
, pd
->og
.header_len
);
117 file
.write((const char *)pd
->og
.body
, pd
->og
.body_len
);
123 void VorbisWriter::close() {
124 if (!file
.isOpen()) {
125 debug("WARNING: VorbisWriter::close() called, but file not open");
130 debug("WARNING: VorbisWriter::close() called but no flush happened, flushing now");
131 QByteArray dummy1
, dummy2
;
132 write(dummy1
, dummy2
, 0, true);
135 AudioFileWriter::close();
138 bool VorbisWriter::write(QByteArray
&left
, QByteArray
&right
, long samples
, bool flush
) {
139 const long maxChunkSize
= 4096;
141 const qint16
*leftData
= (const qint16
*)left
.constData();
142 const qint16
*rightData
= stereo
? (const qint16
*)right
.constData() : NULL
;
144 long todoSamples
= samples
;
148 long chunkSize
= todoSamples
> maxChunkSize
? maxChunkSize
: todoSamples
;
149 todoSamples
-= chunkSize
;
151 if (chunkSize
== 0) {
155 vorbis_analysis_wrote(&pd
->vd
, 0);
157 float **buffer
= vorbis_analysis_buffer(&pd
->vd
, chunkSize
);
159 for (long i
= 0; i
< chunkSize
; i
++)
160 buffer
[0][i
] = (float)leftData
[i
] / 32768.0f
;
161 leftData
+= chunkSize
;
164 for (long i
= 0; i
< chunkSize
; i
++)
165 buffer
[1][i
] = (float)rightData
[i
] / 32768.0f
;
166 rightData
+= chunkSize
;
169 vorbis_analysis_wrote(&pd
->vd
, chunkSize
);
172 while (vorbis_analysis_blockout(&pd
->vd
, &pd
->vb
) == 1) {
173 vorbis_analysis(&pd
->vb
, NULL
);
174 vorbis_bitrate_addblock(&pd
->vb
);
176 while (vorbis_bitrate_flushpacket(&pd
->vd
, &pd
->op
)) {
177 ogg_stream_packetin(&pd
->os
, &pd
->op
);
179 while (!eos
&& ogg_stream_pageout(&pd
->os
, &pd
->og
) != 0) {
180 file
.write((const char *)pd
->og
.header
, pd
->og
.header_len
);
181 file
.write((const char *)pd
->og
.body
, pd
->og
.body_len
);
183 if (ogg_page_eos(&pd
->og
))
190 samplesWritten
+= samples
;
192 left
.remove(0, samples
* 2);
194 right
.remove(0, samples
* 2);