You are browsing the archive for Audio.

Qt Android (Necessitas) soft-keys control volume

March 1, 2012 in C++, Programming, Qt framework, Tutorials

To control application volume from Necessitas the following changes are required into QtActivity.java file

import android.media.AudioManager;

public class QtActivity extends Activity
{
private AudioManager audio;
……..
}

and add into public boolean onKeyDown(int keyCode, KeyEvent event) following code:

public boolean onKeyDown(int keyCode, KeyEvent event)
{
switch (keyCode)
{
case KeyEvent.KEYCODE_VOLUME_UP:
audio.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
return true;

case KeyEvent.KEYCODE_VOLUME_DOWN:
audio.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
return true;
}

…………
}

Source: here

How to build libmad on android

June 14, 2011 in Java, JNI, Programming, Tutorials

There’s no build of libmad in the NDK for obvious reasons, so i had to build that myself. As the autotools configure script of libmad is not useable with the NDK toolchain i used the config.h file from here, which has all the settings needed for building libmad on Android. Compiling libmad is then a simple matter of creating a proper Android.mk and Application.mk file. The Android.mk file looks like this:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := audio-tools
LOCAL_ARM_MODE := arm
LOCAL_SRC_FILES := NativeWaveDecoder.cpp NativeMP3Decoder.cpp mad/bit.c mad/decoder.c mad/fixed.c mad/frame.c mad/huffman.c mad/layer12.c mad/layer3.c mad/stream.c mad/synth.c mad/timer.c mad/version.c
LOCAL_CFLAGS := -DHAVE_CONFIG_H -DFPM_ARM -ffast-math -O3

include $(BUILD_SHARED_LIBRARY)

Now there’s a couple of things that initially bogged down the performance of this. I tested it with the song “Schism” by tool which is a 6:47min long song, encoded at 192kbps. The file weights in at 9.31mb, pretty big for an mp3 imo. NativeMP3Decoder is just a libmad based implementation of the MP3Decoder in the onset detection tutorial framework. So it has a simpleNativeMP3Decoder.readSamples method which will fill a float array with as many samples as there are elements in the float array. If the input file is in stereo the channels get mixed down to mono by averaging. TheNativeMP3Decoder.readSamples() method internally calls a native method with a similar signature. Instead of a float array i pass in a direct ByteBuffer that has enough storage to hold all the samples requested. The native wrapper then writes the samples to this direct buffer which in turn then gets copied to the float array passed into the NativeMP3Decoder.readSamples() method. It looks something like this:

public int readSamples(float[] samples)
{
   if( buffer == null || buffer.capacity() != samples.length )
   {
      ByteBuffer byteBuffer = ByteBuffer.allocateDirect( samples.length * Float.SIZE / 8 );
      byteBuffer.order(ByteOrder.nativeOrder());
      buffer = byteBuffer.asFloatBuffer();
   }

   int readSamples = readSamples( handle, buffer, samples.length );
   if( readSamples == 0 )
   {
      closeFile( handle );
      return 0;
   }

   buffer.position(0);
   buffer.get( samples );

   return samples.length;
}

The call to buffer.get( samples ) kills it all. Without any optimizations (thumb code, -O0, -DFPM_DEFAULT == standard fixed point math in libmad, no arm assembler optimized fp math) decoding the complete files takes 184 seconds on my Milestone. Holy shit, batman! If i eliminate the buffer.get( samples ) call that gets down to 44 seconds! Incredible. Now i still thought that is way to slow so i started adding optimizations. The first thing i did was compiling to straight arm instead of thumb code. You can tell the NDK toolchain to do so by placing this in the Android.mk file:

LOCAL_ARM_MODE := arm

With this enabled decoding takes 36 seconds. The next thing i did was agressive optimization via -O3 as a CFLAG. That shaved off only 2 more seconds, so nothing to write home about. The last optimization is libmad specific. The config.h file i linked to above does not define the fixed point math mode libmad should use. Now, when you have a look at fixed.h of libmad you can see quiet some options for fixed point math there. There’s also a dedicated option for arm processors that uses some nice little arm assembler code to do the heavy lifting. You can enable this by passing -DFPM_ARM as a CFLAG. Now that did wonders! i’m now down to 20 seconds for decoding 407 seconds of mp3 encoded audio. That’s roughly 20x real-time which is totally ok with me. The song I chose is at the extreme end of the song length spectrum i will have to handle in my next audio game project. A song a user uses will be processed once and waiting for that 20 seconds is ok in my book.

I’m afraid i won’t release the source of the ported audio framework as it’s a bit of a mess and would need some work to clean up. What i can give you is the plain source for the native side of the NativeMP3Decoder class if you guarantee me not to laugh. My C days are long over so there’s probably a shitload of don’ts in there. The “handle” system is also kind of creative but good enough for my needs. I learned how to use the low level libmad api by looking at the code here. I actually like doing it this way more than with the shitty callback high-level API. Your mileage may vary. So here it is, be afraid:

#include "NativeMP3Decoder.h"
#include "mad/mad.h"
#include <stdio.h>
#include <string.h>

#define SHRT_MAX (32767)
#define INPUT_BUFFER_SIZE	(5*8192)
#define OUTPUT_BUFFER_SIZE	8192 /* Must be an integer multiple of 4. */

/**
 * Struct holding the pointer to a wave file.
 */
struct MP3FileHandle
{
	int size;
	FILE* file;
	mad_stream stream;
	mad_frame frame;
	mad_synth synth;
	mad_timer_t timer;
	int leftSamples;
	int offset;
	unsigned char inputBuffer[INPUT_BUFFER_SIZE];
};

/** static WaveFileHandle array **/
static MP3FileHandle* handles[100];

/**
 * Seeks a free handle in the handles array and returns its index or -1 if no handle could be found
 */
static int findFreeHandle( )
{
	for( int i = 0; i < 100; i++ )
	{
		if( handles[i] == 0 )
			return i;
	}

	return -1;
}

static inline void closeHandle( MP3FileHandle* handle )
{
	fclose( handle->file );
	mad_synth_finish(&handle->synth);
	mad_frame_finish(&handle->frame);
	mad_stream_finish(&handle->stream);
	delete handle;
}

static inline signed short fixedToShort(mad_fixed_t Fixed)
{
	if(Fixed>=MAD_F_ONE)
		return(SHRT_MAX);
	if(Fixed<=-MAD_F_ONE)
		return(-SHRT_MAX);

	Fixed=Fixed>>(MAD_F_FRACBITS-15);
	return((signed short)Fixed);
}

JNIEXPORT jint JNICALL Java_com_badlogic_audio_io_NativeMP3Decoder_openFile(JNIEnv *env, jobject obj, jstring file)
{
	int index = findFreeHandle( );

	if( index == -1 )
		return -1;

	const char* fileString = env->GetStringUTFChars(file, NULL);
	FILE* fileHandle = fopen( fileString, "rb" );
	env->ReleaseStringUTFChars(file, fileString);
	if( fileHandle == 0 )
		return -1;

	MP3FileHandle* mp3Handle = new MP3FileHandle( );
	mp3Handle->file = fileHandle;
	fseek( fileHandle, 0, SEEK_END);
	mp3Handle->size = ftell( fileHandle );
	rewind( fileHandle );

	mad_stream_init(&mp3Handle->stream);
	mad_frame_init(&mp3Handle->frame);
	mad_synth_init(&mp3Handle->synth);
	mad_timer_reset(&mp3Handle->timer);

	handles[index] = mp3Handle;
	return index;
}

static inline int readNextFrame( MP3FileHandle* mp3 )
{
	do
	{
		if( mp3->stream.buffer == 0 || mp3->stream.error == MAD_ERROR_BUFLEN )
		{
			int inputBufferSize = 0;
			if( mp3->stream.next_frame != 0 )
			{
				int leftOver = mp3->stream.bufend - mp3->stream.next_frame;
				for( int i = 0; i < leftOver; i++ )
					mp3->inputBuffer[i] = mp3->stream.next_frame[i];
				int readBytes = fread( mp3->inputBuffer + leftOver, 1, INPUT_BUFFER_SIZE - leftOver, mp3->file );
				if( readBytes == 0 )
					return 0;
				inputBufferSize = leftOver + readBytes;
			}
			else
			{
				int readBytes = fread( mp3->inputBuffer, 1, INPUT_BUFFER_SIZE, mp3->file );
				if( readBytes == 0 )
					return 0;
				inputBufferSize = readBytes;
			}

			mad_stream_buffer( &mp3->stream, mp3->inputBuffer, inputBufferSize );
			mp3->stream.error = MAD_ERROR_NONE;
		}

		if( mad_frame_decode( &mp3->frame, &mp3->stream ) )
		{
			if( mp3->stream.error == MAD_ERROR_BUFLEN ||(MAD_RECOVERABLE(mp3->stream.error)))
				continue;
			else
				return 0;
		}
		else
			break;
	} while( true );

	mad_timer_add( &mp3->timer, mp3->frame.header.duration );
	mad_synth_frame( &mp3->synth, &mp3->frame );
	mp3->leftSamples = mp3->synth.pcm.length;
	mp3->offset = 0;

	return -1;
}

JNIEXPORT jint JNICALL Java_com_badlogic_audio_io_NativeMP3Decoder_readSamples__ILjava_nio_FloatBuffer_2I(JNIEnv *env, jobject obj, jint handle, jobject buffer, jint size)
{
	MP3FileHandle* mp3 = handles[handle];
	float* target = (float*)env->GetDirectBufferAddress(buffer);

	int idx = 0;
	while( idx != size )
	{
		if( mp3->leftSamples > 0 )
		{
			for( ; idx < size && mp3->offset < mp3->synth.pcm.length; mp3->leftSamples--, mp3->offset++ )
			{
				int value = fixedToShort(mp3->synth.pcm.samples[0][mp3->offset]);

				if( MAD_NCHANNELS(&mp3->frame.header) == 2 )
				{
					value += fixedToShort(mp3->synth.pcm.samples[1][mp3->offset]);
					value /= 2;
				}

				target[idx++] = value / (float)SHRT_MAX;
			}
		}
		else
		{
			int result = readNextFrame( mp3 );
			if( result == 0 )
				return 0;
		}

	}
	if( idx > size )
		return 0;

	return size;
}

JNIEXPORT jint JNICALL Java_com_badlogic_audio_io_NativeMP3Decoder_readSamples__ILjava_nio_ShortBuffer_2I(JNIEnv *env, jobject obj, jint handle, jobject buffer, jint size)
{
	MP3FileHandle* mp3 = handles[handle];
	short* target = (short*)env->GetDirectBufferAddress(buffer);

	int idx = 0;
	while( idx != size )
	{
		if( mp3->leftSamples > 0 )
		{
			for( ; idx < size && mp3->offset < mp3->synth.pcm.length; mp3->leftSamples--, mp3->offset++ )
			{
				int value = fixedToShort(mp3->synth.pcm.samples[0][mp3->offset]);

				if( MAD_NCHANNELS(&mp3->frame.header) == 2 )
				{
					value += fixedToShort(mp3->synth.pcm.samples[1][mp3->offset]);
					value /= 2;
				}

				target[idx++] = value;
			}
		}
		else
		{
			int result = readNextFrame( mp3 );
			if( result == 0 )
				return 0;
		}

	}
	if( idx > size )
		return 0;

	return size;
}

JNIEXPORT void JNICALL Java_com_badlogic_audio_io_NativeMP3Decoder_closeFile(JNIEnv *env, jobject obj, jint handle)
{
	if( handles[handle] != 0 )
	{
		closeHandle( handles[handle] );
		handles[handle] = 0;
	}
}

To compile that for Android all you have to do is download libmad and put the source files into your Android project’s jni folder along with the code above. Then use the Android.mk from above et voila you got yourself a native mp3 decoder for Android.If you feel adventureous you could even extend it to return stereo data.

Edit:

Download libmad from here. Unzip then run:

./configure --enable-speed --disable-debugging --host=arm-eabi CC=arm-eabi-gcc CPPFLAGS="-I$NDK/build/platforms/android-8/arch-arm/usr/include/" CFLAGS="-nostdlib" LDFLAGS="-Wl,-rpath-link=$NDK/build/platforms/android-8/arch-arm/usr/lib/ -L$NDK/build/platforms/android-8/arch-arm/usr/lib/" LIBS="-lc "

You will have config.h. Note to set path to arm-eabi-gcc

Source: here.

Marius Popa liked this post

Compiling ffmpeg on Android

June 14, 2011 in Programming, Tutorials

Create a new project, make jni folder, then download and extract ffmpeg, rename ffmpeg folder to ffmpeg.

android create project -n vPlayer -t 8 -p vPlayer -k me.abitno.vplayer -a PlayerView
mkdir jni && cd jni
wget http://ffmpeg.org/releases/ffmpeg-0.6.tar.bz2
tar xf ffmpeg-0.6.tar.bz2 && mv ffmpeg-0.6 ffmpeg && cd ffmpeg

Make a new config.sh in ffmpeg, notice the defined PREBUILT path and PLATFORM variables:

#!/bin/bash

PREBUILT=/home/abitno/Android/android-ndk-r4/build/prebuilt/linux-x86/arm-eabi-4.4.0
PLATFORM=/home/abitno/Android/android-ndk-r4/build/platforms/android-8/arch-arm

./configure --target-os=linux \
	--arch=arm \
	--enable-version3 \
	--enable-gpl \
	--enable-nonfree \
	--disable-stripping \
	--disable-ffmpeg \
	--disable-ffplay \
	--disable-ffserver \
	--disable-ffprobe \
	--disable-encoders \
	--disable-muxers \
	--disable-devices \
	--disable-protocols \
	--enable-protocol=file \
	--enable-avfilter \
	--disable-network \
	--disable-mpegaudio-hp \
	--disable-avdevice \
	--enable-cross-compile \
	--cc=$PREBUILT/bin/arm-eabi-gcc \
	--cross-prefix=$PREBUILT/bin/arm-eabi- \
	--nm=$PREBUILT/bin/arm-eabi-nm \
	--extra-cflags="-fPIC -DANDROID" \
	--disable-asm \
	--enable-neon \
	--enable-armv5te \
	--extra-ldflags="-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl"

Change file mode to executable and run the file:

chmod +x config.sh
./config.sh

When config is completed, open config.h, find:

#define restrict restrict

and change it to following:

#define restrict

Open libavutil/libm.h and remove static keyword in all function declaration.
Open Makefile in libavcodec, libavfilter, libavformat, libavutil, libpostproc and libswscale and remove two lines:

include $(SUBDIR)../subdir.mak
include $(SUBDIR)../config.mak

add new av.mk file in ffmpeg folder:

# LOCAL_PATH is one of libavutil, libavcodec, libavformat, or libswscale

#include $(LOCAL_PATH)/../config-$(TARGET_ARCH).mak
include $(LOCAL_PATH)/../config.mak

OBJS :=
OBJS-yes :=
MMX-OBJS-yes :=
include $(LOCAL_PATH)/Makefile

# collect objects
OBJS-$(HAVE_MMX) += $(MMX-OBJS-yes)
OBJS += $(OBJS-yes)

FFNAME := lib$(NAME)
FFLIBS := $(foreach,NAME,$(FFLIBS),lib$(NAME))
FFCFLAGS  = -DHAVE_AV_CONFIG_H -Wno-sign-compare -Wno-switch -Wno-pointer-sign
FFCFLAGS += -DTARGET_CONFIG=\"config-$(TARGET_ARCH).h\"

ALL_S_FILES := $(wildcard $(LOCAL_PATH)/$(TARGET_ARCH)/*.S)
ALL_S_FILES := $(addprefix $(TARGET_ARCH)/, $(notdir $(ALL_S_FILES)))

ifneq ($(ALL_S_FILES),)
ALL_S_OBJS := $(patsubst %.S,%.o,$(ALL_S_FILES))
C_OBJS := $(filter-out $(ALL_S_OBJS),$(OBJS))
S_OBJS := $(filter $(ALL_S_OBJS),$(OBJS))
else
C_OBJS := $(OBJS)
S_OBJS :=
endif

C_FILES := $(patsubst %.o,%.c,$(C_OBJS))
S_FILES := $(patsubst %.o,%.S,$(S_OBJS))

FFFILES := $(sort $(S_FILES)) $(sort $(C_FILES))

Create Android.mk in jni folder:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_STATIC_LIBRARIES := libavformat libavcodec libavutil libpostproc libswscale
LOCAL_MODULE := ffmpeg
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))

and in libavformat/Android.mk:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../av.mk
LOCAL_SRC_FILES := $(FFFILES)
LOCAL_C_INCLUDES :=		\
	$(LOCAL_PATH)		\
	$(LOCAL_PATH)/..
LOCAL_CFLAGS += $(FFCFLAGS)
LOCAL_CFLAGS += -include "string.h" -Dipv6mr_interface=ipv6mr_ifindex
LOCAL_LDLIBS := -lz
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)

in libavcodec/Android.mk:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../av.mk
LOCAL_SRC_FILES := $(FFFILES)
LOCAL_C_INCLUDES :=		\
	$(LOCAL_PATH)		\
	$(LOCAL_PATH)/..
LOCAL_CFLAGS += $(FFCFLAGS)
LOCAL_LDLIBS := -lz
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)

and the following Android.mk in each of these folders: libavfilter, libavutil, libpostproc and libswscale:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../av.mk
LOCAL_SRC_FILES := $(FFFILES)
LOCAL_C_INCLUDES :=		\
	$(LOCAL_PATH)		\
	$(LOCAL_PATH)/..
LOCAL_CFLAGS += $(FFCFLAGS)
LOCAL_STATIC_LIBRARIES := $(FFLIBS)
LOCAL_MODULE := $(FFNAME)
include $(BUILD_STATIC_LIBRARY)

Now, just go to jni folder and type ndk-build, then you will have ffmpeg.so.

Source: here and here.

Marius Popa liked this post