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, &currentSection);//SAMPLE_BIG_ENDIAN, bitdepth, cast(int)signed, &currentSection);
128         } else {
129             long bytesRead = ov_read(&vfile, ptr, cast(int)bufferLength, 0, 2, 1, &currentSection);//SAMPLE_LITTLE_ENDIAN, bitdepth, cast(int)signed, &currentSection);
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 }