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  */
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 
127  // open file
128  ca_create_url_ref(env, url, inputURLRef);
129  res = AudioFileOpenURL(inputURLRef, 0x01, 0, &afio->afid); // 0x01 = read only
130  if (res) {
131  if (res == fnfErr || res == kAudioFileUnspecifiedError) {
132  throwFileNotFoundExceptionIfError(env, res, env->GetStringUTFChars(url, NULL));
133  } else {
134  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to open audio file");
135  }
136  goto bail;
137  }
138 
139  // get the source file format
140  size = sizeof(afio->srcFormat);
141  res = AudioFileGetProperty(afio->afid, kAudioFilePropertyDataFormat, &size, &afio->srcFormat);
142  if (res) {
143  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain format for audio file");
144  goto bail;
145  }
146 
147  // find out how many packets fit into the buffer
148  if (!afio->srcFormat.mBytesPerPacket) {
149  // format is VBR, so we need to get max size per packet
150  size = sizeof(afio->srcSizePerPacket);
151  res = AudioFileGetProperty(afio->afid, kAudioFilePropertyPacketSizeUpperBound, &size, &afio->srcSizePerPacket);
152  if (res) {
153  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain packet size upper bound");
154  goto bail;
155  }
156  afio->numPacketsPerRead = afio->srcBufferSize / afio->srcSizePerPacket;
157  afio->pktDescs = new AudioStreamPacketDescription [afio->numPacketsPerRead];
158  }
159  else {
160  afio->srcSizePerPacket = afio->srcFormat.mBytesPerPacket;
161  afio->numPacketsPerRead = afio->srcBufferSize / afio->srcSizePerPacket;
162  }
163 
164  // check for cookies
165  res = AudioFileGetPropertyInfo(afio->afid, kAudioFilePropertyMagicCookieData, &afio->cookieSize, NULL);
166  if (res && res != kAudioFileUnsupportedPropertyError) {
167  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain cookie info from audio file");
168  goto bail;
169  }
170  res = 0;
171  if (!res && afio->cookieSize) {
172  afio->cookie = new char[afio->cookieSize];
173  res = AudioFileGetProperty(afio->afid, kAudioFilePropertyMagicCookieData, &afio->cookieSize, afio->cookie);
174  if (res) {
175  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to obtain cookie from audio file");
176  goto bail;
177  }
178  }
179 
180 
181 bail:
182  if (res) {
183  if (afio->afid != NULL) {
184  AudioFileClose(afio->afid);
185  }
186  if (afio->pktDescs != NULL) {
187  delete afio->pktDescs;
188  }
189  if (afio->cookie != NULL) {
190  delete afio->cookie;
191  }
192  delete afio;
193  }
194 
195 #ifdef DEBUG
196  fprintf(stderr, "Opened: %llu\n", (jlong)afio);
197 #endif
198 
199  return (jlong)afio;
200 }
201 
210 JNIEXPORT jboolean JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_isSeekable(JNIEnv *env, jobject stream, jlong afioPtr) {
211  return JNI_TRUE;
212 }
213 
222 JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_seek(JNIEnv *env, jobject stream, jlong afioPtr, jlong microseconds) {
223  int res = 0;
224  UInt32 size;
225  CAAudioFileIO *afio = (CAAudioFileIO*)afioPtr;
226  AudioFramePacketTranslation translation;
227 
228  translation.mFrame = (SInt64)(afio->srcFormat.mSampleRate * microseconds) / 1000000LL;
229 
230 #ifdef DEBUG
231  fprintf(stderr, "microseconds : %llu\n", microseconds);
232  fprintf(stderr, "translation.mFrame: %llu\n", translation.mFrame);
233 #endif
234 
235  size = sizeof(translation);
236  res = AudioFileGetProperty(afio->afid, kAudioFilePropertyFrameToPacket, &size, &translation);
237  if (res) {
238  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to translate frame to packet.");
239  goto bail;
240  }
241  afio->pos = translation.mPacket;
242  afio->frameOffset = translation.mFrameOffsetInPacket;
243 
244 
245 #ifdef DEBUG
246  fprintf(stderr, "frameOffset: %i\n", afio->frameOffset);
247  fprintf(stderr, "afio->pos : %llu\n", afio->pos);
248 #endif
249 
250  bail:
251 
252  return;
253 }
254 
255 
263 JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAURLInputStream_close(JNIEnv *env, jobject stream, jlong afioPtr) {
264 
265 #ifdef DEBUG
266  fprintf(stderr, "Closing: %llu\n", afioPtr);
267 #endif
268  if (afioPtr == 0) return;
269 
270  CAAudioFileIO *afio = (CAAudioFileIO*)afioPtr;
271  int res = AudioFileClose(afio->afid);
272  if (res) {
273  throwIOExceptionIfError(env, res, "Failed to close audio file");
274  }
275  if (afio->pktDescs != NULL) {
276  delete afio->pktDescs;
277  }
278  if (afio->cookie != NULL) {
279  delete afio->cookie;
280  }
281  delete afio;
282  return;
283 }
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