1 /** 2 Copyright (c) 2018 Clipsey (clipseypone@gmail.com) 3 4 Permission is hereby granted, free of charge, to any person or organization 5 obtaining a copy of the software and accompanying documentation covered by 6 this license (the "Software") to use, reproduce, display, distribute, 7 execute, and transmit the Software, and to prepare derivative works of the 8 Software, and to permit third-parties to whom the Software is furnished to 9 do so, all subject to the following: 10 11 The copyright notices in the Software and this entire statement, including 12 the above license grant, this restriction and the following disclaimer, 13 must be included in all copies of the Software, in whole or in part, and 14 all derivative works of the Software, unless such copies or derivative 15 works are solely in the form of machine-executable object code generated by 16 a source language processor. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 21 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 22 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 23 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 DEALINGS IN THE SOFTWARE. 25 */ 26 module ppc.backend.loaders.audio.ogg; 27 import derelict.ogg.ogg; 28 import derelict.vorbis; 29 import ppc.backend.loaders.audio; 30 import ppc.backend.cfile; 31 import ppc.types.audio; 32 33 /// Info associated with the OGG file 34 public struct OggInfo { 35 public: 36 /// Which version of OGG is used. 37 int oggVersion; 38 39 /// The amount of channels present 40 int channels; 41 42 /// Bitrate of OGG 43 int bitrate; 44 45 /// Bitrate upper value 46 size_t bitrateUpper; 47 48 /// Bitrate nominal value 49 size_t bitrateNominal; 50 51 /// Bitrate lower value 52 size_t bitrateLower; 53 54 /// Bitrate window 55 size_t bitrateWindow; 56 57 /// Length of OGG in samples 58 size_t pcmLength; 59 60 /// Length of OGG in bytes. 61 size_t rawLength; 62 63 /// Creates a new OggInfo 64 this(Ogg oggFile) { 65 66 vorbis_info* inf = ov_info(&oggFile.vfile, -1); 67 68 this.oggVersion = inf._version; 69 this.channels = inf.channels; 70 this.bitrate = inf.rate; 71 this.bitrateUpper = cast(size_t)(*inf).bitrate_upper; 72 this.bitrateNominal = cast(size_t)(*inf).bitrate_nominal; 73 this.bitrateLower = cast(size_t)(*inf).bitrate_lower; 74 this.bitrateWindow = cast(size_t)(*inf).bitrate_window; 75 76 this.pcmLength = cast(size_t)ov_pcm_total(&oggFile.vfile, -1); 77 this.rawLength = cast(size_t)ov_raw_total(&oggFile.vfile, -1); 78 } 79 } 80 81 private ov_callbacks callbacks; 82 83 /// An OGG audio file 84 public class Ogg : AudioStream { 85 private: 86 MemFile mfile; 87 OggVorbis_File vfile; 88 int currentSection; 89 90 public: 91 /// information related to the OGG file 92 OggInfo info; 93 94 /// Gets info in the generic AudioInfo format. 95 override AudioInfo genericInfo() { 96 return AudioInfo(AudioType.OGG, cast(ubyte)info.oggVersion, cast(ubyte)info.channels, info.bitrate, info.pcmLength, info.rawLength); 97 } 98 99 /// Construct file from memory 100 /// Check loadFile from ppc.backend.cfile if you want to load from a raw file. 101 this(MemFile file) { 102 mfile = file; 103 104 // Open file from memory 105 if (ov_open_callbacks(&mfile, &vfile, null, 0, callbacks) < 0) { 106 throw new Exception("Audio does not seem to be an ogg bitstream!..."); 107 } 108 109 // Load and set file info. 110 info = OggInfo(this); 111 } 112 113 ~this() { 114 if (ov_clear(&vfile) != 0) { 115 throw new Exception("Failed to do cleanup of vorbis file data!"); 116 } 117 } 118 119 /** 120 Read data of ogg stream in to specified buffer array by pointer. 121 returns amount of bytes read 122 */ 123 override long read(byte* ptr, uint bufferLength = 4096, uint bitdepth = SAMPLE_DEPTH_16BIT, bool signed = SAMPLE_SIGNED) { 124 import std.stdio; 125 // Read samples of size bufferLength to specified ptr 126 version(BigEndian){ 127 long bytesRead = ov_read(&vfile, ptr, cast(int)bufferLength, 0, 2, 1, ¤tSection);//SAMPLE_BIG_ENDIAN, bitdepth, cast(int)signed, ¤tSection); 128 } else { 129 long bytesRead = ov_read(&vfile, ptr, cast(int)bufferLength, 0, 2, 1, ¤tSection);//SAMPLE_LITTLE_ENDIAN, bitdepth, cast(int)signed, ¤tSection); 130 } 131 132 switch(bytesRead) { 133 case (OV_HOLE): 134 throw new Exception("Flow of data interrupted! Corrupt page?"); 135 case (OV_EBADLINK): 136 throw new Exception("Stream section or link corrupted!"); 137 case (OV_EINVAL): 138 throw new Exception("Initial file headers unreadable or corrupt!"); 139 default: 140 return bytesRead; 141 } 142 } 143 144 /// Seek to position in file 145 override void seekRaw(long position = 0) { 146 ov_raw_seek(&vfile, position); 147 } 148 149 /// Seek to a PCM position in file 150 override void seek(long position = 0) { 151 ov_pcm_seek(&vfile, position); 152 } 153 154 /// Returns the position in the stream 155 override size_t tellRaw() { 156 return cast(size_t)ov_raw_tell(&vfile); 157 } 158 159 /// Returns the position in the stream 160 override size_t tell() { 161 return cast(size_t)ov_pcm_tell(&vfile); 162 } 163 164 /** 165 Read data of ogg stream in to array of specified type. 166 This in untested and should probably not be used 167 see the read() function instead. 168 */ 169 deprecated("It's recommended not to use this function, but rather use the read() function instead.") 170 override T[] readArray(T)(uint bufferLength = 4096, uint bitdepth = SAMPLE_DEPTH_16BIT, bool signed = SAMPLE_SIGNED) if (isNumeric!T) { 171 T[] arr = new T[bufferLength]; 172 read(cast(byte*)&arr, bufferLength, bitdepth, signed); 173 return arr; 174 } 175 } 176 177 // TODO: Replace this with bindbc! 178 ///Load libogg and libvorbis 179 void loadOggFormat() { 180 DerelictOgg.load(); 181 DerelictVorbis.load(); 182 DerelictVorbisFile.load(); 183 } 184 185 186 // Keep one instance of the callback pointer instead of many. 187 void initOGG() { 188 loadOggFormat(); 189 callbacks.read_func = &MemFile.read; 190 callbacks.seek_func = &MemFile.seek; 191 callbacks.close_func = &MemFile.close; 192 callbacks.tell_func = &MemFile.tell; 193 }