1 /*
2 Copyright (C) 2010-2013 The Exult Team
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 */
18 
19 #include "pent_include.h"
20 #include "OggAudioSample.h"
21 #include "headers/exceptions.h"
22 #include "databuf.h"
23 #include <SDL.h>
24 #include <new>
25 
26 namespace Pentagram {
27 
28 ov_callbacks OggAudioSample::callbacks = {
29 	&read_func,
30 	&seek_func,
31 	nullptr,
32 	&tell_func
33 };
34 
OggAudioSample(std::unique_ptr<IDataSource> oggdata_)35 OggAudioSample::OggAudioSample(std::unique_ptr<IDataSource> oggdata_)
36 	: AudioSample(nullptr, 0), oggdata(std::move(oggdata_))
37 {
38 	frame_size = 4096;
39 	decompressor_size = sizeof(OggDecompData);
40 	decompressor_align = alignof(OggDecompData);
41 	bits = 16;
42 	locked = false;
43 }
44 
OggAudioSample(std::unique_ptr<uint8[]> buffer,uint32 size)45 OggAudioSample::OggAudioSample(std::unique_ptr<uint8[]> buffer, uint32 size)
46 	: AudioSample(std::move(buffer), size), oggdata(nullptr)
47 {
48 	frame_size = 4096;
49 	decompressor_size = sizeof(OggDecompData);
50 	decompressor_align = alignof(OggDecompData);
51 	bits = 16;
52 	locked = false;
53 }
54 
read_func(void * ptr,size_t size,size_t nmemb,void * datasource)55 size_t OggAudioSample::read_func  (void *ptr, size_t size, size_t nmemb, void *datasource)
56 {
57 	auto *ids = static_cast<IDataSource*>(datasource);
58 	//if (ids->eof()) return 0;
59 	size_t limit = ids->getAvail();
60 	if (limit == 0) return 0;
61 	else if (limit < size*nmemb) nmemb = limit/size;
62 	ids->read(ptr,size*nmemb);
63 	return nmemb;
64 }
seek_func(void * datasource,ogg_int64_t offset,int whence)65 int    OggAudioSample::seek_func  (void *datasource, ogg_int64_t offset, int whence)
66 {
67 	auto *ids = static_cast<IDataSource*>(datasource);
68 	switch(whence)
69 	{
70 	case SEEK_SET:
71 		ids->seek(static_cast<size_t>(offset));
72 		return 0;
73 	case SEEK_END:
74 		ids->seek(ids->getSize()-static_cast<size_t>(offset));
75 		return 0;
76 	case SEEK_CUR:
77 		ids->skip(static_cast<size_t>(offset));
78 		return 0;
79 	}
80 	return -1;
81 }
tell_func(void * datasource)82 long   OggAudioSample::tell_func  (void *datasource)
83 {
84 	auto *ids = static_cast<IDataSource*>(datasource);
85 	return ids->getPos();
86 }
87 
isThis(IDataSource * oggdata)88 bool OggAudioSample::isThis(IDataSource *oggdata)
89 {
90 	OggVorbis_File vf;
91 	oggdata->seek(0);
92 	int res = ov_test_callbacks(oggdata,&vf,nullptr,0,callbacks);
93 	ov_clear(&vf);
94 
95 	return res == 0;
96 }
97 
98 
initDecompressor(void * DecompData) const99 void OggAudioSample::initDecompressor(void *DecompData) const
100 {
101 	auto *decomp = new (DecompData) OggDecompData;
102 
103 	if (locked)
104 		throw exult_exception("Attempted to play OggAudioSample on more than one channel at the same time.");
105 
106 	if (this->oggdata)
107 	{
108 		locked = true;
109 		decomp->datasource = this->oggdata.get();
110 	}
111 	else
112 	{
113 		decomp->datasource = new IBufferDataView(buffer, buffer_size);
114 	}
115 
116 	decomp->datasource->seek(0);
117 	ov_open_callbacks(decomp->datasource,&decomp->ov,nullptr,0,callbacks);
118 	decomp->bitstream = 0;
119 
120 	vorbis_info *info = ov_info(&decomp->ov,-1);
121 	sample_rate = decomp->last_rate = info->rate;
122 	stereo = decomp->last_stereo = info->channels == 2;
123 	decomp->freed = false;
124 }
125 
freeDecompressor(void * DecompData) const126 void OggAudioSample::freeDecompressor(void *DecompData) const
127 {
128 	auto *decomp = static_cast<OggDecompData *>(DecompData);
129 	if (decomp->freed)
130 		return;
131 	decomp->freed = true;
132 	ov_clear(&decomp->ov);
133 
134 	if (this->oggdata) locked = false;
135 	else delete decomp->datasource;
136 
137 	decomp->datasource = nullptr;
138 	decomp->~OggDecompData();
139 }
140 
decompressFrame(void * DecompData,void * samples) const141 uint32 OggAudioSample::decompressFrame(void *DecompData, void *samples) const
142 {
143 	auto *decomp = static_cast<OggDecompData *>(DecompData);
144 
145 	vorbis_info *info = ov_info(&decomp->ov,-1);
146 
147 	if (info == nullptr) return 0;
148 
149 	sample_rate = decomp->last_rate;
150 	stereo = decomp->last_stereo;
151 	decomp->last_rate = info->rate;
152 	decomp->last_stereo = info->channels == 2;
153 
154 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
155 	const int bigendianp = 0;
156 #else
157 	const int bigendianp = 1;
158 #endif
159 
160 	long count = ov_read(&decomp->ov,static_cast<char*>(samples),frame_size,bigendianp,2,1,&decomp->bitstream);
161 
162 	//if (count == OV_EINVAL || count == 0) {
163 	if (count <= 0) return 0;
164 	//else if (count < 0) {
165 	//	*(uint32*)samples = 0;
166 	//	return 1;
167 	//}
168 
169 	return count;
170 }
171 
172 }
173