1 #include <algorithm> 2 #include <string.h> 3 #include "input_ogg.h" 4 #include "types.h" 5 #include "utility.h" 6 7 8 namespace audiere { 9 10 typedef ogg_int64_t int64_t; 11 12 OGGInputStream()13 OGGInputStream::OGGInputStream() { 14 m_file = 0; 15 16 m_channel_count = 0; 17 m_sample_rate = 0; 18 m_sample_format = SF_S16; 19 } 20 21 ~OGGInputStream()22 OGGInputStream::~OGGInputStream() { 23 // were we initialized successfully? 24 if (m_file) { 25 ov_clear(&m_vorbis_file); 26 } 27 } 28 29 30 bool initialize(FilePtr file)31 OGGInputStream::initialize(FilePtr file) { 32 m_file = file; 33 34 // custom ogg vorbis callbacks 35 ov_callbacks callbacks; 36 callbacks.read_func = FileRead; 37 callbacks.seek_func = FileSeek; 38 callbacks.close_func = FileClose; 39 callbacks.tell_func = FileTell; 40 41 // open ogg vorbis stream 42 int result = ov_open_callbacks(file.get(), &m_vorbis_file, 0, 0, callbacks); 43 if (result) { 44 m_file = 0; 45 return false; 46 } 47 48 // calculate stream type 49 vorbis_info* vi = ov_info(&m_vorbis_file, -1); 50 if (!vi) { 51 ov_clear(&m_vorbis_file); 52 m_file = 0; 53 return false; 54 } 55 56 // read metadata 57 vorbis_comment* comments = ov_comment(&m_vorbis_file, -1); 58 if (comments) { 59 addTag("vendor", comments->vendor, "vorbis"); 60 for (int i = 0; i < comments->comments; ++i) { 61 std::string kv = comments->user_comments[i]; 62 std::string key; 63 std::string value; 64 std::string::iterator eq = std::find(kv.begin(), kv.end(), '='); 65 if (eq != kv.end()) { 66 key.assign(kv.begin(), eq); 67 value.assign(eq + 1, kv.end()); 68 } else { 69 key = kv; 70 } 71 addTag(key, value, "vorbis"); 72 } 73 } 74 75 m_channel_count = vi->channels; 76 m_sample_rate = vi->rate; 77 m_sample_format = SF_S16; // see constructor 78 79 return true; 80 } 81 82 83 void getFormat(int & channel_count,int & sample_rate,SampleFormat & sample_format)84 OGGInputStream::getFormat( 85 int& channel_count, 86 int& sample_rate, 87 SampleFormat& sample_format) 88 { 89 channel_count = m_channel_count; 90 sample_rate = m_sample_rate; 91 sample_format = m_sample_format; 92 } 93 94 95 int doRead(int frame_count,void * buffer)96 OGGInputStream::doRead(int frame_count, void* buffer) { 97 int sample_size = m_channel_count * GetSampleSize(m_sample_format); 98 99 u8* out = (u8*)buffer; 100 101 int samples_left = frame_count; 102 int total_read = 0; 103 while (samples_left > 0) { 104 105 // check to see if the stream format has changed 106 // if so, treat it as an EndOfStream 107 vorbis_info* vi = ov_info(&m_vorbis_file, -1); 108 if (vi && (m_sample_rate != vi->rate || m_channel_count != vi->channels)) { 109 break; 110 } 111 112 #ifdef WORDS_BIGENDIAN 113 #define ENDIANNESS 1 114 #else 115 #define ENDIANNESS 0 116 #endif 117 118 int bitstream; 119 long result = ov_read( 120 &m_vorbis_file, 121 (char*)out, 122 samples_left * sample_size, 123 ENDIANNESS, 124 2, // 16-bit 125 1, // signed 126 &bitstream); 127 128 if (result < 0) { 129 // if error, ignore it 130 continue; 131 } else if (result == 0) { 132 break; 133 } 134 135 u32 samples_read = (u32)(result / sample_size); 136 137 out += samples_read * sample_size; 138 samples_left -= samples_read; 139 total_read += samples_read; 140 } 141 142 return total_read; 143 } 144 145 146 void reset()147 OGGInputStream::reset() { 148 ov_pcm_seek(&m_vorbis_file, 0); 149 } 150 151 152 bool isSeekable()153 OGGInputStream::isSeekable() { 154 return (ov_seekable(&m_vorbis_file) != 0); 155 } 156 157 158 int getLength()159 OGGInputStream::getLength() { 160 if (isSeekable()) { 161 return static_cast<int>(ov_pcm_total(&m_vorbis_file, -1)); 162 } else { 163 return 0; 164 } 165 } 166 167 168 void setPosition(int position)169 OGGInputStream::setPosition(int position) { 170 if (isSeekable()) { 171 ov_pcm_seek(&m_vorbis_file, position); 172 } 173 } 174 175 176 int getPosition()177 OGGInputStream::getPosition() { 178 if (isSeekable()) { 179 return static_cast<int>(ov_pcm_tell(&m_vorbis_file)); 180 } else { 181 return 0; 182 } 183 } 184 185 186 size_t FileRead(void * buffer,size_t size,size_t n,void * opaque)187 OGGInputStream::FileRead(void* buffer, size_t size, size_t n, void* opaque) { 188 File* file = reinterpret_cast<File*>(opaque); 189 return file->read(buffer, size * n) / size; 190 } 191 192 193 int FileSeek(void * opaque,ogg_int64_t offset,int whence)194 OGGInputStream::FileSeek(void* opaque, ogg_int64_t offset, int whence) { 195 File* file = reinterpret_cast<File*>(opaque); 196 File::SeekMode type; 197 switch (whence) { 198 case SEEK_SET: type = File::BEGIN; break; 199 case SEEK_CUR: type = File::CURRENT; break; 200 case SEEK_END: type = File::END; break; 201 default: return -1; 202 } 203 return (file->seek((int)offset, type) ? 0 : -1); 204 } 205 206 207 int FileClose(void *)208 OGGInputStream::FileClose(void* /*opaque*/) { 209 // we don't have to do anything 210 // (read: don't trust ogg vorbis with handling file closes) 211 return 0; 212 } 213 214 215 long FileTell(void * opaque)216 OGGInputStream::FileTell(void* opaque) { 217 File* file = reinterpret_cast<File*>(opaque); 218 return file->tell(); 219 } 220 221 } 222