Xamarin Forms Shared Play Mp3 file on Android - android

I have asked this question in Xamarins forum but nobody has answered, im hoping this forum can provide an answer for me.
My problem is how to play an mp3 file from a shared project on android.
I'm making a Xamarin Forms project and uses interfaces to make platform specific code for playning tha mp3 file.
I have embedded 360 mp3 files in my shared project, and the pass the file to the interface to make the mp3 play.
The class AudioHelper returns a stream by getting the correct mp3 file based on it's name
var assembly = typeof(StaveApp_v2.App).GetTypeInfo().Assembly;
var stream = assembly.GetManifestResourceStream
(resourcePrefix + $"Audio.{name}.mp3");
return stream;
Then I pass the stream to the Interface
var audioStream = AudioHelper.AudioFile("aften");
DependencyService.Get<IPlayWord>().Play(audioStream);
On windows the file plays as expected, but I’m having trouble getting the file to play on android. I'm using AudioTrack to play the sound (the sound should play 2 times). The app plays a scratchy sound 2 times with a duration matching the file length, but I can’t get it to play correctly.
This is the code im using
var memoryStream = new MemoryStream();
audio.CopyTo(memoryStream);
var audioBytes = memoryStream.ToArray();
var byteLenght = audioBytes.Length * sizeof(short);
var audioTrack = new AudioTrack(Android.Media.Stream.Music,
16000,
ChannelOut.Mono,
Encoding.Pcm16bit,
byteLenght,
AudioTrackMode.Static);
for (int i = 0; i < 2; i++)
{
try
{
audioTrack.Write(audioBytes, 0, audioBytes.Length);
audioTrack.Play();
}
catch (IllegalStateException illEx)
{
Log.Debug("StaveApp", $"Unable to initialize audio exception {illEx.Message}");
}
await Task.Delay(2000);
audioTrack.Stop();
audioTrack.ReloadStaticData();
}
audioTrack.Release();
audioTrack.Dispose();
I'm getting no errors in logcat and

Related

Get audio input stream from a locale file on android

I am trying to get the audio input stream from a file in the local file system on an android device.
This is so that i can use this library to show a wave form for the audio file.
https://github.com/newventuresoftware/WaveformControl/blob/master/app/src/main/java/com/newventuresoftware/waveformdemo/MainActivity.java#L125
The example in the project uses rawResource like so
InputStream is = getResources().openRawResource(R.raw.jinglebells);
This input stream is later converted into byte array and passed to somewhere that uses it to paint a wave picture and sound.
however when I did
InputStream is = new InputFileSystem(new File(filePath));
But this does not seem to work properly. The image generated is wrong, and the sound played is nothing like what the file actually is.
This is the body of the function in that library that gets the input stream and convert it into byte arrays.
private short[] getAudioSample() throws IOException {
// If i replace this part with new FileInput(new File(filePath))
// the generated "samples" from it does not work properly with the library.
InputStream is = getResources().openRawResource(R.raw.jinglebells);
byte[] data;
try {
data = IOUtils.toByteArray(is);
} finally {
if (is != null) {
is.close();
}
}
ShortBuffer sb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] samples = new short[sb.limit()];
sb.get(samples);
return samples;
}
The sound file that I would like to get processed and pass to that library is created by a MediaRecorder with the following configurations
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
Basically that library requires PCM samples. Using that 3gp FileInputStream generated by MediaRecorder directly is not gonna cut it.
What I did is to use MediaExtractor and MediaCodec to convert the 3gp audio data into PCM samples by samples. and then it is fed into that library. Then everything worked =)
The logic in encoding audio data can be almost directly taken from here this awesome github repo

Audio will not play on Android using phonegap, but works fine on iOS

It works fine on iOS.
I have looked many answers including these 2:
Play sound on Phonegap app for Android
HTML5 audio not playing in PhoneGap App (Possible to use Media?)
I have tried all of the solutions:
Changing the path to /android_asset/www/ for Android
Using .ogg files
Pre load the audio with a play() / pause().
Using the Media plugin provided by Cordova/Phonegap
Here is my current code:
if(device.platform === "Android") //DIFFERENT PATH FOR ANDROID
{
url = "/android_asset/www/sound.ogg";
}else{
url = "sound.mp3";
}
alert(url);
sound = new Media(url);
sound.play();
Anyone have an ideas?? It feels like I have been going round in circles
I had similar and this solution worked for me
Get complete path of your application using window.location.pathname, this way you dont need to worry about device type, it will give you the complete path including index.html and by using the below function simply strip off index.html from the string and append your media file.
function getPhoneGapPath() {
var path = window.location.pathname;
path = path.substr( path, path.length - 10 ); //strip off index.html
return 'file://' + path;
};
And then
url = getPhoneGapPath()+'sound.ogg'; //OR MP3
alert(url);
sound = new Media(url);
sound.play();
Hope this helps
I finally managed to get it working!!
I first initialising the audio file by finding the full Android path using #Sarim Sidd help above and then preloading the audio file:
//INITIALISE SOUND
if(device.platform === "Android") //DIFFERENT PATH FOR ANDROID
{
//navigator.notification.beep(1);
var path = window.location.pathname;
path = path.substr( path, path.length - 10 ); //strip off index.html
url = 'file://'+path+"sound.ogg";
}else{
url = "sound.mp3";
}
//sound = new Media(url);
sound = new Media(url);
sound.play();
sound.pause();
Then when to play the audio I forced the volume to full (1.0) before calling the play method. For some reason I noticed the audio kept reseting to mute each time I started the app:
//PLAY BELL SOUND THROUGH APP
sound.setVolume(1.0);
sound.play();

How to play a zipped wave file in Flash Mobile

I am working on a project in Flash Mobile using ActionScript. I have a zipped wav file that I need to be able to de serialize and play as needed in a Button Press action. Below is the code for zipping the wav file.
mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
btnRecord.setStyle("icon", recOff);
sampleCount++;
// save the raw PCM samples as a bare WAV file
var wav:ByteArray = new ByteArray();
var writer:WAVWriter = new WAVWriter();
writer.numOfChannels = 1;
writer.sampleBitRate = 16;
writer.samplingRate = 11025;
samples.position = 0;
writer.processSamples(wav, samples, 11025, 1);
wav.position = 0;
// zip the WAV file
var fzip:FZip = new FZip();
fzip.addFile(name + sampleCount.toString(), wav);
var zip:ByteArray = new ByteArray();
fzip.serialize(zip);
var recSpot:Object = {
id: null,
audio: zip,
name: "New Audio File " + newRecNum,
existsdb: "false"
};
newRecNum++;
recordings.addItem(recSpot);
}
What can I do to play this file, really haven't had to play a zipped file before.
I'm not familiar with WAVWriter (which is probably somewhat beside the point), but here's what I do know.
Firstly, because of the nature of a compression, you cannot (as far as I know) play a zipped audio file, period. You will need to unzip it first.
A quick Google search turned up THIS AS3 TUTORIAL on unzipping with FZIP. The example program is using .PNGs, but I would assume you can adjust it to work with the raw .WAV file you zipped earlier. Skip down to Step 5 for the actual code. (You'll need to rewrite it to work with your interface, obviously.)
You won't need the DataProvider variable in step 5, as that is for components, specifically. You'll need to load your data into something else. If your method of playing WAV files is anything like mine (I use the as3WAVSound class), you'll probably want to load the data into a ByteArray and play off of that.
You also probably won't need the for loop he uses in step 10, as your code appears to be creating a ZIP with only one WAV file. That simplifies things considerably.
Anyway, I hope that answers your question!

Using AudioTrack in AudioTrack.MODE_STATIC?

I've found lots of tutorials and posts showing how to use AudioTrack to play wav files in AudioTrack.MODE_STREAM and I've successfully implemented this example.
However I'm having issues with performance when playing multiple audio tracks at once and thinking that I should first create the tracks using AudioTrack.MODE_STATIC then just call play each time.
I can't find any resources on how to implement this. How can I do this?
Thanks
The two main sticking points for me were realizing that .write() comes first and that the instantiated player must have the size of the entire clip as the buffer_size_in_bytes.
Assuming you have recorded a PCM file using AudioRecord, you can play it back with STATIC_MODE like so...
File file = new File(FILENAME);
int audioLength = (int)file.length();
byte filedata[] = new byte[audioLength];
try{
InputStream inputStream = new BufferedInputStream(new FileInputStream(FILENAME));
int lengthOfAudioClip = inputStream.read(filedata, 0, audioLength);
player = new AudioTrack(STREAM_TYPE, SAMPLE_RATE, CHANNEL_OUT_CONFIG, AUDIO_FORMAT,audioLength, AUDIO_MODE);
player.write(filedata, OFFSET, lengthOfAudioClip);
player.setPlaybackRate(playbackRate);
player.play();
}

How can i pause voice recording in Android?

My aim is to pause in recording file.
I see in Android developer site its but Media Recorder have not pause option.
Java supports merge two audio file programatically but In android its not work.
Join two WAV files from Java?
And also I used default device audio recorder Apps which is available in all device but in Samsung few devices have not returened recording path.
Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
startActivityForResult(intent,REQUESTCODE_RECORDING);
Any one help for voice recording with pause functionality.
http://developer.android.com/reference/android/media/MediaRecorder.html
MediaRecorder does not have pause and resume methods. You need to use stop and start methods instead.
I had such a requirement in one of my projects, What we done was like make a raw file for saving recorded data in start of recording using AudioRecord , the for each resume we append the data to the same file
like
FileOutputStream fos= new FileOutputStream(filename, true);
here the filename is the name of the raw file and append the new recording data to it.
And when user stop the recording we will convert the entire raw file to .wav( or other) formats. Sorry that i cant post the entire code. Hope this will give you a direction to work.
You can refer my answer here if still have this issue. For API level >= 24 pause/resume methods are available in Android MediaRecorder class.
For API level < 24
Add below dependency in your gradle file:
compile 'com.googlecode.mp4parser:isoparser:1.0.2'
The solution is to stop recorder when user pause and start again on resume as already mentioned in many other answers in stackoverflow. Store all the audio/video files generated in an array and use below method to merge all media files. The example is taken from mp4parser library and modified little bit as per my need.
public static boolean mergeMediaFiles(boolean isAudio, String sourceFiles[], String targetFile) {
try {
String mediaKey = isAudio ? "soun" : "vide";
List<Movie> listMovies = new ArrayList<>();
for (String filename : sourceFiles) {
listMovies.add(MovieCreator.build(filename));
}
List<Track> listTracks = new LinkedList<>();
for (Movie movie : listMovies) {
for (Track track : movie.getTracks()) {
if (track.getHandler().equals(mediaKey)) {
listTracks.add(track);
}
}
}
Movie outputMovie = new Movie();
if (!listTracks.isEmpty()) {
outputMovie.addTrack(new AppendTrack(listTracks.toArray(new Track[listTracks.size()])));
}
Container container = new DefaultMp4Builder().build(outputMovie);
FileChannel fileChannel = new RandomAccessFile(String.format(targetFile), "rw").getChannel();
container.writeContainer(fileChannel);
fileChannel.close();
return true;
}
catch (IOException e) {
Log.e(LOG_TAG, "Error merging media files. exception: "+e.getMessage());
return false;
}
}
Use flag isAudio as true for Audio files and false for Video files.
You can't do it using Android API, but you can save a lot of mp4 files and merge it using mp4parser: powerful library written in Java. Also see my simple recorder with a "pause": https://github.com/lassana/continuous-audiorecorder.

Categories

Resources