LCOV - code coverage report
Current view: top level - source/soundmanager/data - ogg.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 168 0.6 %
Date: 2023-01-19 00:18:29 Functions: 2 39 5.1 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. 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 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : #include "precompiled.h"
      18             : 
      19             : #include "ogg.h"
      20             : 
      21             : #if CONFIG2_AUDIO
      22             : 
      23             : #include "lib/byte_order.h"
      24             : #include "lib/external_libraries/openal.h"
      25             : #include "lib/external_libraries/vorbis.h"
      26             : #include "lib/file/file_system.h"
      27             : #include "lib/file/io/io.h"
      28             : #include "lib/file/vfs/vfs_util.h"
      29             : #include "maths/MathUtil.h"
      30             : #include "ps/CLogger.h"
      31             : #include "ps/Filesystem.h"
      32             : 
      33             : 
      34           0 : static Status LibErrorFromVorbis(int err)
      35             : {
      36           0 :     switch(err)
      37             :     {
      38           0 :     case 0:
      39           0 :         return INFO::OK;
      40           0 :     case OV_HOLE:
      41           0 :         return ERR::AGAIN;
      42           0 :     case OV_EREAD:
      43           0 :         return ERR::IO;
      44           0 :     case OV_EFAULT:
      45           0 :         return ERR::LOGIC;
      46           0 :     case OV_EIMPL:
      47           0 :         return ERR::NOT_SUPPORTED;
      48           0 :     case OV_EINVAL:
      49           0 :         return ERR::INVALID_PARAM;
      50           0 :     case OV_ENOTVORBIS:
      51           0 :         return ERR::NOT_SUPPORTED;
      52           0 :     case OV_EBADHEADER:
      53           0 :         return ERR::CORRUPTED;
      54           0 :     case OV_EVERSION:
      55           0 :         return ERR::INVALID_VERSION;
      56           0 :     case OV_ENOTAUDIO:
      57           0 :         return ERR::_1;
      58           0 :     case OV_EBADPACKET:
      59           0 :         return ERR::_2;
      60           0 :     case OV_EBADLINK:
      61           0 :         return ERR::_3;
      62           0 :     case OV_ENOSEEK:
      63           0 :         return ERR::_4;
      64           0 :     default:
      65           0 :         return ERR::FAIL;
      66             :     }
      67             : }
      68             : 
      69             : 
      70             : //-----------------------------------------------------------------------------
      71             : 
      72           0 : class VorbisFileAdapter
      73             : {
      74             : public:
      75           0 :     VorbisFileAdapter(const PFile& openedFile)
      76           0 :         : file(openedFile)
      77           0 :         , size(FileSize(openedFile->Pathname()))
      78           0 :         , offset(0)
      79             :     {
      80           0 :     }
      81             : 
      82           0 :     static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context)
      83             :     {
      84           0 :         VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
      85           0 :         const off_t sizeRequested = numItems*itemSize;
      86           0 :         const off_t sizeRemaining = adapter->size - adapter->offset;
      87           0 :         const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining);
      88             : 
      89           0 :         io::Operation op(*adapter->file.get(), bufferToFill, sizeToRead, adapter->offset);
      90           0 :         if(io::Run(op) == INFO::OK)
      91             :         {
      92           0 :             adapter->offset += sizeToRead;
      93           0 :             return sizeToRead;
      94             :         }
      95             : 
      96           0 :         errno = EIO;
      97           0 :         return 0;
      98             :     }
      99             : 
     100           0 :     static int Seek(void* context, ogg_int64_t offset, int whence)
     101             :     {
     102           0 :         VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
     103             : 
     104           0 :         off_t origin = 0;
     105           0 :         switch(whence)
     106             :         {
     107           0 :         case SEEK_SET:
     108           0 :             origin = 0;
     109           0 :             break;
     110           0 :         case SEEK_CUR:
     111           0 :             origin = adapter->offset;
     112           0 :             break;
     113           0 :         case SEEK_END:
     114           0 :             origin = adapter->size+1;
     115           0 :             break;
     116           0 :             NODEFAULT;
     117             :         }
     118             : 
     119           0 :         adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size);
     120           0 :         return 0;
     121             :     }
     122             : 
     123           0 :     static int Close(void* context)
     124             :     {
     125           0 :         VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
     126           0 :         adapter->file.reset();
     127           0 :         return 0;   // return value is ignored
     128             :     }
     129             : 
     130           0 :     static long Tell(void* context)
     131             :     {
     132           0 :         VorbisFileAdapter* adapter = static_cast<VorbisFileAdapter*>(context);
     133           0 :         return adapter->offset;
     134             :     }
     135             : 
     136             : private:
     137             :     PFile file;
     138             :     off_t size;
     139             :     off_t offset;
     140             : };
     141             : 
     142             : //-----------------------------------------------------------------------------
     143             : 
     144           0 : class VorbisBufferAdapter
     145             : {
     146             : public:
     147           0 :     VorbisBufferAdapter(const std::shared_ptr<u8>& buffer, size_t size)
     148           0 :         : buffer(buffer)
     149             :         , size(size)
     150           0 :         , offset(0)
     151             :     {
     152           0 :     }
     153             : 
     154           0 :     static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context)
     155             :     {
     156           0 :         VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
     157             : 
     158           0 :         const off_t sizeRequested = numItems*itemSize;
     159           0 :         const off_t sizeRemaining = adapter->size - adapter->offset;
     160           0 :         const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining);
     161             : 
     162           0 :         memcpy(bufferToFill, adapter->buffer.get() + adapter->offset, sizeToRead);
     163             : 
     164           0 :         adapter->offset += sizeToRead;
     165           0 :         return sizeToRead;
     166             :     }
     167             : 
     168           0 :     static int Seek(void* context, ogg_int64_t offset, int whence)
     169             :     {
     170           0 :         VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
     171             : 
     172           0 :         off_t origin = 0;
     173           0 :         switch(whence)
     174             :         {
     175           0 :         case SEEK_SET:
     176           0 :             origin = 0;
     177           0 :             break;
     178           0 :         case SEEK_CUR:
     179           0 :             origin = adapter->offset;
     180           0 :             break;
     181           0 :         case SEEK_END:
     182           0 :             origin = adapter->size+1;
     183           0 :             break;
     184           0 :             NODEFAULT;
     185             :         }
     186             : 
     187           0 :         adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size);
     188           0 :         return 0;
     189             :     }
     190             : 
     191           0 :     static int Close(void* context)
     192             :     {
     193           0 :         VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
     194           0 :         adapter->buffer.reset();
     195           0 :         return 0;   // return value is ignored
     196             :     }
     197             : 
     198           0 :     static long Tell(void* context)
     199             :     {
     200           0 :         VorbisBufferAdapter* adapter = static_cast<VorbisBufferAdapter*>(context);
     201           0 :         return adapter->offset;
     202             :     }
     203             : 
     204             : private:
     205             :     std::shared_ptr<u8> buffer;
     206             :     off_t size;
     207             :     off_t offset;
     208             : };
     209             : 
     210             : 
     211             : //-----------------------------------------------------------------------------
     212             : 
     213             : template <typename Adapter>
     214           0 : class OggStreamImpl : public OggStream
     215             : {
     216             : public:
     217           0 :     OggStreamImpl(const Adapter& adapter)
     218           0 :         : adapter(adapter)
     219             :     {
     220           0 :         m_fileEOF = false;
     221           0 :         info = NULL;
     222           0 :     }
     223             : 
     224           0 :     Status Close()
     225             :     {
     226           0 :         ov_clear( &vf );
     227             : 
     228           0 :         return 0;
     229             :     }
     230             : 
     231           0 :     Status Open()
     232             :     {
     233             :         ov_callbacks callbacks;
     234           0 :         callbacks.read_func = Adapter::Read;
     235           0 :         callbacks.close_func = Adapter::Close;
     236           0 :         callbacks.seek_func = Adapter::Seek;
     237           0 :         callbacks.tell_func = Adapter::Tell;
     238           0 :         const int ret = ov_open_callbacks(&adapter, &vf, 0, 0, callbacks);
     239           0 :         if(ret != 0)
     240           0 :             WARN_RETURN(LibErrorFromVorbis(ret));
     241             : 
     242           0 :         const int link = -1;    // retrieve info for current bitstream
     243           0 :         info = ov_info(&vf, link);
     244           0 :         if(!info)
     245           0 :             WARN_RETURN(ERR::INVALID_HANDLE);
     246             : 
     247           0 :         return INFO::OK;
     248             :     }
     249             : 
     250           0 :     virtual ALenum Format()
     251             :     {
     252           0 :         return (info->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
     253             :     }
     254             : 
     255           0 :     virtual ALsizei SamplingRate()
     256             :     {
     257           0 :         return info->rate;
     258             :     }
     259           0 :     virtual bool atFileEOF()
     260             :     {
     261           0 :         return m_fileEOF;
     262             :     }
     263             : 
     264           0 :     virtual Status ResetFile()
     265             :     {
     266           0 :         ov_time_seek( &vf, 0 );
     267           0 :         m_fileEOF = false;
     268           0 :         return INFO::OK;
     269             :     }
     270             : 
     271           0 :     virtual Status GetNextChunk(u8* buffer, size_t size)
     272             :     {
     273             :         // we may have to call ov_read multiple times because it
     274             :         // treats the buffer size "as a limit and not a request"
     275           0 :         size_t bytesRead = 0;
     276           0 :         for(;;)
     277             :         {
     278           0 :             const int isBigEndian = (BYTE_ORDER == BIG_ENDIAN);
     279           0 :             const int wordSize = sizeof(i16);
     280           0 :             const int isSigned = 1;
     281             :             int bitstream;  // unused
     282           0 :             const int ret = ov_read(&vf, (char*)buffer+bytesRead, int(size-bytesRead), isBigEndian, wordSize, isSigned, &bitstream);
     283           0 :             if(ret == 0) {  // EOF
     284           0 :                 m_fileEOF = true;
     285           0 :                 return (Status)bytesRead;
     286             :             }
     287           0 :             else if(ret < 0)
     288           0 :                 WARN_RETURN(LibErrorFromVorbis(ret));
     289             :             else    // success
     290             :             {
     291           0 :                 bytesRead += ret;
     292           0 :                 if(bytesRead == size)
     293           0 :                     return (Status)bytesRead;
     294             :             }
     295             :         }
     296             :     }
     297             : 
     298             : private:
     299             :     Adapter adapter;
     300             :     OggVorbis_File vf;
     301             :     vorbis_info* info;
     302             :     bool m_fileEOF;
     303             : };
     304             : 
     305             : 
     306             : //-----------------------------------------------------------------------------
     307             : 
     308           0 : Status OpenOggStream(const OsPath& pathname, OggStreamPtr& stream)
     309             : {
     310           0 :     PFile file(new File);
     311           0 :     RETURN_STATUS_IF_ERR(file->Open(pathname, L'r'));
     312             : 
     313           0 :     std::shared_ptr<OggStreamImpl<VorbisFileAdapter>> tmp = std::make_shared<OggStreamImpl<VorbisFileAdapter>>(VorbisFileAdapter(file));
     314           0 :     RETURN_STATUS_IF_ERR(tmp->Open());
     315           0 :     stream = tmp;
     316           0 :     return INFO::OK;
     317             : }
     318             : 
     319           0 : Status OpenOggNonstream(const PIVFS& vfs, const VfsPath& pathname, OggStreamPtr& stream)
     320             : {
     321           0 :     std::shared_ptr<u8> contents;
     322             :     size_t size;
     323           0 :     RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, contents, size));
     324             : 
     325           0 :     std::shared_ptr<OggStreamImpl<VorbisBufferAdapter>> tmp = std::make_shared<OggStreamImpl<VorbisBufferAdapter>>(VorbisBufferAdapter(contents, size));
     326           0 :     RETURN_STATUS_IF_ERR(tmp->Open());
     327           0 :     stream = tmp;
     328           0 :     return INFO::OK;
     329           3 : }
     330             : 
     331             : #endif  // CONFIG2_AUDIO
     332             : 

Generated by: LCOV version 1.13