CASampledSP
Loading...
Searching...
No Matches
CAURLInputStream.cpp
Go to the documentation of this file.
1/*
2 * =================================================
3 * Copyright 2011 tagtraum industries incorporated
4 * This file is part of CASampledSP.
5 *
6 * CASampledSP is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * CASampledSP is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with CASampledSP; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 * =================================================
20 *
21 * @author <a href="mailto:hs@tagtraum.com">Hendrik Schreiber</a>
22 */
23#include "com_tagtraum_casampledsp_CAURLInputStream.h"
24#include "CAUtils.h"
25
26static jfieldID nativeBufferFID = NULL;
27static jmethodID rewindMID = NULL;
28static jmethodID limitMID = NULL;
29
35static void init_ids(JNIEnv *env, jobject stream) {
36 if (nativeBufferFID == NULL || rewindMID == NULL || limitMID == NULL) {
37 nativeBufferFID = env->GetFieldID(env->GetObjectClass(stream), "nativeBuffer", "Ljava/nio/ByteBuffer;");
38 jclass bufferClass = env->FindClass("java/nio/Buffer");
39 rewindMID = env->GetMethodID(bufferClass, "rewind", "()Ljava/nio/Buffer;");
40 limitMID = env->GetMethodID(bufferClass, "limit", "(I)Ljava/nio/Buffer;");
41 }
42}
43
44
52JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_fillNativeBuffer(JNIEnv *env, jobject stream, jlong afioPtr) {
53#ifdef DEBUG
54 fprintf(stderr, "fillNativeBuffer: %llu\n", afioPtr);
55#endif
56
57 int res = 0;
58 jobject byteBuffer = NULL;
59 CAAudioFileIO *afio = (CAAudioFileIO*)afioPtr;
60 UInt32 ioNumberDataPackets;
61 UInt32 outNumBytes = 0;
62
63 init_ids(env, stream);
64
65 // get java-managed byte buffer reference
66 byteBuffer = env->GetObjectField(stream, nativeBufferFID);
67 if (byteBuffer == NULL) {
68 throwIOExceptionIfError(env, 1, "Failed to get native buffer");
69 goto bail;
70 }
71
72 // find out buffer's capacity
73 outNumBytes = env->GetDirectBufferCapacity(byteBuffer);
74 // get pointer to our java managed bytebuffer
75 afio->srcBuffer = (char *)env->GetDirectBufferAddress(byteBuffer);
76 if (afio->srcBuffer == NULL) {
77 throwIOExceptionIfError(env, 1, "Failed to get address for native buffer");
78 goto bail;
79 }
80
81 // figure out how much to read
82 ioNumberDataPackets = afio->numPacketsPerRead;
83
84 // do the actual read from the file
85 res = AudioFileReadPacketData(afio->afid, false, &outNumBytes, afio->pktDescs,
86 afio->pos, &ioNumberDataPackets, afio->srcBuffer);
87
88 if (res) {
89 throwIOExceptionIfError(env, res, "Failed to read packet data from file");
90 goto bail;
91 }
92
93 // advance input file packet position
94 afio->lastPos = afio->pos;
95 afio->pos += ioNumberDataPackets;
96 afio->srcBufferSize = outNumBytes;
97
98 // we already wrote to the buffer, now we still need to
99 // set new bytebuffer limit and position to 0.
100 env->CallObjectMethod(byteBuffer, rewindMID);
101 env->CallObjectMethod(byteBuffer, limitMID, outNumBytes);
102
103bail:
104 return;
105}
106
115JNIEXPORT jlong JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_open(JNIEnv *env, jobject stream, jstring url, jint bufferSize) {
116 int res = 0;
117 CFURLRef inputURLRef;
118 CAAudioFileIO *afio = new CAAudioFileIO;
119 UInt32 size;
120
121 afio->srcBufferSize = bufferSize;
122 afio->pos = 0;
123 afio->afid = NULL;
124 afio->pktDescs = NULL;
125 afio->cookie = NULL;
126 afio->cookieSize = 0;
127 afio->frameOffset = 0;
128 afio->numPacketsPerRead = 1;
129
130 // open file
131 ca_create_url_ref(env, url, inputURLRef);
132 res = AudioFileOpenURL(inputURLRef, 0x01, 0, &afio->afid); // 0x01 = read only
133 if (res) {
134 if (res == fnfErr || res == kAudioFileUnspecifiedError) {
135 throwFileNotFoundExceptionIfError(env, res, env->GetStringUTFChars(url, NULL));
136 } else {
137 throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to open audio file");
138 }
139 goto bail;
140 }
141
142 // get the source file format
143 size = sizeof(afio->srcFormat);
144 res = AudioFileGetProperty(afio->afid, kAudioFilePropertyDataFormat, &size, &afio->srcFormat);
145 if (res) {
146 throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain format for audio file");
147 goto bail;
148 }
149
150 // find out how many packets fit into the buffer
151 if (!afio->srcFormat.mBytesPerPacket) {
152#ifdef DEBUG
153 fprintf(stderr, "VBR\n");
154#endif
155 // format is VBR, so we need to get max size per packet
156 size = sizeof(afio->srcSizePerPacket);
157 res = AudioFileGetProperty(afio->afid, kAudioFilePropertyPacketSizeUpperBound, &size, &afio->srcSizePerPacket);
158 if (res) {
159 throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain packet size upper bound");
160 goto bail;
161 }
162 if (afio->srcSizePerPacket != 0) {
164 }
165#ifdef DEBUG
166 else {
167 fprintf(stderr, "VBR: srcSizePerPacket == 0!!\n");
168 }
169#endif
170 afio->pktDescs = new AudioStreamPacketDescription[afio->numPacketsPerRead];
171 }
172 else {
173#ifdef DEBUG
174 fprintf(stderr, "CBR\n");
175#endif
176 afio->srcSizePerPacket = afio->srcFormat.mBytesPerPacket;
177 if (afio->srcSizePerPacket != 0) {
179 }
180#ifdef DEBUG
181 else {
182 fprintf(stderr, "CBR: srcSizePerPacket == 0!!\n");
183 }
184#endif
185 }
186
187 // check for cookies
188 res = AudioFileGetPropertyInfo(afio->afid, kAudioFilePropertyMagicCookieData, &afio->cookieSize, NULL);
189 if (res && res != kAudioFileUnsupportedPropertyError) {
190 throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain cookie info from audio file");
191 goto bail;
192 }
193 res = 0;
194 if (!res && afio->cookieSize) {
195 afio->cookie = new char[afio->cookieSize];
196 res = AudioFileGetProperty(afio->afid, kAudioFilePropertyMagicCookieData, &afio->cookieSize, afio->cookie);
197 if (res) {
198 throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain cookie from audio file");
199 goto bail;
200 }
201 }
202
203
204bail:
205 if (res) {
206 if (afio->afid != NULL) {
207 AudioFileClose(afio->afid);
208 }
209 if (afio->pktDescs != NULL) {
210 delete afio->pktDescs;
211 }
212 if (afio->cookie != NULL) {
213 delete afio->cookie;
214 }
215 delete afio;
216 }
217
218#ifdef DEBUG
219 fprintf(stderr, "Opened: %llu\n", (jlong)afio);
220#endif
221
222 return (jlong)afio;
223}
224
233JNIEXPORT jboolean JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_isSeekable(JNIEnv *env, jobject stream, jlong afioPtr) {
234 return JNI_TRUE;
235}
236
245JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_seek(JNIEnv *env, jobject stream, jlong afioPtr, jlong microseconds) {
246 int res = 0;
247 UInt32 size;
248 CAAudioFileIO *afio = (CAAudioFileIO*)afioPtr;
249 AudioFramePacketTranslation translation;
250
251 translation.mFrame = (SInt64)(afio->srcFormat.mSampleRate * microseconds) / 1000000LL;
252
253#ifdef DEBUG
254 fprintf(stderr, "microseconds : %llu\n", microseconds);
255 fprintf(stderr, "translation.mFrame: %llu\n", translation.mFrame);
256#endif
257
258 size = sizeof(translation);
259 res = AudioFileGetProperty(afio->afid, kAudioFilePropertyFrameToPacket, &size, &translation);
260 if (res) {
261 throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to translate frame to packet.");
262 goto bail;
263 }
264 afio->pos = translation.mPacket;
265 afio->frameOffset = translation.mFrameOffsetInPacket;
266
267
268#ifdef DEBUG
269 fprintf(stderr, "frameOffset: %i\n", afio->frameOffset);
270 fprintf(stderr, "afio->pos : %llu\n", afio->pos);
271#endif
272
273 bail:
274
275 return;
276}
277
278
286JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_close(JNIEnv *env, jobject stream, jlong afioPtr) {
287
288#ifdef DEBUG
289 fprintf(stderr, "Closing: %llu\n", afioPtr);
290#endif
291 if (afioPtr == 0) return;
292
293 CAAudioFileIO *afio = (CAAudioFileIO*)afioPtr;
294 int res = AudioFileClose(afio->afid);
295 if (res) {
296 throwIOExceptionIfError(env, res, "Failed to close audio file");
297 }
298 if (afio->pktDescs != NULL) {
299 delete afio->pktDescs;
300 }
301 if (afio->cookie != NULL) {
302 delete afio->cookie;
303 }
304 delete afio;
305 return;
306}
JNIEXPORT jlong JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_open(JNIEnv *env, jobject stream, jstring url, jint bufferSize)
Opens the given URL via AudioFileOpenURL.
JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_seek(JNIEnv *env, jobject stream, jlong afioPtr, jlong microseconds)
Attempts the seek a given timestamp in the resource.
JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_fillNativeBuffer(JNIEnv *env, jobject stream, jlong afioPtr)
Callback to fill the native buffer.
JNIEXPORT jboolean JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_isSeekable(JNIEnv *env, jobject stream, jlong afioPtr)
Indicates whether the resource is seekable.
JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_close(JNIEnv *env, jobject stream, jlong afioPtr)
Closes this resource and frees all associated resources.
void throwFileNotFoundExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an IllegalArgumentException.
Definition CAUtils.cpp:90
void throwUnsupportedAudioFileExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an UnsupportedAudioFileException exception.
Definition CAUtils.cpp:39
void throwIOExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an IOException.
Definition CAUtils.cpp:56
void ca_create_url_ref(JNIEnv *env, jstring path, CFURLRef &urlRef)
Creates a CFURLRef from the given path.
Definition CAUtils.cpp:101
Central context representing the native peer to the Java CAURLInputStream object.
Definition CAUtils.h:78
AudioFileID afid
File id.
Definition CAUtils.h:79
char * cookie
Cookie.
Definition CAUtils.h:60
SInt64 lastPos
Last position (in packets)
Definition CAUtils.h:57
UInt32 numPacketsPerRead
Number of packets per read.
Definition CAUtils.h:55
char * srcBuffer
Source buffer.
Definition CAUtils.h:53
UInt32 frameOffset
Frame offset (needed for seeking to the middles of a packet)
Definition CAUtils.h:58
UInt32 srcSizePerPacket
Source size per packet.
Definition CAUtils.h:59
AudioStreamBasicDescription srcFormat
Source format.
Definition CAUtils.h:51
UInt32 cookieSize
Cookie size.
Definition CAUtils.h:61
SInt64 pos
Current position (in packets)
Definition CAUtils.h:56
UInt32 srcBufferSize
Source buffer size.
Definition CAUtils.h:54
AudioStreamPacketDescription * pktDescs
Packet descriptions.
Definition CAUtils.h:52