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