In Android 4.x my audio looping app has 0.2 to 0.5 second gaps in the sound loops.
I'm using MediaPlayer as my sounds can be quite large (2-3mb each in some cases) and it can run multiple instances at the same time.
I have researched this quite a bit and I see there is a bug for Android 4.x... however, I have tried many work arounds and I can't seem to get any of them working.
Converted all the wavs to OGG using Audacity (quality level 2 to 10, it didn't matter)
Tried setNextMediaPlayer()
Tried to use seekTo(0) on stop and repeat
Tried soundPool which has its own bugs
Here's a sample of the code I'm using:
public class SoundPlayer implements OnCompletionListener {
private MediaPlayer mp = null;
public void initPlayer() {
if(mp == null) {
mp = new MediaPlayer();
}
}
public void prepare(Context context, int resource) {
initPlayer();
try{
mp.reset();
Uri uri = Uri.parse("android.resource://com.myapp.app/"+resource);
mp.setDataSource(context,uri);
mp.prepare();
isPrepared = true;
mp.setOnCompletionListener(this);
} catch(Exception e) {
e.printStackTrace();
}
}
.......... etc (Uses typical MediaPlayer methods such as stop(), start(), setLooping(true)
}
I'm not using anything special, so I'm just wondering if anyone knows of a work around for the looping bug on Android.
Okay old Post, but this solution works. This is kind of hack. It'll help somebody if somebody came to this answer.
I am just using three media players. mp1, mp2, mp3. mp1 gets play, and I set mp2 to its setNextMediaPlayer. when mp1 ends, I set mp3 to mp2's setNextMediaPlayer. when mp2 ends, I set mp1 to mp3's setNextMediaPlayer. Following is the complete code which play pause, stop, set vol. Just create an object BZMediaPlayer, start it by providing uri, or resource id.
import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.util.Log;
public class BZMediaPlayer {
private Context context;
private Uri uri;
private int resourceId;
// which file is getting played
public static final int URI_PLAYING = 1;
public static final int RESOURCE_PLAYING = 2;
private int filePlaying;
// states of the media player
public static final int STATE_PLAYING = 1;
public static final int STATE_PAUSED = 2;
public static final int STATE_STOP = 3;
// current state
private int state = STATE_STOP;
// current mediaPlayer which is playing
private int mediaPlayerIndex = -1;
// 3 media players
private MediaPlayer mp[] = new MediaPlayer[3];
// current volume
private float vol;
public BZMediaPlayer(Context context) {
this.context = context;
}
/**
* plays the provided uri
* #param uri
*/
public void play(Uri uri) {
this.uri = uri;
// current playing file
filePlaying = URI_PLAYING;
// stop any playing session
stop();
// initialize and set listener to three mediaplayers
for (int i = 0; i < mp.length; i++) {
mp[i] = MediaPlayer.create(context, uri);
mp[i].setOnCompletionListener(completionListener);
}
// set nextMediaPlayers
mp[0].setNextMediaPlayer(mp[1]);
mp[1].setNextMediaPlayer(mp[2]);
// start the first MediaPlayer
mp[0].start();
// set mediaplayer inex
mediaPlayerIndex = 0;
// set state
state = STATE_PLAYING;
}
/**
* play file from resource
* #param resourceId
*/
public void play(int resourceId) {
this.resourceId = resourceId;
filePlaying = RESOURCE_PLAYING;
stop();
for (int i = 0; i < mp.length; i++) {
mp[i] = MediaPlayer.create(context, resourceId);
mp[i].setOnCompletionListener(completionListener);
}
mp[0].setNextMediaPlayer(mp[1]);
mp[1].setNextMediaPlayer(mp[2]);
mp[0].start();
mediaPlayerIndex = 0;
state = STATE_PLAYING;
}
/**
* play if the mediaplayer is pause
*/
public void play() {
if (state == STATE_PAUSED) {
mp[mediaPlayerIndex].start();
Log.d("BZMediaPlayer", "playing");
state = STATE_PLAYING;
}
}
/**
* pause current playing session
*/
public void pause() {
if (state == STATE_PLAYING) {
mp[mediaPlayerIndex].pause();
Log.d("BZMediaPlayer", "pausing");
state = STATE_PAUSED;
}
}
/**
* get current state
* #return
*/
public int getState() {
return state;
}
/**
* stop every mediaplayer
*/
public void stop() {
for(int i = 0 ; i < mp.length ; i++) {
if (mp[i] != null) {
mp[i].stop();
if(mp[i].isPlaying()) {
mp[i].release();
}
}
}
state = STATE_STOP;
}
/**
* set vol for every mediaplayer
* #param vol
*/
public void setVol(float vol) {
this.vol = vol;
for(int i = 0 ; i < mp.length ; i++) {
if (mp[i] != null && mp[i].isPlaying()) {
mp[i].setVolume(vol, vol);
}
}
}
/**
* internal listener which handles looping thing
*/
private MediaPlayer.OnCompletionListener completionListener = new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer curmp) {
int mpEnds = 0;
int mpPlaying = 0;
int mpNext = 0;
if(curmp == mp[0]) {
mpEnds = 0;
mpPlaying = 1;
mpNext = 2;
}
else if(curmp == mp[1]) {
mpEnds = 1;
mpPlaying = 2;
mpNext = 0; // corrected, else index out of range
}
else if(curmp == mp[2]) {
mpEnds = 2;
mpPlaying = 0; // corrected, else index out of range
mpNext = 1; // corrected, else index out of range
}
// as we have set mp2 mp1's next, so index will be 1
mediaPlayerIndex = mpPlaying;
Log.d("BZMediaPlayer", "Media Player " + mpEnds);
try {
// mp3 is already playing release it
if (mp[mpNext] != null) {
mp[mpNext].release();
}
// if we are playing uri
if (filePlaying == URI_PLAYING) {
mp[mpNext] = MediaPlayer.create(context, uri);
} else {
mp[mpNext] = MediaPlayer.create(context, resourceId);
}
// at listener to mp3
mp[mpNext].setOnCompletionListener(this);
// set vol
mp[mpNext].setVolume(vol, vol);
// set nextMediaPlayer
mp[mpPlaying].setNextMediaPlayer(mp[mpNext]);
// set nextMediaPlayer vol
mp[mpPlaying].setVolume(vol, vol);
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
Hope it will help somebody
Edit: clean code
Related
I want to be able to play all audio from one app on only one channel on a device e.g only on left speakers.
I can do this with MediaPlayer playing an mp3 file like this to play only on left speaker
MediaPlayer mediaPlayer=MediaPlayer.create(this,R.raw.bitter_sweet);
mediaPlayer.setVolume(1.0f, 0f);
mediaPlayer.start();
But I want ALL audio from the app to play on left speaker, i.e if i add a webview and load a youtube url the audio should still play on one channel.
Using Exoplayer I can achieve this by creating a custom AudioProcessor adopted from the ChannelMappingAudioProcessor
public class StereoVolumeProcessor implements AudioProcessor {
private int channelCount;
private int sampleRateHz;
private int[] pendingOutputChannels;
private boolean active;
private int[] outputChannels;
private ByteBuffer buffer;
private ByteBuffer outputBuffer;
private boolean inputEnded;
private float[] volume;
private static final int LEFT_SPEAKER = 0;
private static final int RIGHT_SPEAKER = 1;
public StereoVolumeProcessor() {
buffer = EMPTY_BUFFER;
outputBuffer = EMPTY_BUFFER;
channelCount = Format.NO_VALUE;
sampleRateHz = Format.NO_VALUE;
}
public void setChannelMap(int[] outputChannels) {
pendingOutputChannels = outputChannels;
}
#Override
public boolean configure(int sampleRateHz, int channelCount, #C.Encoding int encoding)
throws UnhandledFormatException {
if(volume == null){
throw new IllegalStateException("volume has not been set! Call setVolume(float left,float right)");
}
boolean outputChannelsChanged = !Arrays.equals(pendingOutputChannels, outputChannels);
outputChannels = pendingOutputChannels;
if (outputChannels == null) {
active = false;
return outputChannelsChanged;
}
if (encoding != C.ENCODING_PCM_16BIT) {
throw new UnhandledFormatException(sampleRateHz, channelCount, encoding);
}
if (!outputChannelsChanged && this.sampleRateHz == sampleRateHz
&& this.channelCount == channelCount) {
return false;
}
this.sampleRateHz = sampleRateHz;
this.channelCount = channelCount;
active = true;
return true;
}
#Override
public boolean isActive() {
return active;
}
#Override
public int getOutputChannelCount() {
return outputChannels == null ? channelCount : outputChannels.length;
}
#Override
public int getOutputEncoding() {
return C.ENCODING_PCM_16BIT;
}
/**
* Returns the sample rate of audio output by the processor, in hertz. The value may change as a
* result of calling {#link #configure(int, int, int)} and is undefined if the instance is not
* active.
*/
#Override
public int getOutputSampleRateHz() {
return sampleRateHz;
}
#Override
public void queueInput(ByteBuffer inputBuffer) {
int position = inputBuffer.position();
int limit = inputBuffer.limit();
int size = limit - position;
if (buffer.capacity() < size) {
buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
} else {
buffer.clear();
}
if(isActive()){
int ch = 0;
for(int i = position;i<limit;i+=2){
short sample = (short) (inputBuffer.getShort(i)* volume[ch++]);
buffer.putShort(sample);
ch%=channelCount;
}
}else{
throw new IllegalStateException();
}
inputBuffer.position(limit);
buffer.flip();
outputBuffer = buffer;
}
#Override
public void queueEndOfStream() {
inputEnded = true;
}
/**
* Sets the volume of right and left channels/speakers
* The values are between 0.0 and 1.0
*
* #param left
* #param right
*/
public void setVolume(float left,float right){
volume = new float[]{left,right};
}
public float getLeftVolume(){
return volume[LEFT_SPEAKER];
}
public float getRightVolume(){
return volume[RIGHT_SPEAKER];
}
#Override
public ByteBuffer getOutput() {
ByteBuffer outputBuffer = this.outputBuffer;
this.outputBuffer = EMPTY_BUFFER;
return outputBuffer;
}
#SuppressWarnings("ReferenceEquality")
#Override
public boolean isEnded() {
return inputEnded && outputBuffer == EMPTY_BUFFER;
}
#Override
public void flush() {
outputBuffer = EMPTY_BUFFER;
inputEnded = false;
}
#Override
public void reset() {
flush();
buffer = EMPTY_BUFFER;
channelCount = Format.NO_VALUE;
sampleRateHz = Format.NO_VALUE;
outputChannels = null;
active = false;
}
}
Volume can be set with a value between 0.0 and 1.0 on either left, right or both speakers.
Use the processor as follows
stereoVolumeProcessor = new StereoVolumeProcessor();
stereoVolumeProcessor.setChannelMap(new int[]{0,1});
stereoVolumeProcessor.setVolume(1,0);
RenderersFactory factory = new DefaultRenderersFactory(this){
/**
* Builds an array of {#link AudioProcessor}s that will process PCM audio before output.
*/
#Override
protected AudioProcessor[] buildAudioProcessors() {
return new AudioProcessor[] {stereoVolumeProcessor};
}
};
I have ExoPlayer which plays HLS videos, the thing is i need to give user ability to change video quality(auto/1080/720/480).
I figured out that playing around with AdaptiveTrackSelection.Factory does set the quality, but it remains till the object is killed.
I have also tried using MappingTrackSelector, i know that my video has 4 tracks, but i did not get how to select any of it manually. Will this selection make it work?
Thanks for any ideas.
MappingTrackSelector.MappedTrackInfo trackInfo = mDefaultTrackSelector.getCurrentMappedTrackInfo();
mDefaultTrackSelector.selectTracks(
//what should go here?
, trackInfo.getTrackGroups(4));
Regarding this thread :https://github.com/google/ExoPlayer/issues/2250, I managed to change exo player video quality while playing previous one, so it does not getting in buffering instantly.
So I have next classes :
public enum HLSQuality {
Auto, Quality1080, Quality720, Quality480, NoValue
}
class HLSUtil {
private HLSUtil() {
}
#NonNull
static HLSQuality getQuality(#NonNull Format format) {
switch (format.height) {
case 1080: {
return HLSQuality.Quality1080;
}
case 720: {
return HLSQuality.Quality720;
}
case 480:
case 486: {
return HLSQuality.Quality480;
}
default: {
return HLSQuality.NoValue;
}
}
}
static boolean isQualityPlayable(#NonNull Format format) {
return format.height <= 1080;
}
}
public class ClassAdaptiveTrackSelection extends BaseTrackSelection {
public static final class Factory implements TrackSelection.Factory {
private final BandwidthMeter bandwidthMeter;
private final int maxInitialBitrate = 2000000;
private final int minDurationForQualityIncreaseMs = 10000;
private final int maxDurationForQualityDecreaseMs = 25000;
private final int minDurationToRetainAfterDiscardMs = 25000;
private final float bandwidthFraction = 0.75f;
private final float bufferedFractionToLiveEdgeForQualityIncrease = 0.75f;
public Factory(BandwidthMeter bandwidthMeter) {
this.bandwidthMeter = bandwidthMeter;
}
#Override
public ClassAdaptiveTrackSelection createTrackSelection(TrackGroup group, int... tracks) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality reset to Auto");
sHLSQuality = HLSQuality.Auto;
return new ClassAdaptiveTrackSelection(
group,
tracks,
bandwidthMeter,
maxInitialBitrate,
minDurationForQualityIncreaseMs,
maxDurationForQualityDecreaseMs,
minDurationToRetainAfterDiscardMs,
bandwidthFraction,
bufferedFractionToLiveEdgeForQualityIncrease
);
}
}
private static HLSQuality sHLSQuality = HLSQuality.Auto;
private final BandwidthMeter bandwidthMeter;
private final int maxInitialBitrate;
private final long minDurationForQualityIncreaseUs;
private final long maxDurationForQualityDecreaseUs;
private final long minDurationToRetainAfterDiscardUs;
private final float bandwidthFraction;
private final float bufferedFractionToLiveEdgeForQualityIncrease;
private int selectedIndex;
private int reason;
private ClassAdaptiveTrackSelection(TrackGroup group,
int[] tracks,
BandwidthMeter bandwidthMeter,
int maxInitialBitrate,
long minDurationForQualityIncreaseMs,
long maxDurationForQualityDecreaseMs,
long minDurationToRetainAfterDiscardMs,
float bandwidthFraction,
float bufferedFractionToLiveEdgeForQualityIncrease) {
super(group, tracks);
this.bandwidthMeter = bandwidthMeter;
this.maxInitialBitrate = maxInitialBitrate;
this.minDurationForQualityIncreaseUs = minDurationForQualityIncreaseMs * 1000L;
this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L;
this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L;
this.bandwidthFraction = bandwidthFraction;
this.bufferedFractionToLiveEdgeForQualityIncrease = bufferedFractionToLiveEdgeForQualityIncrease;
selectedIndex = determineIdealSelectedIndex(Long.MIN_VALUE);
reason = C.SELECTION_REASON_INITIAL;
}
#Override
public void updateSelectedTrack(long playbackPositionUs, long bufferedDurationUs, long availableDurationUs) {
long nowMs = SystemClock.elapsedRealtime();
// Stash the current selection, then make a new one.
int currentSelectedIndex = selectedIndex;
selectedIndex = determineIdealSelectedIndex(nowMs);
if (selectedIndex == currentSelectedIndex) {
return;
}
if (!isBlacklisted(currentSelectedIndex, nowMs)) {
// Revert back to the current selection if conditions are not suitable for switching.
Format currentFormat = getFormat(currentSelectedIndex);
Format selectedFormat = getFormat(selectedIndex);
if (selectedFormat.bitrate > currentFormat.bitrate
&& bufferedDurationUs < minDurationForQualityIncreaseUs(availableDurationUs)) {
// The selected track is a higher quality, but we have insufficient buffer to safely switch
// up. Defer switching up for now.
selectedIndex = currentSelectedIndex;
} else if (selectedFormat.bitrate < currentFormat.bitrate
&& bufferedDurationUs >= maxDurationForQualityDecreaseUs) {
// The selected track is a lower quality, but we have sufficient buffer to defer switching
// down for now.
selectedIndex = currentSelectedIndex;
}
}
// If we adapted, update the trigger.
if (selectedIndex != currentSelectedIndex) {
reason = C.SELECTION_REASON_ADAPTIVE;
}
}
#Override
public int getSelectedIndex() {
return selectedIndex;
}
#Override
public int getSelectionReason() {
return reason;
}
#Override
public Object getSelectionData() {
return null;
}
#Override
public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) {
if (queue.isEmpty()) {
return 0;
}
int queueSize = queue.size();
long bufferedDurationUs = queue.get(queueSize - 1).endTimeUs - playbackPositionUs;
if (bufferedDurationUs < minDurationToRetainAfterDiscardUs) {
return queueSize;
}
int idealSelectedIndex = determineIdealSelectedIndex(SystemClock.elapsedRealtime());
Format idealFormat = getFormat(idealSelectedIndex);
// If the chunks contain video, discard from the first SD chunk beyond
// minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal
// track.
for (int i = 0; i < queueSize; i++) {
MediaChunk chunk = queue.get(i);
Format format = chunk.trackFormat;
long durationBeforeThisChunkUs = chunk.startTimeUs - playbackPositionUs;
if (durationBeforeThisChunkUs >= minDurationToRetainAfterDiscardUs
&& format.bitrate < idealFormat.bitrate
&& format.height != Format.NO_VALUE && format.height < 720
&& format.width != Format.NO_VALUE && format.width < 1280
&& format.height < idealFormat.height) {
return i;
}
}
return queueSize;
}
private int determineIdealSelectedIndex(long nowMs) {
if (sHLSQuality != HLSQuality.Auto) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for " + String.valueOf(sHLSQuality));
for (int i = 0; i < length; i++) {
Format format = getFormat(i);
if (HLSUtil.getQuality(format) == sHLSQuality) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality set to " + String.valueOf(sHLSQuality));
return i;
}
}
}
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for auto quality " + String.valueOf(sHLSQuality));
long bitrateEstimate = bandwidthMeter.getBitrateEstimate();
long effectiveBitrate = bitrateEstimate == BandwidthMeter.NO_ESTIMATE
? maxInitialBitrate : (long) (bitrateEstimate * bandwidthFraction);
int lowestBitrateNonBlacklistedIndex = 0;
for (int i = 0; i < length; i++) {
if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
Format format = getFormat(i);
if (format.bitrate <= effectiveBitrate && HLSUtil.isQualityPlayable(format)) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality auto quality found " + String.valueOf(sHLSQuality));
return i;
} else {
lowestBitrateNonBlacklistedIndex = i;
}
}
}
return lowestBitrateNonBlacklistedIndex;
}
private long minDurationForQualityIncreaseUs(long availableDurationUs) {
boolean isAvailableDurationTooShort = availableDurationUs != C.TIME_UNSET
&& availableDurationUs <= minDurationForQualityIncreaseUs;
return isAvailableDurationTooShort
? (long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease)
: minDurationForQualityIncreaseUs;
}
static void setHLSQuality(HLSQuality HLSQuality) {
sHLSQuality = HLSQuality;
}
}
Hope it helps someone.
You can check out ExoPlayer_TrackSelection from github for changing video quality manually.
I am using Exo Player ExtractorMediaSource for playing video in my android app. I am downloading media from server and save in local database and on a specific time Alarm i play this media using ConcatenatingMediaSource in exo player. but first i check that all video file downloaded or not and start player with downloaded media source . and if any video is not downloaded then i want to download it in background at when it downloaded then i want to add this video in my already created playlist
This is sample code
private void playAndUpdateVideo(ArrayList<String> mediaSourc) {
simpleExoPlayerView.setVisibility(View.VISIBLE);
simpleExoPlayerView.setDefaultArtwork(null);
mainHandler = new Handler();
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new DefaultTrackSelector( videoTrackSelectionFactory);
dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, "com.cloveritservices.hype"), bandwidthMeter);
// 2. Create a default LoadControl
extractorsFactory = new DefaultExtractorsFactory();
LoadControl loadControl = new DefaultLoadControl();
// 3. Create the player
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
player.addListener(this);
//Set media controller
simpleExoPlayerView.setUseController(false);
simpleExoPlayerView.requestFocus();
// Bind the player to the view.
simpleExoPlayerView.setPlayer(player);
MediaSource[] mediaSources = new MediaSource[mediaSourc.size()];
for (int i=0;i<mediaSourc.size();i++)
{
mediaSources[i]= buildMediaSource(Uri.parse(mediaSourc.get(i)));
}
MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0]
: new ConcatenatingMediaSource(mediaSources);
LoopingMediaSource loopingSource = new LoopingMediaSource(mediaSource);
player.prepare(loopingSource);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
boolean isChecked = settings.getBoolean("switch", false);
if (!isChecked)
player.setVolume(0f);
else player.setVolume(2f);
player.setPlayWhenReady(true);
}
And here i am checking for video file that it is downloaded or not
if (CommonUtils.isExternalStorageExistAndWritable()) {
for (int i = 0; i < videoUrl.size(); i++) {
if (!new File(Environment.getExternalStorageDirectory().toString() + Constants.PROFILE_VIDEO_FOLDER + CommonUtils.fileFromUrl(videoUrl.get(i))).exists() && !CommonUtils.currentlyDownloading(context,CommonUtils.fileFromUrl(videoUrl.get(i)))) {
downloadByDownloadManager(videoUrl.get(i), CommonUtils.fileFromUrl(videoUrl.get(i)));
if (flag==Constants.FLAG_PLAY){downloadFlag=true;}
}
}
} else {
Toast.makeText(getApplicationContext(), "SD Card not mounted.Please Mount SD Card", Toast.LENGTH_SHORT).show();
}
if (flag==Constants.FLAG_PLAY && !downloadFlag)
{
playAndUpdateVideo(videoUrl);
}
public void downloadByDownloadManager(String url, String fileName1) {
downloadUrl=url;
fileName=fileName1;
request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("video file");
request.setTitle(fileName);
request.setNotificationVisibility(2);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
request.setDestinationInExternalPublicDir(Constants.PROFILE_VIDEO_FOLDER, fileName);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
// get download service and enqueue file
}
Please help that how to add missing video file later to playlist if it is not downloaded.
To add new video files to your playlist, you need a new MediaSource implementation which can handle a list of sources to enable resizing. This is fairly simple to achieve, the easiest way to do so is to create a modified implementation of ConcaternatingMediaSource which uses lists instead of arrays to store and iterate over media sources. You then replace the ConcaternatingMediaSource in playAndUpdateVideo with the new implementation using lists. This will allow you to add and remove from your playlist as you wish, you can append new media files when your download complete listener is triggered. Here is the full class for a media source implementation using lists:
public final class DynamicMediaSource implements MediaSource {
private List<MediaSource> mediaSources;
private SparseArray<Timeline> timelines;
private SparseArray<Object> manifests;
private Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;
private boolean isRepeatOneAtomic;
private Listener listener;
private DynamicTimeline timeline;
/**
* #param mediaSources The {#link MediaSource}s to concatenate. It is valid for the same
* {#link MediaSource} instance to be present more than once in the array.
*/
public DynamicMediaSource(List<MediaSource> mediaSources) {
this(false, mediaSources);
}
/**
* #param isRepeatOneAtomic Whether the concatenated media source shall be treated as atomic
* (i.e., repeated in its entirety) when repeat mode is set to {#code Player.REPEAT_MODE_ONE}.
* #param mediaSources The {#link MediaSource}s to concatenate. It is valid for the same
* {#link MediaSource} instance to be present more than once in the array.
*/
public DynamicMediaSource(boolean isRepeatOneAtomic, List<MediaSource> mediaSources) {
for (MediaSource mediaSource : mediaSources) {
Assertions.checkNotNull(mediaSource);
}
this.mediaSources = mediaSources;
this.isRepeatOneAtomic = isRepeatOneAtomic;
timelines = new SparseArray<Timeline>();
manifests = new SparseArray<Object>();
sourceIndexByMediaPeriod = new HashMap<>();
duplicateFlags = buildDuplicateFlags(mediaSources);
}
#Override
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
this.listener = listener;
for (int i = 0; i < mediaSources.size(); i++) {
if (!duplicateFlags.get(i)) {
final int index = i;
mediaSources.get(i).prepareSource(player, false, new Listener() {
#Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
handleSourceInfoRefreshed(index, timeline, manifest);
}
});
}
}
}
#Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
for (int i = 0; i < mediaSources.size(); i++) {
if (!duplicateFlags.get(i)) {
mediaSources.get(i).maybeThrowSourceInfoRefreshError();
}
}
}
#Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
int sourceIndex = timeline.getChildIndexByPeriodIndex(id.periodIndex);
MediaPeriodId periodIdInSource =
new MediaPeriodId(id.periodIndex - timeline.getFirstPeriodIndexByChildIndex(sourceIndex));
MediaPeriod mediaPeriod = mediaSources.get(sourceIndex).createPeriod(periodIdInSource, allocator);
sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex);
return mediaPeriod;
}
#Override
public void releasePeriod(MediaPeriod mediaPeriod) {
int sourceIndex = sourceIndexByMediaPeriod.get(mediaPeriod);
sourceIndexByMediaPeriod.remove(mediaPeriod);
mediaSources.get(sourceIndex).releasePeriod(mediaPeriod);
}
#Override
public void releaseSource() {
for (int i = 0; i < mediaSources.size(); i++) {
if (!duplicateFlags.get(i)) {
mediaSources.get(i).releaseSource();
}
}
}
private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
Object sourceManifest) {
// Set the timeline and manifest.
timelines.put(sourceFirstIndex, sourceTimeline);
manifests.put(sourceFirstIndex, sourceManifest);
// Also set the timeline and manifest for any duplicate entries of the same source.
for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
timelines.put(i, sourceTimeline);
manifests.put(i, sourceManifest);
}
}
for(int i= 0; i<mediaSources.size(); i++){
if(timelines.get(i) == null){
// Don't invoke the listener until all sources have timelines.
return;
}
}
timeline = new DynamicTimeline(timelines, isRepeatOneAtomic);
listener.onSourceInfoRefreshed(timeline, new ArrayList(asList(manifests)));
}
private static SparseArray<Boolean> buildDuplicateFlags(List<MediaSource> mediaSources) {
SparseArray<Boolean> duplicateFlags = new SparseArray<Boolean>();
IdentityHashMap<MediaSource, Void> sources = new IdentityHashMap<>(mediaSources.size());
for (int i = 0; i < mediaSources.size(); i++) {
MediaSource source = mediaSources.get(i);
if (!sources.containsKey(source)) {
sources.put(source, null);
duplicateFlags.append(i,false);
} else {
duplicateFlags.append(i,true);
}
}
return duplicateFlags;
}
/**
* A {#link Timeline} that is the concatenation of one or more {#link Timeline}s.
*/
public static final class DynamicTimeline extends AbstractConcatenatedTimeline {
private final SparseArray<Timeline> timelines;
private final int[] sourcePeriodOffsets;
private final int[] sourceWindowOffsets;
private final boolean isRepeatOneAtomic;
public DynamicTimeline(SparseArray<Timeline> timelines, boolean isRepeatOneAtomic) {
super(timelines.size());
int[] sourcePeriodOffsets = new int[timelines.size()];
int[] sourceWindowOffsets = new int[timelines.size()];
long periodCount = 0;
int windowCount = 0;
for (int i = 0; i < timelines.size(); i++) {
Timeline timeline = timelines.get(i);
periodCount += timeline.getPeriodCount();
Assertions.checkState(periodCount <= Integer.MAX_VALUE,
"ConcatenatingMediaSource children contain too many periods");
sourcePeriodOffsets[i] = (int) periodCount;
windowCount += timeline.getWindowCount();
sourceWindowOffsets[i] = windowCount;
}
this.timelines = timelines;
this.sourcePeriodOffsets = sourcePeriodOffsets;
this.sourceWindowOffsets = sourceWindowOffsets;
this.isRepeatOneAtomic = isRepeatOneAtomic;
}
#Override
public int getWindowCount() {
return sourceWindowOffsets[sourceWindowOffsets.length - 1];
}
#Override
public int getPeriodCount() {
return sourcePeriodOffsets[sourcePeriodOffsets.length - 1];
}
#Override
public int getNextWindowIndex(int windowIndex, #Player.RepeatMode int repeatMode) {
if (isRepeatOneAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
repeatMode = Player.REPEAT_MODE_ALL;
}
return super.getNextWindowIndex(windowIndex, repeatMode);
}
#Override
public int getPreviousWindowIndex(int windowIndex, #Player.RepeatMode int repeatMode) {
if (isRepeatOneAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
repeatMode = Player.REPEAT_MODE_ALL;
}
return super.getPreviousWindowIndex(windowIndex, repeatMode);
}
#Override
public int getChildIndexByPeriodIndex(int periodIndex) {
return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1;
}
#Override
protected int getChildIndexByWindowIndex(int windowIndex) {
return Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1;
}
#Override
protected int getChildIndexByChildUid(Object childUid) {
if (!(childUid instanceof Integer)) {
return C.INDEX_UNSET;
}
return (Integer) childUid;
}
#Override
protected Timeline getTimelineByChildIndex(int childIndex) {
return timelines.get(childIndex);
}
#Override
public int getFirstPeriodIndexByChildIndex(int childIndex) {
return childIndex == 0 ? 0 : sourcePeriodOffsets[childIndex - 1];
}
#Override
protected int getFirstWindowIndexByChildIndex(int childIndex) {
return childIndex == 0 ? 0 : sourceWindowOffsets[childIndex - 1];
}
#Override
protected Object getChildUidByChildIndex(int childIndex) {
return childIndex;
}
}
}
To check when the file is downloaded and set a download completed listener, you can use a BroadcastReceiver. A detailed example of how to set up the BroadcastReceiver is provided here.
I am using two media player, first media player will play songs with music and the other is vocal of that songs, this vocal player should be mute but play the wave graph of vocal music, how to get please help me out..!
I need to mute the media player while generating the waveform graph from audio.
I am using two media player, first media player will play songs with music and the other is vocal of that songs, this vocal player should be mute but play the wave graph of vocal music, how to get please help me out..!
I need to mute the media player while generating the waveform graph from audio.
Here is my code.
public class MainFinalAllActivity extends Activity {
private Button btnPlay;
// Media Player
private MediaPlayer mp;
private MediaPlayer mSilentPlayer; /* to avoid tunnel player issue */
private MediaPlayer vocalMediaPlayer;
private VisualizerView mVisualizerView;
// Handler to update UI timer, progress bar etc,.
private Handler mHandler = new Handler();
private int currentSongIndex = 0;
private ArrayList<HashMap<String, String>> songsList = new ArrayList<HashMap<String, String>>();
private MediaRecorder myAudioRecorder;
private String outputFile = null;
String vocalPath = "/sdcard/test_v.mp3";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_final_layout);
// All player buttons
btnPlay = (Button) findViewById(R.id.btnPlay);
songTitleLabel = (TextView) findViewById(R.id.songTitle);
// Mediaplayer
mp = new MediaPlayer();
vocalMediaPlayer = new MediaPlayer();
songsList = SelectedAlbumPlayList.goToFinalPageSongsList;
int songIndex = SelectedAlbumPlayList.songIndex;
mp.setLooping(true);
playSong(songIndex);
String pathOfSelectedSong = songsList.get(songIndex).get("songPath");
// We need to link the visualizer view to the media player so that
// it displays something
mVisualizerView = (VisualizerView) findViewById(R.id.visualizerView);
mVisualizerView.link(vocalMediaPlayer);
//start the line renderer
addLineRenderer();
/**
* Play button click event
* plays a song and changes button to pause image
* pauses a song and changes button to play image
* */
btnPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// check for already playing
if (mp.isPlaying()) {
if (mp != null) {
mp.pause();
vocalMediaPlayer.pause();
// Changing button image to play button
btnPlay.setText("Play");
}
} else {
// Resume song
if (mp != null) {
mp.start();
vocalMediaPlayer.start();
// Changing button image to pause button
btnPlay.setText("Pause");
}
}
}
});
}
/**
* Receiving song index from playlist view
* and play the song
*/
#Override
protected void onActivityResult(int requestCode,
int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == 100) {
currentSongIndex = data.getExtras().getInt("songIndex");
// play selected song
playSong(currentSongIndex);
}
}
/**
* Function to play a song
*
* #param songIndex - index of song
*/
public void playSong(int songIndex) {
// Play song
try {
mp.reset();
vocalMediaPlayer.reset();
//vocalMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setDataSource(songsList.get(songIndex).get("songPath"));
vocalMediaPlayer.setDataSource(vocalPath);
mp.prepare();
vocalMediaPlayer.prepare();
mp.start();
vocalMediaPlayer.start();
//vocalMediaPlayer.setVolume(0,0);
// Displaying Song title
String songTitle = songsList.get(songIndex).get("songTitle");
songTitleLabel.setText(songTitle);
//playVocalSong(vocalPath);
// Changing Button Image to pause image
btnPlay.setText("Pause");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Update timer on seekbar
* */
public void updateProgressBar() {
mHandler.postDelayed(mUpdateTimeTask, 100);
}
int currentAmplitude;
String TAG = null;
byte[] bytes;
/**
* Background Runnable thread
* */
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
Bundle b = new Bundle();
Message msg = mHandler.obtainMessage();
if (myAudioRecorder != null) {
int previousValue = currentAmplitude;
currentAmplitude = myAudioRecorder.getMaxAmplitude();
//bytes = currentAmplitude.toByteArray();
//amplitude = mRecorder.getMaxAmplitude();
b.putLong("currentTime", currentAmplitude);
//Log.i("AMPLITUDE", new Integer(currentAmplitude).toString());
} else {
b.putLong("currentTime", 0);
}
msg.setData(b);
mHandler.sendMessage(msg);
mHandler.postDelayed(this, 100);
}
};
private void addLineRenderer()
{
Paint linePaint = new Paint();
linePaint.setStrokeWidth(1f);
linePaint.setAntiAlias(true);
linePaint.setColor(Color.argb(88, 0, 128, 255));
Paint lineFlashPaint = new Paint();
lineFlashPaint.setStrokeWidth(5f);
lineFlashPaint.setAntiAlias(true);
//lineFlashPaint.setColor(Color.argb(188, 255, 255, 255));
lineFlashPaint.setColor(Color.rgb(255,69,0));
//LineRenderer lineRenderer = new LineRenderer(linePaint, lineFlashPaint, true);
LineRenderer lineRenderer = new LineRenderer(linePaint, lineFlashPaint, true);
mVisualizerView.addRenderer(lineRenderer);
}
#Override
public void onDestroy(){
super.onDestroy();
mp.stop();
vocalMediaPlayer.stop();
}
}
//VisualizerView class
/**
* A class that draws visualizations of data received from a
* {#link android.media.audiofx.Visualizer.OnDataCaptureListener#onWaveFormDataCapture } and
* {#link android.media.audiofx.Visualizer.OnDataCaptureListener#onFftDataCapture }
*/
public class VisualizerView extends View {
private static final String TAG = "VisualizerView";
private Handler mHandler = new Handler();;
private byte[] mBytes;
private byte[] mFFTBytes;
private Rect mRect = new Rect();
private Visualizer mVisualizer;
private Set<Renderer> mRenderers;
private Paint mFlashPaint = new Paint();
private Paint mFadePaint = new Paint();
public VisualizerView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs);
init();
}
public VisualizerView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public VisualizerView(Context context)
{
this(context, null, 0);
}
private void init() {
mBytes = null;
mFFTBytes = null;
mFlashPaint.setColor(Color.argb(122, 255, 255, 255));
mFadePaint.setColor(Color.argb(238, 255, 255, 255)); // Adjust alpha to change how quickly the image fades
mFadePaint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
mRenderers = new HashSet<Renderer>();
}
/**
* Links the visualizer to a player
* #param player - MediaPlayer instance to link to
*/
public void link(final MediaPlayer player)
{
if(player == null)
{
throw new NullPointerException("Cannot link to null MediaPlayer");
}
// Create the Visualizer object and attach it to our media player.
mVisualizer = new Visualizer(player.getAudioSessionId());
mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
//mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
// Pass through Visualizer data to VisualizerView
Visualizer.OnDataCaptureListener captureListener = new Visualizer.OnDataCaptureListener()
{
#Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes,
int samplingRate)
{
updateVisualizer(bytes);
getDisplay();
player.setVolume(0,0);
}
#Override
public void onFftDataCapture(Visualizer visualizer, byte[] bytes,
int samplingRate)
{
//updateVisualizerFFT(bytes);
}
};
mVisualizer.setDataCaptureListener(captureListener,
Visualizer.getMaxCaptureRate() / 2, true, true);
// Enabled Visualizer and disable when we're done with the stream
mVisualizer.setEnabled(true);
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
{
#Override
public void onCompletion(MediaPlayer mediaPlayer)
{
mVisualizer.setEnabled(false);
}
});
}
public void addRenderer(Renderer renderer)
{
if(renderer != null)
{
mRenderers.add(renderer);
}
}
public void clearRenderers()
{
mRenderers.clear();
}
/**
* Call to release the resources used by VisualizerView. Like with the
* MediaPlayer it is good practice to call this method
*/
public void release()
{
mVisualizer.release();
}
//calculating RMS Value from byte array
public int calculateRMSLevel(byte[] audioData) {
// audioData might be buffered data read from a data line
long lSum = 0;
for (int i = 0; i < audioData.length; i++) {
lSum = lSum + audioData[i];
}
double dAvg = lSum / audioData.length;
double sumMeanSquare = 0d;
for (int j = 0; j < audioData.length; j++) {
sumMeanSquare = sumMeanSquare + Math.pow(audioData[j] - dAvg, 2d);
}
double averageMeanSquare = sumMeanSquare / audioData.length;
return (int) (Math.pow(averageMeanSquare, 0.5d) + 0.5);
}
/**
* Pass data to the visualizer. Typically this will be obtained from the
* Android Visualizer.OnDataCaptureListener call back. See
* {#link android.media.audiofx.Visualizer.OnDataCaptureListener#onWaveFormDataCapture }
* #param bytes
*/
public void updateVisualizer(byte[] bytes) {
int t = calculateRMSLevel(bytes);
Visualizer.MeasurementPeakRms measurementPeakRms = new Visualizer.MeasurementPeakRms();
int x = mVisualizer.getMeasurementPeakRms(measurementPeakRms);
mBytes = bytes;
invalidate();
}
/**
* Pass FFT data to the visualizer. Typically this will be obtained from the
* Android Visualizer.OnDataCaptureListener call back. See
* {#link android.media.audiofx.Visualizer.OnDataCaptureListener#onFftDataCapture }
* #param bytes
*/
public void updateVisualizerFFT(byte[] bytes) {
int t = calculateRMSLevel(bytes);
//System.out.println("Amplitude:"+t);
mFFTBytes = bytes;
invalidate();
}
boolean mFlash = false;
/**
* Call this to make the visualizer flash. Useful for flashing at the start
* of a song/loop etc...
*/
public void flash() {
mFlash = true;
invalidate();
}
Bitmap mCanvasBitmap;
Canvas mCanvas;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Create canvas once we're ready to draw
mRect.set(0, 0, getWidth(), getHeight());
if(mCanvasBitmap == null)
{
mCanvasBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888);
}
if(mCanvas == null)
{
mCanvas = new Canvas(mCanvasBitmap);
}
if (mBytes != null) {
// Render all audio renderers
AudioData audioData = new AudioData(mBytes);
for(Renderer r : mRenderers)
{
r.render(mCanvas, audioData, mRect);
}
}
if (mFFTBytes != null) {
// Render all FFT renderers
FFTData fftData = new FFTData(mFFTBytes);
for(Renderer r : mRenderers)
{
r.render(mCanvas, fftData, mRect);
}
}
// Fade out old contents
mCanvas.drawPaint(mFadePaint);
if(mFlash)
{
mFlash = false;
mCanvas.drawPaint(mFlashPaint);
}
canvas.drawBitmap(mCanvasBitmap, new Matrix(), null);
}
}
This question already exists:
Closed 10 years ago.
Possible Duplicate:
how to convert or record .wav file in 16khz 16bit mono little-endian?
I want to implement audio recording from an android device at 16000 khz 16bit mono little endian wav file.
I had implemented the logic in android like this. I'm using one class name as extaudiorecorder.
public class ExtAudioRecorder {
private final static int[] sampleRates = {44100, 22050, 11025, 16000};
public static ExtAudioRecorder getInstanse(Boolean recordingCompressed) {
ExtAudioRecorder result = null;
if(recordingCompressed) {
result = new ExtAudioRecorder(false, AudioSource.MIC, sampleRates[3],
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
}
else {
int i=0;
do {
result = new ExtAudioRecorder(true, AudioSource.MIC, sampleRates[i],
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
} while((++i<sampleRates.length) &
!(result.getState() == ExtAudioRecorder.State.INITIALIZING));
}
return result;
}
/**
* INITIALIZING : recorder is initializing;
* READY : recorder has been initialized, recorder not yet started
* RECORDING : recording
* ERROR : reconstruction needed
* STOPPED: reset needed
*/
public enum State {INITIALIZING, READY, RECORDING, ERROR, STOPPED};
public static final boolean RECORDING_UNCOMPRESSED = true;
public static final boolean RECORDING_COMPRESSED = false;
// The interval in which the recorded samples are output to the file
// Used only in uncompressed mode
private static final int TIMER_INTERVAL = 120;
// Toggles uncompressed recording on/off;
// RECORDING_UNCOMPRESSED / RECORDING_COMPRESSED
private boolean rUncompressed;
// Recorder used for uncompressed recording
private AudioRecord audioRecorder = null;
// Recorder used for compressed recording
private MediaRecorder mediaRecorder = null;
// Stores current amplitude (only in uncompressed mode)
private int cAmplitude = 0;
// Output file path
private String filePath = null;
// Recorder state; see State
private State state;
// File writer (only in uncompressed mode)
private RandomAccessFile randomAccessWriter;
// Number of channels, sample rate, sample size(size in bits), buffer size, audio source, sample size(see AudioFormat)
private short nChannels;
private int sRate;
private short bSamples;
private int bufferSize;
private int aSource;
private int aFormat;
// Number of frames written to file on each output(only in uncompressed mode)
private int framePeriod;
// Buffer for output(only in uncompressed mode)
private byte[] buffer;
// Number of bytes written to file after header(only in uncompressed mode)
// after stop() is called, this size is written to the header/data chunk in the wave file
private int payloadSize;
/**
* Returns the state of the recorder in a RehearsalAudioRecord.State typed object.
* Useful, as no exceptions are thrown.
* #return recorder state
*/
public State getState() { return state; }
/**
* Method used for recording.
*/
private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener() {
public void onPeriodicNotification(AudioRecord recorder) {
audioRecorder.read(buffer, 0, buffer.length); // Fill buffer
try {
randomAccessWriter.write(buffer); // Write buffer to file
payloadSize += buffer.length;
if (bSamples == 16) {
for (int i=0; i<buffer.length/2; i++) { // 16bit sample size
short curSample = getShort(buffer[i*2], buffer[i*2+1]);
if (curSample > cAmplitude) { // Check amplitude
cAmplitude = curSample;
}
}
}
else { // 8bit sample size
for (int i=0; i<buffer.length; i++) {
if (buffer[i] > cAmplitude) { // Check amplitude
cAmplitude = buffer[i];
}
}
}
}
catch (IOException e) {
Log.e(ExtAudioRecorder.class.getName(),
"Error occured in updateListener, recording is aborted");
//stop();
}
}
public void onMarkerReached(AudioRecord recorder) {
// NOT USED
}
};
/**
* Default constructor
* Instantiates a new recorder, in case of compressed recording the parameters can be left as 0.
* In case of errors, no exception is thrown, but the state is set to ERROR
*/
public ExtAudioRecorder(boolean uncompressed, int audioSource, int sampleRate,
int channelConfig, int audioFormat) {
try {
rUncompressed = uncompressed;
if (rUncompressed) { // RECORDING_UNCOMPRESSED
if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
bSamples = 16;
}
else { bSamples = 8; }
if (channelConfig == AudioFormat.CHANNEL_CONFIGURATION_MONO) {
nChannels = 1;
}
else { nChannels = 2; }
aSource = audioSource;
sRate = sampleRate;
aFormat = audioFormat;
framePeriod = sampleRate * TIMER_INTERVAL / 1000;
bufferSize = framePeriod * 2 * bSamples * nChannels / 8;
if (bufferSize < AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat))
{ // Check to make sure buffer size is not smaller than the smallest allowed one
bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
// Set frame period and timer interval accordingly
framePeriod = bufferSize / ( 2 * bSamples * nChannels / 8 );
Log.w(ExtAudioRecorder.class.getName(), "Increasing buffer size to " + Integer.toString(bufferSize));
}
audioRecorder = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, bufferSize);
if (audioRecorder.getState() != AudioRecord.STATE_INITIALIZED)
throw new Exception("AudioRecord initialization failed");
audioRecorder.setRecordPositionUpdateListener(updateListener);
audioRecorder.setPositionNotificationPeriod(framePeriod);
} else
{ // RECORDING_COMPRESSED
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
}
cAmplitude = 0;
filePath = null;
state = State.INITIALIZING;
} catch (Exception e)
{
if (e.getMessage() != null)
{
Log.e(ExtAudioRecorder.class.getName(), e.getMessage());
}
else
{
Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured while initializing recording");
}
state = State.ERROR;
}
}
/**
* Sets output file path, call directly after construction/reset.
*
* #param output file path
*
*/
public void setOutputFile(String argPath)
{
try
{
if (state == State.INITIALIZING)
{
filePath = argPath;
if (!rUncompressed)
{
mediaRecorder.setOutputFile(filePath);
}
}
}
catch (Exception e)
{
if (e.getMessage() != null)
{
Log.e(ExtAudioRecorder.class.getName(), e.getMessage());
}
else
{
Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured while setting output path");
}
state = State.ERROR;
}
}
/**
*
* Returns the largest amplitude sampled since the last call to this method.
*
* #return returns the largest amplitude since the last call, or 0 when not in recording state.
*
*/
public int getMaxAmplitude()
{
if (state == State.RECORDING)
{
if (rUncompressed)
{
int result = cAmplitude;
cAmplitude = 0;
return result;
}
else
{
try
{
return mediaRecorder.getMaxAmplitude();
}
catch (IllegalStateException e)
{
return 0;
}
}
}
else
{
return 0;
}
}
/**
*
* Prepares the recorder for recording, in case the recorder is not in the INITIALIZING state and the file path was not set
* the recorder is set to the ERROR state, which makes a reconstruction necessary.
* In case uncompressed recording is toggled, the header of the wave file is written.
* In case of an exception, the state is changed to ERROR
*
*/
public void prepare()
{
try
{
if (state == State.INITIALIZING)
{
if (rUncompressed)
{
if ((audioRecorder.getState() == AudioRecord.STATE_INITIALIZED) & (filePath != null))
{
// write file header
randomAccessWriter = new RandomAccessFile(filePath, "rw");
randomAccessWriter.setLength(0); // Set file length to 0, to prevent unexpected behavior in case the file already existed
randomAccessWriter.writeBytes("RIFF");
randomAccessWriter.writeInt(0); // Final file size not known yet, write 0
randomAccessWriter.writeBytes("WAVE");
randomAccessWriter.writeBytes("fmt ");
randomAccessWriter.writeInt(Integer.reverseBytes(16)); // Sub-chunk size, 16 for PCM
randomAccessWriter.writeShort(Short.reverseBytes((short) 1)); // AudioFormat, 1 for PCM
randomAccessWriter.writeShort(Short.reverseBytes(nChannels));// Number of channels, 1 for mono, 2 for stereo
randomAccessWriter.writeInt(Integer.reverseBytes(sRate)); // Sample rate
randomAccessWriter.writeInt(Integer.reverseBytes(sRate*bSamples*nChannels/8)); // Byte rate, SampleRate*NumberOfChannels*BitsPerSample/8
randomAccessWriter.writeShort(Short.reverseBytes((short)(nChannels*bSamples/8))); // Block align, NumberOfChannels*BitsPerSample/8
randomAccessWriter.writeShort(Short.reverseBytes(bSamples)); // Bits per sample
randomAccessWriter.writeBytes("data");
randomAccessWriter.writeInt(0); // Data chunk size not known yet, write 0
buffer = new byte[framePeriod*bSamples/8*nChannels];
state = State.READY;
}
else
{
Log.e(ExtAudioRecorder.class.getName(), "prepare() method called on uninitialized recorder");
state = State.ERROR;
}
}
else
{
mediaRecorder.prepare();
state = State.READY;
}
}
else
{
Log.e(ExtAudioRecorder.class.getName(), "prepare() method called on illegal state");
release();
state = State.ERROR;
}
}
catch(Exception e)
{
if (e.getMessage() != null)
{
Log.e(ExtAudioRecorder.class.getName(), e.getMessage());
}
else
{
Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured in prepare()");
}
state = State.ERROR;
}
}
/**
*
*
* Releases the resources associated with this class, and removes the unnecessary files, when necessary
*
*/
public void release()
{
if (state == State.RECORDING)
{
stop();
}
else
{
if ((state == State.READY) & (rUncompressed))
{
try
{
randomAccessWriter.close(); // Remove prepared file
}
catch (IOException e)
{
Log.e(ExtAudioRecorder.class.getName(), "I/O exception occured while closing output file");
}
(new File(filePath)).delete();
}
}
if (rUncompressed)
{
if (audioRecorder != null)
{
audioRecorder.release();
}
}
else
{
if (mediaRecorder != null)
{
mediaRecorder.release();
}
}
}
/**
*
*
* Resets the recorder to the INITIALIZING state, as if it was just created.
* In case the class was in RECORDING state, the recording is stopped.
* In case of exceptions the class is set to the ERROR state.
*
*/
public void reset()
{
try
{
if (state != State.ERROR)
{
release();
filePath = null; // Reset file path
cAmplitude = 0; // Reset amplitude
if (rUncompressed)
{
audioRecorder = new AudioRecord(aSource, sRate, nChannels+1, aFormat, bufferSize);
}
else
{
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
}
state = State.INITIALIZING;
}
}
catch (Exception e)
{
Log.e(ExtAudioRecorder.class.getName(), e.getMessage());
state = State.ERROR;
}
}
/**
*
*
* Starts the recording, and sets the state to RECORDING.
* Call after prepare().
*
*/
public void start()
{
if (state == State.READY)
{
if (rUncompressed)
{
payloadSize = 0;
audioRecorder.startRecording();
audioRecorder.read(buffer, 0, buffer.length);
}
else
{
mediaRecorder.start();
}
state = State.RECORDING;
}
else
{
Log.e(ExtAudioRecorder.class.getName(), "start() called on illegal state");
state = State.ERROR;
}
}
/**
*
*
* Stops the recording, and sets the state to STOPPED.
* In case of further usage, a reset is needed.
* Also finalizes the wave file in case of uncompressed recording.
*
*/
public void stop()
{
if (state == State.RECORDING)
{
if (rUncompressed)
{
audioRecorder.stop();
try
{
randomAccessWriter.seek(4); // Write size to RIFF header
randomAccessWriter.writeInt(Integer.reverseBytes(36+payloadSize));
randomAccessWriter.seek(40); // Write size to Subchunk2Size field
randomAccessWriter.writeInt(Integer.reverseBytes(payloadSize));
randomAccessWriter.close();
}
catch(IOException e)
{
Log.e(ExtAudioRecorder.class.getName(), "I/O exception occured while closing output file");
state = State.ERROR;
}
}
else
{
mediaRecorder.stop();
}
state = State.STOPPED;
}
else
{
Log.e(ExtAudioRecorder.class.getName(), "stop() called on illegal state");
state = State.ERROR;
}
}
/*
*
* Converts a byte[2] to a short, in LITTLE_ENDIAN format
*
*/
private short getShort(byte argB1, byte argB2)
{
return (short)(argB1 | (argB2 << 8));
}
}
And i m using that class in my main activity as here ..i m recording sound on button click and stop it on another button click
package com.test.android_recorder;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.view.Menu;
import android.widget.Button;
import android.widget.TextView;
import android.view.View;
import android.view.View.OnClickListener;
public class AndroidRecorder extends Activity {
ExtAudioRecorder extAudioRecorder;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(com.test.android_recorder.R.layout.activity_android_recorder);
Button button = (Button) findViewById(R.id.button1);
Button buttonstop = (Button) findViewById(R.id.buttonstop);
// Start recording
extAudioRecorder = ExtAudioRecorder.getInstanse(true);
button.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
TextView txt1=(TextView)findViewById(com.test.android_recorder.R.id.textView1);
txt1.setText("Button clciked");
// Compressed recording (AMR)
//extAudioRecorder = ExtAudioRecorder.getInstanse(false); // Uncompressed recording (WAV)
extAudioRecorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath() + "/javarecorder");
extAudioRecorder.prepare();
extAudioRecorder.start();
// ImageView iv = (ImageView) findViewById(R.id.imageview1);
//iv.setVisibility(View.VISIBLE);
}
});
buttonstop.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
//TextView txt1=(TextView)findViewById(com.test.android_recorder.R.id.textView1);
// txt1.setText("Button clciked");
// ImageView iv = (ImageView) findViewById(R.id.imageview1);
//iv.setVisibility(View.VISIBLE);
// Stop recording
extAudioRecorder.stop();
extAudioRecorder.release();
}
});
/*
*/
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(com.test.android_recorder.R.menu.activity_android_recorder, menu);
return true;
}
}
My Problem is that now file is recording but the recorded file is too noisy and unclear.i want to use this recorded file in sphinx 4 speech recgnition.and its require 16000KHZ 16 bit mono big endian..but when i use the recorded file in my sphhinx4 speech recognition then it give me unsupported source error...i want to make my wav file quality good...how can i?
Please Help me Out
private final static int[] sampleRates = {44100, 22050, 11025, 8000};
none of these sampleRates are 16khz.
Edit:
illegal state often times indicates that you don't have the permission added in your manifest.xml.