CASampledSP
Loading...
Searching...
No Matches
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 * 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_CAStreamInputStream.h"
24#include "CAUtils.h"
25#include <pthread.h>
26
27static jfieldID nativeBufferFieldID = NULL;
28static jmethodID rewindMethodID = NULL;
29static jmethodID setLimitMethodID = NULL;
30static jmethodID getLimitMethodID = NULL;
31static jmethodID setPositionMethodID = NULL;
32static jmethodID getPositionMethodID = NULL;
33
39static 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
58static 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
132bail:
133 return;
134}
135
136
140static 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
199bail:
200 return;
201}
202
212JNIEXPORT 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
236bail:
237 return;
238}
239
249 (JNIEnv *env, jobject stream, jint hint, jint bufferSize) {
250 int res = 0;
252
253 init_ids(env, stream);
254
255 asio->srcBufferSize = bufferSize;
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
273bail:
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}
JNIEXPORT jlong JNICALL Java_com_tagtraum_casampledsp_CAStreamInputStream_open(JNIEnv *env, jobject stream, jint hint, jint bufferSize)
Opens the audio stream - at this point only the callbacks are set up via AudioFileStreamOpen.
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 void JNICALL Java_com_tagtraum_casampledsp_CAStreamInputStream_close(JNIEnv *env, jobject stream, jlong asioPtr)
Closes the stream and all associated resources.
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
JNIEnv * env
JNI environment.
Definition CAUtils.h:49
char * cookie
Cookie.
Definition CAUtils.h:60
SInt64 lastPos
Last position (in packets)
Definition CAUtils.h:57
jobject javaInstance
Calling java object.
Definition CAUtils.h:50
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
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
Central context representing the native peer to the Java CAStreamInputStream object.
Definition CAUtils.h:86
AudioFileStreamID asid
Stream id.
Definition CAUtils.h:87