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
|
#include "vorbis_encoder.h"
#include <stdexcept>
#include <cstring>
VorbisEncoder::VorbisEncoder(RawAudioSource::p source_) : source(source_) {
vorbis_info_init(&vi);
vorbis_encode_init_vbr(&vi, source->get_channels(), source->get_samplerate(), .4);
vorbis_analysis_init(&dsp, &vi);
vorbis_block_init(&dsp, &vb);
vorbis_comment vc;
vorbis_comment_init(&vc);
// add tags and stuff here
ogg_packet op, op_comm, op_code;
vorbis_analysis_headerout(&dsp, &vc, &op, &op_comm, &op_code);
ogg_stream_init(&os, 0);
ogg_stream_packetin(&os, &op);
ogg_stream_packetin(&os, &op_comm);
ogg_stream_packetin(&os, &op_code);
headers_written = false;
}
VorbisEncoder::~VorbisEncoder() {
vorbis_info_clear(&vi);
vorbis_dsp_clear(&dsp);
vorbis_block_clear(&vb);
ogg_stream_clear(&os);
}
/** Write ogg stream pages to the buffer.
* \return Number of bytes written.
*/
std::size_t VorbisEncoder::write_pages(char *buf, std::size_t buf_size) {
std::size_t written = 0;
while(ogg_stream_pageout(&os, &og)) {
std::size_t total_len = (std::size_t)(og.header_len + og.body_len);
// TODO: Handle this somehow
if(written + total_len > buf_size) {
throw std::runtime_error("page too large");
}
std::memcpy(buf, og.header, og.header_len);
std::memcpy(buf+og.header_len, og.body, og.body_len);
written = og.header_len + og.body_len;
}
return written;
}
/** Read from source and write pages.
* Encodes a number of samples read from \c source,
* then calls \c write_pages to write completed pages.
* \return Number of bytes written by \c write_pages.
*/
std::size_t VorbisEncoder::encode(char *buf, std::size_t buf_size) {
const int samples_n = 1024;
int channels = source->get_channels();
// samples_n samples, channels, 2 byte per sample
char src_data[samples_n*channels*2];
int16_t *src_data_16 = (int16_t*)src_data;
std::streamsize src_read = source->read(src_data, samples_n*channels*2);
if(src_read <= 0) {
vorbis_analysis_wrote(&dsp, 0);
} else if(src_read % 4) {
throw std::runtime_error("invalid buffer size");
} else {
int samples_read = src_read / 4;
float **buffer = vorbis_analysis_buffer(&dsp, samples_read);
int sample;
for(sample = 0; sample < samples_read; sample++) {
for(int c = 0; c < channels; c++) {
int i = sample*channels+c;
if(i*channels >= src_read) break;
buffer[c][sample] = src_data_16[sample*channels+c]/32768.;
}
}
vorbis_analysis_wrote(&dsp, sample);
}
while(vorbis_analysis_blockout(&dsp, &vb) == 1) {
vorbis_analysis(&vb, NULL);
vorbis_bitrate_addblock(&vb);
ogg_packet op;
while(vorbis_bitrate_flushpacket(&dsp, &op) == 1) {
ogg_stream_packetin(&os, &op);
}
}
return write_pages(buf, buf_size);
}
std::size_t VorbisEncoder::read(char* buf, std::size_t buf_size) {
if(!headers_written) {
headers_written = true;
if(!ogg_stream_flush(&os, &og)) {
throw std::runtime_error("couldn't flush ogg stream header");
}
std::memcpy(buf, og.header, og.header_len);
std::memcpy(buf+og.header_len, og.body, og.body_len);
return og.header_len + og.body_len;
}
std::size_t written = 0;
// run until we have a page or reached end of stream
while(written == 0 && !ogg_page_eos(&og)) {
written = encode(buf, buf_size);
}
return written;
}
std::string VorbisEncoder::get_mime_type() {
return "application/ogg";
}
|