CASampledSP
CAStreamInputStream.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_CAStreamInputStream.h"
24 #include "CAUtils.h"
25 #include <pthread.h>
26 
27 static jfieldID nativeBufferFieldID = NULL;
28 static jmethodID rewindMethodID = NULL;
29 static jmethodID setLimitMethodID = NULL;
30 static jmethodID getLimitMethodID = NULL;
31 static jmethodID setPositionMethodID = NULL;
32 static jmethodID getPositionMethodID = NULL;
33 
39 static void init_ids(JNIEnv *env, jobject stream) {
40  // get method and field ids, if we don't have them already
41  if (nativeBufferFieldID == NULL || rewindMethodID == NULL || setLimitMethodID == NULL
42  || getLimitMethodID == NULL || getPositionMethodID==NULL || setPositionMethodID==NULL) {
43 
44  nativeBufferFieldID = env->GetFieldID(env->GetObjectClass(stream), "nativeBuffer", "Ljava/nio/ByteBuffer;");
45  jclass bufferClass = env->FindClass("java/nio/Buffer");
46  rewindMethodID = env->GetMethodID(bufferClass, "rewind", "()Ljava/nio/Buffer;");
47  setLimitMethodID = env->GetMethodID(bufferClass, "limit", "(I)Ljava/nio/Buffer;");
48  getLimitMethodID = env->GetMethodID(bufferClass, "limit", "()I");
49  getPositionMethodID = env->GetMethodID(bufferClass, "position", "()I");
50  setPositionMethodID = env->GetMethodID(bufferClass, "position", "(I)Ljava/nio/Buffer;");;
51  }
52 }
53 
54 
58 static void CAStreamInputStream_PacketsProc (
59  void *inClientData,
60  UInt32 inNumberBytes,
61  UInt32 inNumberPackets,
62  const void *inInputData,
63  AudioStreamPacketDescription *inPacketDescriptions
64  ){
65 #ifdef DEBUG
66  fprintf(stderr, "CAStreamInputStream_PacketsProc\n");
67 #endif
68 
69  CAAudioStreamIO *asio = (CAAudioStreamIO*)inClientData;
70  jobject byteBuffer = NULL;
71  jlong capacity = 0;
72  jint limit = 0;
73  int totalPackets = 0;
74  int oldPackets = 0;
75  AudioStreamPacketDescription *newPktDescs = NULL;
76  int i=0;
77 
78  // get java-managed byte buffer reference
79  byteBuffer = asio->env->GetObjectField(asio->javaInstance, nativeBufferFieldID);
80  if (byteBuffer == NULL) {
81  throwIOExceptionIfError(asio->env, 1, "Failed to obtain native buffer");
82  goto bail;
83  }
84  limit = asio->env->CallIntMethod(byteBuffer, getLimitMethodID);
85 
86  // get pointer to our java managed bytebuffer
87  asio->srcBuffer = (char *)asio->env->GetDirectBufferAddress(byteBuffer);
88  capacity = asio->env->GetDirectBufferCapacity(byteBuffer);
89  if (asio->srcBuffer == NULL) {
90  throwIOExceptionIfError(asio->env, 1, "Failed to obtain direct buffer address");
91  goto bail;
92  }
93  if (capacity-limit < inNumberBytes) {
94  throwIOExceptionIfError(asio->env, 1, "Native buffer to small for decoded audio");
95  goto bail;
96  }
97  // copy data to our byte buffer
98  memcpy(asio->srcBuffer+limit, inInputData, inNumberBytes);
99 
100  // advance input file packet position
101  if (limit == 0) {
102  asio->lastPos = asio->pos;
103  }
104  oldPackets = asio->pos - asio->lastPos;
105  asio->pos += inNumberPackets;
106  asio->srcBufferSize = inNumberBytes+limit;
107  totalPackets = asio->pos - asio->lastPos;
108 
109  // we already wrote to the buffer, now we still need to
110  // set new bytebuffer limit and position to 0.
111  asio->env->CallObjectMethod(byteBuffer, setPositionMethodID, 0);
112  asio->env->CallObjectMethod(byteBuffer, setLimitMethodID, inNumberBytes+limit);
113 
114  if (inPacketDescriptions) {
115  newPktDescs = new AudioStreamPacketDescription[totalPackets];
116  if (asio->pktDescs != NULL) {
117  // copy to new array
118  memcpy(newPktDescs, asio->pktDescs, sizeof(AudioStreamPacketDescription)*oldPackets);
119  // delete the old one
120  delete[] asio->pktDescs;
121  }
122 
123  // copy new packets
124  memcpy(&newPktDescs[oldPackets], inPacketDescriptions, sizeof(AudioStreamPacketDescription)*inNumberPackets);
125  // correct offsets
126  for (i=1; i<totalPackets; i++) {
127  newPktDescs[i].mStartOffset = newPktDescs[i-1].mDataByteSize+newPktDescs[i-1].mStartOffset;
128  }
129  asio->pktDescs = newPktDescs;
130  }
131 
132 bail:
133  return;
134 }
135 
136 
140 static void CAStreamInputStream_PropertyListenerProc(void *inClientData,
141  AudioFileStreamID stream,
142  AudioFileStreamPropertyID inPropertyID,
143  UInt32 *ioFlags) {
144  int res = 0;
145  UInt32 size;
146 
147 #ifdef DEBUG
148  fprintf(stderr, "CAStreamInputStream_PropertyListenerProc\n");
149  fprintf(stderr, "AudioFileStreamPropertyID %i\n", inPropertyID);
150 #endif
151 
152  CAAudioStreamIO *asio = (CAAudioStreamIO*)inClientData;
153 
154  if (inPropertyID == kAudioFileStreamProperty_MagicCookieData) {
155  res = AudioFileStreamGetPropertyInfo(stream, kAudioFileStreamProperty_MagicCookieData, &asio->cookieSize, NULL);
156  if (res && res != kAudioFileUnsupportedPropertyError) {
157  throwUnsupportedAudioFileExceptionIfError(asio->env, res, "Failed to obtain cookie info from audio stream");
158  goto bail;
159  }
160  res = 0;
161  if (!res && asio->cookieSize) {
162  asio->cookie = new char[asio->cookieSize];
163  res = AudioFileStreamGetProperty(stream, kAudioFileStreamProperty_MagicCookieData, &asio->cookieSize, asio->cookie);
164  if (res) {
165  throwUnsupportedAudioFileExceptionIfError(asio->env, res, "Failed to obtain cookie from audio stream");
166  goto bail;
167  }
168  }
169  }
170 
171  if (inPropertyID == kAudioFileStreamProperty_DataFormat) {
172  size = sizeof(asio->srcFormat);
173  res = AudioFileStreamGetProperty(stream, kAudioFileStreamProperty_DataFormat, &size, &asio->srcFormat);
174  if (res) {
175  throwUnsupportedAudioFileExceptionIfError(asio->env, res, "Failed to read audio format from stream");
176  goto bail;
177  }
178  /*
179  // find out how many packets fit into the buffer
180  if (asio->srcFormat.mBytesPerPacket == 0) {
181  // format is VBR, so we need to get max size per packet
182  size = sizeof(asio->srcSizePerPacket);
183  res = AudioFileStreamGetProperty(asio->asid, kAudioFileStreamProperty_MaximumPacketSize, &size, &asio->srcSizePerPacket);
184  if (res) {
185  throwIOExceptionIfError(asio->env, res, "AudioStreamGetProperty kAudioFileStreamProperty_MaximumPacketSize failed");
186  goto bail;
187  }
188  asio->numPacketsPerRead = asio->srcBufferSize / asio->srcSizePerPacket;
189  asio->pktDescs = new AudioStreamPacketDescription [asio->numPacketsPerRead];
190  }
191  else {
192  asio->srcSizePerPacket = asio->srcFormat.mBytesPerPacket;
193  asio->numPacketsPerRead = asio->srcBufferSize / asio->srcSizePerPacket;
194  asio->pktDescs = NULL;
195  }
196  */
197  }
198 
199 bail:
200  return;
201 }
202 
212 JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAStreamInputStream_fillNativeBuffer(JNIEnv *env, jobject stream, jlong asioPtr, jbyteArray buf, jint length) {
213 
214 #ifdef DEBUG
215  fprintf(stderr, "fillNativeBuffer: %lld\n", asioPtr);
216 #endif
217 
218  int res = 0;
219  CAAudioStreamIO *asio = (CAAudioStreamIO*)asioPtr;
220  char inBuf[length];
221 
222  // update jav env
223  asio->env = env;
224  asio->javaInstance = stream;
225 
226  // convert byte buf to native array
227  env->GetByteArrayRegion(buf, 0, length, (jbyte*)inBuf);
228 
229  // pump bytes into the stream reader
230  res = AudioFileStreamParseBytes(asio->asid, length, &inBuf, kAudioFileStreamPropertyFlag_CacheProperty);
231  if (res) {
232  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to parse bytes from audio stream");
233  goto bail;
234  }
235 
236 bail:
237  return;
238 }
239 
249  (JNIEnv *env, jobject stream, jint hint) {
250  int res = 0;
251  CAAudioStreamIO *asio = new CAAudioStreamIO;
252 
253  init_ids(env, stream);
254 
255  asio->srcBufferSize = BUFFER_SIZE;
256  asio->pos = 0;
257  asio->lastPos = 0;
258  asio->asid = NULL;
259  asio->env = env;
260  asio->javaInstance = stream;
261  asio->srcFormat.mFormatID = 0;
262  asio->pktDescs = NULL;
263  asio->cookie = NULL;
264  asio->cookieSize = 0;
265  asio->frameOffset = 0;
266 
267  res = AudioFileStreamOpen(asio, CAStreamInputStream_PropertyListenerProc, CAStreamInputStream_PacketsProc, hint, &asio->asid);
268  if (res) {
269  throwUnsupportedAudioFileExceptionIfError(env, res, "Failed to open audio stream");
270  goto bail;
271  }
272 
273 bail:
274  if (res) {
275  if (asio->cookie != NULL) {
276  delete asio->cookie;
277  }
278  if (asio->pktDescs != NULL) {
279  delete[] asio->pktDescs;
280  }
281  if (asio->asid != NULL) {
282  AudioFileStreamClose(asio->asid);
283  }
284  delete asio;
285  }
286 
287  return (jlong)asio;
288 }
289 
298  (JNIEnv *env, jobject stream, jlong asioPtr) {
299  if (asioPtr == 0) return;
300  CAAudioStreamIO *asio = (CAAudioStreamIO*)asioPtr;
301  if (asio->cookie != NULL) {
302  delete asio->cookie;
303  }
304  if (asio->pktDescs != NULL) {
305  delete[] asio->pktDescs;
306  }
307  int res = AudioFileStreamClose(asio->asid);
308  if (res) {
309  throwIOExceptionIfError(env, res, "Failed to close audio stream");
310  }
311  delete asio;
312 }
#define BUFFER_SIZE
Definition: CAUtils.h:44
UInt32 cookieSize
Cookie size.
Definition: CAUtils.h:63
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
AudioFileStreamID asid
Stream id.
Definition: CAUtils.h:89
Central context representing the native peer to the Java CAStreamInputStream object.
Definition: CAUtils.h:87
void throwUnsupportedAudioFileExceptionIfError(JNIEnv *env, int err, const char *message)
Throws an UnsupportedAudioFileException exception.
Definition: CAUtils.cpp:39
JNIEnv * env
JNI environment.
Definition: CAUtils.h:51
UInt32 srcBufferSize
Source buffer size.
Definition: CAUtils.h:56
JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAStreamInputStream_fillNativeBuffer(JNIEnv *env, jobject stream, jlong asioPtr, jbyteArray buf, jint length)
Called by the Java code to fill the native buffer.
JNIEXPORT jlong JNICALL Java_com_tagtraum_casampledsp_CAStreamInputStream_open(JNIEnv *env, jobject stream, jint hint)
Opens the audio stream - at this point only the callbacks are set up via AudioFileStreamOpen.
char * cookie
Cookie.
Definition: CAUtils.h:62
AudioStreamPacketDescription * pktDescs
Packet descriptions.
Definition: CAUtils.h:54
jobject javaInstance
Calling java object.
Definition: CAUtils.h:52
JNIEXPORT void JNICALL Java_com_tagtraum_casampledsp_CAStreamInputStream_close(JNIEnv *env, jobject stream, jlong asioPtr)
Closes the stream and all associated resources.
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
SInt64 pos
Current position (in packets)
Definition: CAUtils.h:58