Очередь Java, массивы и JNI

Я думаю, что это - общий вопрос о Java, но он действительно включает некоторый материал Android.

Таким образом, вот соглашение: у меня есть перенесенная версия JNI MPG123 для Android, и я могу вытянуть данные PCM из файла MP3 на SDCard. Я хотел бы вытянуть блок эти данные, сделать анализ его с помощью другого класса, который я записал, затем продвиньте его в очередь. Идеально, я хотел бы, чтобы аудио воспроизвело с четырехсекундной задержкой. Я достиг его без задержки с помощью этого кода:

public void run() 
{
  // The actual thread where the PCM data gets processed and analyzed
 short[] pcm = new short[ 4096 ];
 short[] zero = new short[ 4096 ];
 Queue<short[]> buffer = new LinkedList<short[]>();

 // Fill the zero buffer
 for( int i = 0; i < 4096; i++ )
  zero[i] = 0;

 // Push back 4 seconds of silence - Note this commented section
 //for( int i = 0; i < 43; i++ )
 // buffer.add( zero );

 // Analyze the whole file 
 while( !isInterrupted() )
 {
  // Grab and analyze data, add events
  int ret = audio.GetPCM( pcm );
  //Log.d( "AudioThread", "GetPCM returns: " + ret );
  if( ret != MPG123.MPG123_DONE )
  {
   // Process the PCM data
   if( bd.Process( pcm ) != BeatDetection.AUDALYSIS_OK )
    Log.v( "AudioThread", "Beat Detection Error!" );

   // Add the data to the PCM buffer
   buffer.add( pcm );
  }

  // Play the audio
  short[] data = buffer.poll();
  if( data != null ) // If we have data left in the buffer
   at.write( data, 0, 4096 ); // Write it to the audio track
  else
   break; // Otherwise we need to exit the loop

  Log.d( "AudioThread", "BufferSize= " + buffer.size() );
 }

 // Debug message
 Log.d( "AudioThread", "Exiting Thread." );

 // Clean stuff up
 bd.Cleanup();
 audio.Cleanup();
 at.stop();
 at.release(); 
 }

Тем не менее, когда я пытаюсь ввести задержку в свою очередь путем добавления "4 секунд" ценность 0 в очередь (Отметьте прокомментированный блок), мои музыкальные игры сразу же, но запускает 4 секунды в песню и играет в в реальном времени, пока очередь не начинает пустеть, где она играет тот же блок много раз до убегающий из цикла. Почти кажется, как будто очередь действует как очередь "Первым пришел - последним вышел", а не очередь "Метода"первым пришел - первым вышел"".

Возможно, я не использую Очередь Java правильно? Возможно, это - некоторая нечетная ошибка с android AudioTrack?

Спасибо!

- Griff

РЕДАКТИРОВАНИЕ - я решил проблему. Это, кажется, проблема с моей реализацией JNI GetPCM. Кажется, что, когда я скопировал данные в PCM от уровня JNI, это установило те же данные для каждого экземпляра PCM, пододвинутой обратно в очередь, установив каждую запись PCM в очереди к тому же кадру данных PCM. Я решил проблему путем изменения:

buffer.add( pcm );

кому:

buffer.add( pcm.clone() );

Новый Вопрос: это нормальное поведение Java? Я реализовывал функцию JNI неправильно?

JNIEXPORT jint JNICALL Java_com_motalenbubble_projectlucid_MPG123_GetPCM(
    JNIEnv* env,
    jobject obj,
    jshortArray data ) // jshortarray already has a size attribute
{
    // Call mpg123_read
    jint  err  = MPG123_OK, length;
    jshort *pcm;
    jsize len = (*env)->GetArrayLength( env, data );

    pcm = (*env)->GetShortArrayElements( env, data, NULL );
    err = mpg123_read( mh, (unsigned char*)pcm, len * sizeof( short ), &length ); // Read length is in bytes - we need in shorts. 
    (*env)->ReleaseShortArrayElements( env, data, pcm, 0 );
    // Annoying logging
    //dprintf(0, "read(%d): %s\n", length, err == MPG123_ERR ? mpg123_strerror(mh) : mpg123_plain_strerror(err)); 
    return err;
}
1
задан Griffin 27 July 2010 в 00:57
поделиться

1 ответ

Похоже, роль вашей JNI-функции заключается в следующем:

Передайте ей инициализированный буфер (т.е. объект короткого массива java), и он будет заполнен следующей порцией выборок из декодер mp3.

Для выполнения этой задачи, похоже, она реализована правильно. Функция никогда не создает экземпляр объекта короткого массива java, и я не думаю, что этого следует ожидать.

Я дам описание проблемы, хотя, кажется, вы уже поняли, что происходит.

В методе run () для переменной pcm вы создаете экземпляр короткого массива один раз. Каждый раз, когда вы вызываете audio.GetPCM (pcm) , вы передаете ему один и тот же объект массива. А затем, когда вы вызываете buffer.add (pcm) , он всегда ставит в очередь ссылку на тот же массив . Итак, если вы вызовете GetPCM, а затем снова вызовете GetPCM до того, как вы записали кадр на аудиоустройство, GetPCM катастрофически перезапишет массив.

Вы решили проблему с помощью buffer.add (pcm.clone ()) , потому что он всегда выделяет новый массив и добавляет ссылку на этот новый массив в очередь.GetPCM по-прежнему перезаписывает один и тот же массив ( pcm ) каждый раз, но это не проблема, потому что очередь содержит ссылки на несвязанные массивы, которые были созданы как копии предыдущего содержимого массива pcm .

Проблема на самом деле не связана с Android или JNI; GetPCM можно также реализовать на чистой Java, и возникнет такая же проблема.


Использование pcm.clone () - безопасное решение, но оно может быть не самым эффективным, поскольку требует создания экземпляра нового массива для каждого кадра.

Если вам не нужно заранее декодировать данные mp3, вы должны иметь возможность разработать механизм, который предотвращает вызов GetPCM до тех пор, пока очередь не станет пустой, и вы можете обойтись одним или двумя заранее выделенными буферы.

С другой стороны, если вам абсолютно необходимо декодировать данные mp3 за четыре секунды до этого, вам неизбежно потребуется много места в буфере. Но в целом производительность лучше, если память выделяется один раз вначале и используется повторно, а не динамически выделяется повторно.

2
ответ дан 2 September 2019 в 22:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: