Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating a FileDescriptor on Android without first opening a file

In Android, is it possible to generate a FileDescriptor directly from a byte array, without having to open a file first?

In Android 2.2, I am generating a MIDI file on the fly, and then playing it back using MediaPlayer. I've included the text of the Main.java file that does this successfully below. So far so good.

However, this process first calls...

FileOutputStream outputStream = openFileOutput(file, MODE_PRIVATE);
outputStream.write(byteStream);
outputStream.close();

... to write out the file, and then calls...

FileInputStream inputStream = new FileInputStream(midifile);
FileDescriptor fileDescriptor = inputStream.getFD();

... to read it back in, before calling:

mediaPlayer.setDataSource(fileDescriptor);

This seems to me to be wasteful. Can I create the FileDescriptor directly from the byteArray, so that the MIDI stream can be played immediately?


== Working code ==

package com.example.midi;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;

public class Main extends Activity {

  private String file = "midi.mid";
  private MediaPlayer mediaPlayer;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

    createNewMIDIFile();
    playNewMIDIFile();
  }

  public void createNewMIDIFile() {
    Integer[] stream = new Integer[]{
        //
        0x4d, 0x54, 0x68, 0x64, // MThd = MIDI file designator
        0x00, 0x00, 0x00, 0x06, // Standard MIDI File (SMF)
        0x00, 0x01, 0x00, 0x02, // multiple-track format: 2 tracks
        0x00, 0x40, // 64 ticks per beat (quarter note)
        0x4D, 0x54, 0x72, 0x6B, // Header for track 1
        0x00, 0x00, 0x00, 0x0B, // 11  bytes to describe the track
        0x00, 0xFF, 0x51, 0x03, // set tempo:
        0x0F, 0x42, 0x40, //  1,000,000 microseconds / beat: 60 bpm
        0x00, 0xFF, 0x2F, 0x00, // End of track 1
        0x4D, 0x54, 0x72, 0x6B, // Header for track 2
        0x00, 0x00, 0x00, 0x0F, // 15 bytes to describe the track
        0x00, // Immediately
        0xC1, 0x01, // change instrument for track 2 to piano
        0x00, // Immediately
        0x91, 0x3C, 0x7F, // play middle C with a velocity of 127
        0x30, // 48 ticks later (dotted eighth note)
        0x81, 0x3C, 0x00, // stop playing the middle C
        0x00, 0xFF, 0x2F, 0x00 // End of track 2
    };

    int length = stream.length;
    byte[] byteStream = new byte[length];
    for (int ii = 0; ii < length; ii++) {
      byteStream[ii] = (byte) (stream[ii] % 256);
    }

    try {
      FileOutputStream outputStream = openFileOutput(file, MODE_PRIVATE);
      outputStream.write(byteStream);
      outputStream.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void play(View view) {
  /* Triggered by a button defined in activity_main.xml as 
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="play"
    android:text="Play MIDI" />
  */
    playNewMIDIFile();
  }

  public void playNewMIDIFile() {
    try {
      String filename = getFilesDir() + "/" + file;
      File midifile = new File(filename);
      FileInputStream inputStream = new FileInputStream(midifile);
      FileDescriptor fileDescriptor = inputStream.getFD();
      mediaPlayer.reset();
      mediaPlayer.setDataSource(fileDescriptor);
      inputStream.close();
      mediaPlayer.prepare();
      mediaPlayer.start();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

For more information on building a MIDI file on the fly, see kevinboone.net, skytopia and sonicspot

like image 762
James Newton Avatar asked Oct 31 '14 18:10

James Newton


1 Answers

It is not necessary to simulate FileDescriptor.
You can implement your own MediaDataSource and set it invoking MediaPlayer::setDataSource(yourMediaDataSource).

like image 142
Eugene Avatar answered Sep 19 '22 11:09

Eugene