1 /**
2 * Copyright (c) 2006-2019 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21 #ifdef LOVE_SUPPORT_COREAUDIO
22
23 // LOVE
24 #include "CoreAudioDecoder.h"
25
26 // C++
27 #include <vector>
28
29 namespace love
30 {
31 namespace sound
32 {
33 namespace lullaby
34 {
35
36 // Callbacks
37 namespace
38 {
readFunc(void * inClientData,SInt64 inPosition,UInt32 requestCount,void * buffer,UInt32 * actualCount)39 OSStatus readFunc(void *inClientData, SInt64 inPosition, UInt32 requestCount, void *buffer, UInt32 *actualCount)
40 {
41 Data *data = (Data *) inClientData;
42 SInt64 bytesLeft = data->getSize() - inPosition;
43
44 if (bytesLeft > 0)
45 {
46 UInt32 actualSize = bytesLeft >= requestCount ? requestCount : (UInt32) bytesLeft;
47 memcpy(buffer, (char *) data->getData() + inPosition, actualSize);
48 *actualCount = actualSize;
49 }
50 else
51 {
52 *actualCount = 0;
53 return kAudioFilePositionError;
54 }
55
56 return noErr;
57 }
58
getSizeFunc(void * inClientData)59 SInt64 getSizeFunc(void *inClientData)
60 {
61 Data *data = (Data *) inClientData;
62 return data->getSize();
63 }
64 } // anonymous namespace
65
CoreAudioDecoder(Data * data,int bufferSize)66 CoreAudioDecoder::CoreAudioDecoder(Data *data, int bufferSize)
67 : Decoder(data, bufferSize)
68 , audioFile(nullptr)
69 , extAudioFile(nullptr)
70 , inputInfo()
71 , outputInfo()
72 , duration(-2.0)
73 {
74 try
75 {
76 OSStatus err = noErr;
77
78 // Open the file represented by the Data.
79 err = AudioFileOpenWithCallbacks(data, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile);
80 if (err != noErr)
81 throw love::Exception("Could open audio file for decoding.");
82
83 // We want to use the Extended AudioFile API.
84 err = ExtAudioFileWrapAudioFileID(audioFile, false, &extAudioFile);
85
86 if (err != noErr)
87 throw love::Exception("Could open audio file for decoding.");
88
89 // Get the format of the audio data.
90 UInt32 propertySize = sizeof(inputInfo);
91 err = ExtAudioFileGetProperty(extAudioFile, kExtAudioFileProperty_FileDataFormat, &propertySize, &inputInfo);
92
93 if (err != noErr)
94 throw love::Exception("Could not determine file format.");
95
96 // Set the output format to 16 bit signed integer (native-endian) data.
97 // Keep the channel count and sample rate of the source format.
98 outputInfo.mSampleRate = inputInfo.mSampleRate;
99 outputInfo.mChannelsPerFrame = inputInfo.mChannelsPerFrame;
100
101 int bytes = (inputInfo.mBitsPerChannel == 8) ? 1 : 2;
102
103 outputInfo.mFormatID = kAudioFormatLinearPCM;
104 outputInfo.mBitsPerChannel = bytes * 8;
105 outputInfo.mBytesPerFrame = bytes * outputInfo.mChannelsPerFrame;
106 outputInfo.mFramesPerPacket = 1;
107 outputInfo.mBytesPerPacket = bytes * outputInfo.mChannelsPerFrame;
108 outputInfo.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
109
110 // unsigned 8-bit or signed 16-bit integer PCM data.
111 if (outputInfo.mBitsPerChannel == 16)
112 outputInfo.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
113
114 // Set the desired output format.
115 propertySize = sizeof(outputInfo);
116 err = ExtAudioFileSetProperty(extAudioFile, kExtAudioFileProperty_ClientDataFormat, propertySize, &outputInfo);
117
118 if (err != noErr)
119 throw love::Exception("Could not set decoder properties.");
120 }
121 catch (love::Exception &)
122 {
123 closeAudioFile();
124 throw;
125 }
126
127 sampleRate = (int) outputInfo.mSampleRate;
128 }
129
~CoreAudioDecoder()130 CoreAudioDecoder::~CoreAudioDecoder()
131 {
132 closeAudioFile();
133 }
134
closeAudioFile()135 void CoreAudioDecoder::closeAudioFile()
136 {
137 if (extAudioFile != nullptr)
138 ExtAudioFileDispose(extAudioFile);
139 else if (audioFile != nullptr)
140 AudioFileClose(audioFile);
141
142 extAudioFile = nullptr;
143 audioFile = nullptr;
144 }
145
accepts(const std::string & ext)146 bool CoreAudioDecoder::accepts(const std::string &ext)
147 {
148 UInt32 size = 0;
149 std::vector<UInt32> types;
150
151 // Get the size in bytes of the type array we're about to get.
152 OSStatus err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_ReadableTypes, sizeof(UInt32), nullptr, &size);
153 if (err != noErr)
154 return false;
155
156 types.resize(size / sizeof(UInt32));
157
158 // Get an array of supported types.
159 err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ReadableTypes, 0, nullptr, &size, &types[0]);
160 if (err != noErr)
161 return false;
162
163 // Turn the extension string into a CFStringRef.
164 CFStringRef extstr = CFStringCreateWithCString(nullptr, ext.c_str(), kCFStringEncodingUTF8);
165
166 CFArrayRef exts = nullptr;
167 size = sizeof(CFArrayRef);
168
169 for (UInt32 type : types)
170 {
171 // Get the extension strings for the type.
172 err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ExtensionsForType, sizeof(UInt32), &type, &size, &exts);
173 if (err != noErr)
174 continue;
175
176 // A type can have more than one extension string.
177 for (CFIndex i = 0; i < CFArrayGetCount(exts); i++)
178 {
179 CFStringRef value = (CFStringRef) CFArrayGetValueAtIndex(exts, i);
180
181 if (CFStringCompare(extstr, value, 0) == kCFCompareEqualTo)
182 {
183 CFRelease(extstr);
184 CFRelease(exts);
185 return true;
186 }
187 }
188
189 CFRelease(exts);
190 }
191
192 CFRelease(extstr);
193 return false;
194 }
195
clone()196 love::sound::Decoder *CoreAudioDecoder::clone()
197 {
198 return new CoreAudioDecoder(data.get(), bufferSize);
199 }
200
decode()201 int CoreAudioDecoder::decode()
202 {
203 int size = 0;
204
205 while (size < bufferSize)
206 {
207 AudioBufferList dataBuffer;
208 dataBuffer.mNumberBuffers = 1;
209 dataBuffer.mBuffers[0].mDataByteSize = bufferSize - size;
210 dataBuffer.mBuffers[0].mData = (char *) buffer + size;
211 dataBuffer.mBuffers[0].mNumberChannels = outputInfo.mChannelsPerFrame;
212
213 UInt32 frames = (bufferSize - size) / outputInfo.mBytesPerFrame;
214
215 if (ExtAudioFileRead(extAudioFile, &frames, &dataBuffer) != noErr)
216 return size;
217
218 if (frames == 0)
219 {
220 eof = true;
221 break;
222 }
223
224 size += frames * outputInfo.mBytesPerFrame;
225 }
226
227 return size;
228 }
229
seek(double s)230 bool CoreAudioDecoder::seek(double s)
231 {
232 OSStatus err = ExtAudioFileSeek(extAudioFile, (SInt64) (s * inputInfo.mSampleRate));
233
234 if (err == noErr)
235 {
236 eof = false;
237 return true;
238 }
239
240 return false;
241 }
242
rewind()243 bool CoreAudioDecoder::rewind()
244 {
245 OSStatus err = ExtAudioFileSeek(extAudioFile, 0);
246
247 if (err == noErr)
248 {
249 eof = false;
250 return true;
251 }
252
253 return false;
254 }
255
isSeekable()256 bool CoreAudioDecoder::isSeekable()
257 {
258 return true;
259 }
260
getChannelCount() const261 int CoreAudioDecoder::getChannelCount() const
262 {
263 return outputInfo.mChannelsPerFrame;
264 }
265
getBitDepth() const266 int CoreAudioDecoder::getBitDepth() const
267 {
268 return outputInfo.mBitsPerChannel;
269 }
270
getDuration()271 double CoreAudioDecoder::getDuration()
272 {
273 // Only calculate the duration if we haven't done so already.
274 if (duration == -2.0)
275 {
276 SInt64 samples = 0;
277 UInt32 psize = (UInt32) sizeof(samples);
278
279 OSStatus err = ExtAudioFileGetProperty(extAudioFile, kExtAudioFileProperty_FileLengthFrames, &psize, &samples);
280
281 if (err == noErr)
282 duration = (double) samples / (double) sampleRate;
283 else
284 duration = -1.0;
285 }
286
287 return duration;
288 }
289
290 } // lullaby
291 } // sound
292 } // love
293
294 #endif // LOVE_SUPPORT_COREAUDIO
295