CASampledSP
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  * FFSampledSP 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  * FFSampledSP 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 FFSampledSP; 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 
26 static jfieldID nativeBufferFID = NULL;
27 static jmethodID rewindMID = NULL;
28 static jmethodID limitMID = NULL;
29 
35 static 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 
52 JNIEXPORT 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 outNumBytes = BUFFER_SIZE; // max bytes to read
61  UInt32 ioNumberDataPackets;
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  // get pointer to our java managed bytebuffer
72  afio->srcBuffer = (char *)env->GetDirectBufferAddress(byteBuffer);
73  if (afio->srcBuffer == NULL) {
74  throwIOExceptionIfError(env, 1, "Failed to get address for native buffer");
75  goto bail;
76  }
77 
78 
79  // figure out how much to read
80  ioNumberDataPackets = afio->numPacketsPerRead;
81 
82  // do the actual read from the file
83  res = AudioFileReadPacketData(afio->afid, false, &outNumBytes, afio->pktDescs,
84  afio->pos, &ioNumberDataPackets, afio->srcBuffer);
85 
86  if (res) {
87  throwIOExceptionIfError(env, res, "Failed to read packet data from file");
88  goto bail;
89  }
90 
91  // advance input file packet position
92  afio->lastPos = afio->pos;
93  afio->pos += ioNumberDataPackets;
94  afio->srcBufferSize = outNumBytes;
95 
96  // we already wrote to the buffer, now we still need to
97  // set new bytebuffer limit and position to 0.
98  env->CallObjectMethod(byteBuffer, rewindMID);
99  env->CallObjectMethod(byteBuffer, limitMID, outNumBytes);
100 
101 bail:
102  return;
103 }
104 
113 JNIEXPORT jlong JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_open(JNIEnv *env, jobject stream, jstring url) {
114  int res = 0;
115  CFURLRef inputURLRef;
116  CAAudioFileIO *afio = new CAAudioFileIO;
117  UInt32 size;
118 
119  afio->srcBufferSize = BUFFER_SIZE;
120  afio->pos = 0;
121  afio->afid = NULL;
122  afio->pktDescs = NULL;
123  afio->cookie = NULL;
124  afio->cookieSize = 0;
125  afio->frameOffset = 0;
126  afio->numPacketsPerRead = 1;
127 
128  // open file
129  ca_create_url_ref(env, url, inputURLRef);
130  res = AudioFileOpenURL(inputURLRef, 0x01, 0, &afio->afid); // 0x01 = read only
131  if (res) {
132  if (res == fnfErr || res == kAudioFileUnspecifiedError) {
133  throwFileNotFoundExceptionIfError(env, res, env->GetStringUTFChars(url, NULL));
134  } else {
135  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to open audio file");
136  }
137  goto bail;
138  }
139 
140  // get the source file format
141  size = sizeof(afio->srcFormat);
142  res = AudioFileGetProperty(afio->afid, kAudioFilePropertyDataFormat, &size, &afio->srcFormat);
143  if (res) {
144  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain format for audio file");
145  goto bail;
146  }
147 
148  // find out how many packets fit into the buffer
149  if (!afio->srcFormat.mBytesPerPacket) {
150 #ifdef DEBUG
151  fprintf(stderr, "VBR\n");
152 #endif
153  // format is VBR, so we need to get max size per packet
154  size = sizeof(afio->srcSizePerPacket);
155  res = AudioFileGetProperty(afio->afid, kAudioFilePropertyPacketSizeUpperBound, &size, &afio->srcSizePerPacket);
156  if (res) {
157  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain packet size upper bound");
158  goto bail;
159  }
160  if (afio->srcSizePerPacket != 0) {
161  afio->numPacketsPerRead = afio->srcBufferSize / afio->srcSizePerPacket;
162  }
163 #ifdef DEBUG
164  else {
165  fprintf(stderr, "VBR: srcSizePerPacket == 0!!\n");
166  }
167 #endif
168  afio->pktDescs = new AudioStreamPacketDescription[afio->numPacketsPerRead];
169  }
170  else {
171 #ifdef DEBUG
172  fprintf(stderr, "CBR\n");
173 #endif
174  afio->srcSizePerPacket = afio->srcFormat.mBytesPerPacket;
175  if (afio->srcSizePerPacket != 0) {
176  afio->numPacketsPerRead = afio->srcBufferSize / afio->srcSizePerPacket;
177  }
178 #ifdef DEBUG
179  else {
180  fprintf(stderr, "CBR: srcSizePerPacket == 0!!\n");
181  }
182 #endif
183  }
184 
185  // check for cookies
186  res = AudioFileGetPropertyInfo(afio->afid, kAudioFilePropertyMagicCookieData, &afio->cookieSize, NULL);
187  if (res && res != kAudioFileUnsupportedPropertyError) {
188  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain cookie info from audio file");
189  goto bail;
190  }
191  res = 0;
192  if (!res && afio->cookieSize) {
193  afio->cookie = new char[afio->cookieSize];
194  res = AudioFileGetProperty(afio->afid, kAudioFilePropertyMagicCookieData, &afio->cookieSize, afio->cookie);
195  if (res) {
196  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain cookie from audio file");
197  goto bail;
198  }
199  }
200 
201 
202 bail:
203  if (res) {
204  if (afio->afid != NULL) {
205  AudioFileClose(afio->afid);
206  }
207  if (afio->pktDescs != NULL) {
208  delete afio->pktDescs;
209  }
210  if (afio->cookie != NULL) {
211  delete afio->cookie;
212  }
213  delete afio;
214  }
215 
216 #ifdef DEBUG
217  fprintf(stderr, "Opened: %llu\n", (jlong)afio);
218 #endif
219 
220  return (jlong)afio;
221 }
222 
231 JNIEXPORT jboolean JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_isSeekable(JNIEnv *env, jobject stream, jlong afioPtr) {
232  return JNI_TRUE;
233 }
234 
243 JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_seek(JNIEnv *env, jobject stream, jlong afioPtr, jlong microseconds) {
244  int res = 0;
245  UInt32 size;
246  CAAudioFileIO *afio = (CAAudioFileIO*)afioPtr;
247  AudioFramePacketTranslation translation;
248 
249  translation.mFrame = (SInt64)(afio->srcFormat.mSampleRate * microseconds) / 1000000LL;
250 
251 #ifdef DEBUG
252  fprintf(stderr, "microseconds : %llu\n", microseconds);
253  fprintf(stderr, "translation.mFrame: %llu\n", translation.mFrame);
254 #endif
255 
256  size = sizeof(translation);
257  res = AudioFileGetProperty(afio->afid, kAudioFilePropertyFrameToPacket, &size, &translation);
258  if (res) {
259  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to translate frame to packet.");
260  goto bail;
261  }
262  afio->pos = translation.mPacket;
263  afio->frameOffset = translation.mFrameOffsetInPacket;
264 
265 
266 #ifdef DEBUG
267  fprintf(stderr, "frameOffset: %i\n", afio->frameOffset);
268  fprintf(stderr, "afio->pos : %llu\n", afio->pos);
269 #endif
270 
271  bail:
272 
273  return;
274 }
275 
276 
284 JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_close(JNIEnv *env, jobject stream, jlong afioPtr) {
285 
286 #ifdef DEBUG
287  fprintf(stderr, "Closing: %llu\n", afioPtr);
288 #endif
289  if (afioPtr == 0) return;
290 
291  CAAudioFileIO *afio = (CAAudioFileIO*)afioPtr;
292  int res = AudioFileClose(afio->afid);
293  if (res) {
294  throwIOExceptionIfError(env, res, "Failed to close audio file");
295  }
296  if (afio->pktDescs != NULL) {
297  delete afio->pktDescs;
298  }
299  if (afio->cookie != NULL) {
300  delete afio->cookie;
301  }
302  delete afio;
303  return;
304 }
JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_close(JNIEnv *env, jobject stream, jlong afioPtr)
Closes this resource and frees all associated resources.
#define BUFFER_SIZE
Definition: CAUtils.h:44
UInt32 cookieSize
Cookie size.
Definition: CAUtils.h:63
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.
AudioStreamBasicDescription srcFormat
Source format.
Definition: CAUtils.h:53
UInt32 frameOffset
Frame offset (needed for seeking to the middles of a packet)
Definition: CAUtils.h:60
char * srcBuffer
Source buffer.
Definition: CAUtils.h:55
void ca_create_url_ref(JNIEnv *env, jstring path, CFURLRef &urlRef)
Creates a CFURLRef from the given path.
Definition: CAUtils.cpp:101
void throwUnsupportedAudioFileExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an UnsupportedAudioFileException exception.
Definition: CAUtils.cpp:39
Central context representing the native peer to the Java CAURLInputStream object. ...
Definition: CAUtils.h:79
UInt32 srcBufferSize
Source buffer size.
Definition: CAUtils.h:56
JNIEXPORT jlong JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_open(JNIEnv *env, jobject stream, jstring url)
Opens the given URL via AudioFileOpenURL.
char * cookie
Cookie.
Definition: CAUtils.h:62
AudioStreamPacketDescription * pktDescs
Packet descriptions.
Definition: CAUtils.h:54
AudioFileID afid
File id.
Definition: CAUtils.h:81
JNIEXPORT jboolean JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_isSeekable(JNIEnv *env, jobject stream, jlong afioPtr)
Indicates whether the resource is seekable.
UInt32 srcSizePerPacket
Source size per packet.
Definition: CAUtils.h:61
void throwIOExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an IOException.
Definition: CAUtils.cpp:56
SInt64 lastPos
Last position (in packets)
Definition: CAUtils.h:59
JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_fillNativeBuffer(JNIEnv *env, jobject stream, jlong afioPtr)
Callback to fill the native buffer.
SInt64 pos
Current position (in packets)
Definition: CAUtils.h:58
void throwFileNotFoundExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an IllegalArgumentException.
Definition: CAUtils.cpp:90
UInt32 numPacketsPerRead
Number of packets per read.
Definition: CAUtils.h:57