Copying only a part of a buffer from native code to Java using JNI
I have a very large char buffer in C and need to copy some part of it to a Java array.
Specifically, I need the elements starting at 16,384 and ending at 32000. How can I do this?
Initially I tried this:
jbyte * bytes = (* env) -> GetByteArrayElements (env, array, NULL);
memmove (bytes, (jbyte *) buffer, buffer_size);
(* Env) -> ReleaseByteArrayElements (env, array, (jbyte *) bytes, 0);
(* Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size);
But with this code the entire buffer is transmitted, and it is very big (more than 40 MB). I need only a small part of buffer.
EDIT: Thank you very much, but your version is not quite working. I applied it as follows: memmove (array, (jbyte *) (buffer + numbers), 16385); in the buffer - at each iteration is copied to the "buffer" new 16384 bytes. such figures in the amount of buffer and numbers, respectively: 16384 - 0
32,768 - 16,385
49152 - 32769
65536 - 49153
81,920 - 65,537
98,304 - 81,921
Ie at each iteration by turns in the "numbers" of right-hand column. As a result, - bytes are copied are not always successful. the first iteration is always successful. Further, by turns both successful and unsuccessful. memtcpy gives the same result. What will advise? How to solve the problem?
EDIT2: My code:
JNIEXPORT jint work (JNIEnv * env, jobject obj, jbyteArray array)
{
int argc;
char * args [3];
char * argv [3];
argv [1] = "Music/Tg.mp3";
argv [2] = "testwavS3.flac";
argc = 3;
sox_effects_chain_t * chain;
sox_effect_t * e;
static sox_format_t * in, * out; / * input and output files开发者_开发知识库 * /
char * buffer;
size_t buffer_size;
size_t number_read;
/ * All libSoX applications must start by initialising the SoX library * /
sox_init ();
/ * Open the input file (with default parameters) * /
in = sox_open_read (argv [1], NULL, NULL, NULL);
# Define MAX_SAMPLES (size_t) 8192
__android_log_write (ANDROID_LOG_ERROR, "Read", "Haha");
sox_sample_t samples [MAX_SAMPLES]; / * Temporary store whilst copying. * /
jclass cls;
jmethodID mid, mid2;
cls = (* env) -> GetObjectClass (env, obj);
mid = (* env) -> GetStaticMethodID (env, cls, "testt",
"(I) V");
jbyteArray bytearrayBuffer = (* env) -> NewByteArray (env, & in-> signal.length); / / construct a new byte array
out = sox_open_memstream_write (& buffer, & buffer_size, & in-> signal, NULL, "sox", NULL);
int numbers = 0;
in-> encoding.bits_per_sample = 16;
out-> encoding.bits_per_sample = 16;
(* Env) -> GetByteArrayElements (env, array, NULL);
while (number_read = sox_read (in, samples, MAX_SAMPLES)) {
sox_write (out, samples, number_read);
memmove (array, (jbyte *) (buffer + numbers), (buffer_size-numbers));
numbers + = buffer_size-numbers;
(* Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size);
}
sox_close (out);
sox_close (in);
# If! Defined FIXED_BUFFER
free (buffer);
# Endif
}
using SoX to decode the audio and give buffer in Java.
If you want only a part of the Java array, look at the JNI methods Get<PrimitiveType>ArrayRegion
and Release<PrimitiveType>ArrayRegion
.
If you want only a part of the C++ array, you can pass a different starting address and length in memmove
.
Why not just:
memmove(bytes, (jbyte*)(buffer+16384),(32001-16384));
with appropriate changes to the target Java array, and appropriate bounds checking of the C++ buffer.
As an aside: My C/C++ is rusty, but is not memcpy
more efficient than memmove
if you know you don't have overlapping memory?
Edit 2011-06-13
Looking over the full function that you posted, it's still not entirely clear what you are really intending to do. However, I can see several potential problems in what is posted, not the least of which is that it appears it should not compile. I will leave you to follow up on these observations and either correct the code or rule them out (recall it's been a while since I coded C full-time, so I may not be entirely accurate in every observation).
- The function declares the environment pointer as
JNIEnv *env
, but several of the JNI calls use(*Env)
instead of*env
- if this compiles at all, it's probably a mistake, and even if the pointer happens to be valid, it's horrible form - use the environment pointer passed in the function call. - Unless
sox_open_memstream_write
allocatesbuffer
, the pointer is not valid. - The byte array
bytearrayBuffer
is constructed but never used. - The
free(buffer)
releases memory that was never allocated. - The loop is doing
memmove
toarray
which is a jbyteArray and is not a pointer returned from locking the array for read/write from JNI (using, e.g.GetByteArrayElements
). I would have expect a point cast warning from the C compiler. - The return of
(*Env) -> GetByteArrayElements (env, array, NULL);
is not being used; therefore you have no valid pointer to the array contents, and you have leaked memory since the Java array is not released usingReleaseByteArrayElements
. - The use of
numbers
seems illogical; the first time through the value will be 0, then next time through it will bebuffer_size
, and after that it will be indexing off the end of the buffer. - The invocation
(*Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size);
does not appear to be able to anything useful. If it's meant to tell Java about the buffer size, then either return the value or create a Java array size of exactly that size.
Apart from the Java array being incorrectly accessed, I think the heart of your problem could be that you need:
numbers + = number_read;
instead of
numbers + = buffer_size-numbers;
assuming the buffer is created by sox_open_memstream_write
and is created large enough to write the entire output (which seems nonsensical since the structure of the API would seem to indicate reading chunks of input and writing processed output to a temporary output buffer which output buffer is being reused for each write.
Maybe the loop should be something like:
for(int ofs=0,number_read; (number_read=sox_read(in,samples,MAX_SAMPLES))!=0; ofs+=number_read) {
sox_write(out,samples,number_read);
if(ofs>=16,384 && ofs<32000) { // from 16384
memcpy(jvatgt,buffer,min(number_read,(32000-ofs)); // to 32000 (see question)
}
//(*Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size); WTH??
}
(PS: Please stop emailing me - I will respond here when and if I feel like it)
精彩评论