2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
7 #include "OggVorbisStream.hpp"
8 #include "FileSys/FileMan.hpp"
9 #include "FileSys/File.hpp"
16 // Static callback method for the IO read function used by libvorbis to read from ogg files.
17 // We use our streamed file here to load data into buffer, so libvorbis works directly with cf::FileSys files.
18 // ElementSize: Size in bytes of one element to read.
19 // NumRead: Number of elements to read.
20 static size_t FileSysRead(void* Buffer
, size_t ElementSize
, size_t NumRead
, void* DataSource
)
22 cf::FileSys::InFileI
* StreamFile
=(cf::FileSys::InFileI
*) DataSource
;
24 return StreamFile
->Read((char*)Buffer
, uint32_t(ElementSize
*NumRead
));
28 // Static callback method for the IO seek function used by libvorbis to seek in ogg files.
29 // We use our streamed file here to seek in a file, so libvorbis works directly with cf::FileSys files.
30 static int FileSysSeek(void* DataSource
, ogg_int64_t Offset
, int Whence
)
32 cf::FileSys::InFileI
* StreamFile
=(cf::FileSys::InFileI
*) DataSource
;
34 cf::FileSys::FileI::SeekFromT SeekFrom
=cf::FileSys::FileI::FROM_BEGINNING
;
37 case SEEK_CUR
: SeekFrom
=cf::FileSys::FileI::FROM_CURRENT_POS
; break;
38 case SEEK_END
: SeekFrom
=cf::FileSys::FileI::FROM_END
; break;
41 if (StreamFile
->Seek((int)Offset
, SeekFrom
))
42 return int(StreamFile
->GetPos());
48 // Static callback method for the IO tell function used by libvorbis to tell the current position in ogg files.
49 // We use our streamed file here to tell the position, so libvorbis works directly with cd::FileSys files.
50 static long FileSysTell(void* DataSource
)
52 cf::FileSys::InFileI
* StreamFile
=(cf::FileSys::InFileI
*) DataSource
;
54 return long(StreamFile
->GetPos());
58 static std::string
ErrorToStr(int ErrorCode
);
61 OggVorbisStreamT::OggVorbisStreamT(const std::string
& FileName
)
67 // Create struct of custom callback methods, so libvorbis works with cf::FileSys.
68 ov_callbacks CallBacks
;
70 CallBacks
.read_func
=&FileSysRead
;
71 CallBacks
.seek_func
=&FileSysSeek
;
72 CallBacks
.close_func
=NULL
; // Not set since we want to close the streamed file ourselves using the file manager.
73 CallBacks
.tell_func
=&FileSysTell
;
75 StreamFile
=cf::FileSys::FileMan
->OpenRead(FileName
);
78 int Result
=ov_test_callbacks(StreamFile
, &StreamHandle
, NULL
, 0, CallBacks
);;
81 throw std::runtime_error("OggVorbis: File is no OggVorbis file ("+ErrorToStr(Result
)+")");
83 Result
=ov_test_open(&StreamHandle
);
86 throw std::runtime_error("OggVorbis: Couldn't open stream ("+ErrorToStr(Result
)+")");
88 // Read rate and channels from the current bitstream.
89 StreamInfo
=ov_info(&StreamHandle
, BitStream
);
92 throw std::runtime_error("OggVorbis: Couldn't access stream information");
96 OggVorbisStreamT::~OggVorbisStreamT()
98 ov_clear(&StreamHandle
);
100 cf::FileSys::FileMan
->Close(StreamFile
);
104 int OggVorbisStreamT::Read(unsigned char* Buffer
, unsigned int Size
)
106 // libvorbis implementation doesn't actually write Size bytes from the stream in to the buffer when ov_read is called.
107 // Size is just a maximum that will never be exceeded.
108 // To make sure we read Size bytes from the stream into the buffer, we need to call ov_read until Size bytes are read.
110 unsigned int ReadBytes
=0;
112 int CurrentBitStream
=BitStream
;
113 long CurrentRate
=StreamInfo
->rate
;
115 while (ReadBytes
<Size
)
117 Result
=ov_read(&StreamHandle
, (char*)&Buffer
[ReadBytes
], Size
-ReadBytes
, 0, 2, 1, &BitStream
);
121 std::cout
<< "OggVorbis: Couldn't read from stream (Error: " << ErrorToStr(Result
) << ")\n";
125 if (Result
==0) return ReadBytes
;
129 if (CurrentBitStream
!=BitStream
)
131 StreamInfo
=ov_info(&StreamHandle
, BitStream
);
132 CurrentBitStream
=BitStream
;
134 if (CurrentRate
!=StreamInfo
->rate
)
136 CurrentRate
=StreamInfo
->rate
;
137 std::cout
<< "OggVorbis: Warning! Ogg file contains multiple streams with different sample rate, playback errors may occur...\n";
146 bool OggVorbisStreamT::Rewind()
148 int Result
=ov_time_seek(&StreamHandle
, 0.0);
152 std::cout
<< "OggVorbis: Couldn't rewind stream (Error: " << ErrorToStr(Result
) << ")\n";
160 unsigned int OggVorbisStreamT::GetChannels()
162 if (StreamInfo
==NULL
) return 0;
164 return StreamInfo
->channels
;
168 unsigned int OggVorbisStreamT::GetRate()
170 if (StreamInfo
==NULL
) return 0;
172 return StreamInfo
->rate
;
176 static std::string
ErrorToStr(int ErrorCode
)
180 case OV_EREAD
: return "OV_EREAD";
181 case OV_ENOTVORBIS
: return "OV_ENOTVORBIS";
182 case OV_EVERSION
: return "OV_EVERSION";
183 case OV_EBADHEADER
: return "OV_EBADHEADER";
184 case OV_EFAULT
: return "OV_EFAULT";
185 case OV_HOLE
: return "OV_HOLE";
186 case OV_EBADLINK
: return "OV_EBADLINK";
187 case OV_ENOSEEK
: return "OV_ENOSEEK";
188 case OV_EINVAL
: return "OV_EINVAL";
191 return "Unknown error";