package com.codefixia.audio;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.regex.Pattern;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Environment;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.Patterns;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import com.codefixia.drumcloud.DrumCloud;
import com.codefixia.drumcloud.R;
import com.codefixia.googledrive.DownloadFile;
import com.leff.midi.MidiFile;
import com.leff.midi.MidiTrack;
import com.leff.midi.event.MidiEvent;
import com.leff.midi.event.NoteOff;
import com.leff.midi.event.NoteOn;
import com.leff.midi.event.meta.Tempo;
import com.leff.midi.event.meta.TimeSignature;
public class MidiFileIO {
public final static String midiFolder=Environment.getExternalStorageDirectory().getPath()+"/drumcloud/midi/";
public final static int basePitch=35;
public static String fileToLoad="";
public static void showMidiPreLoadDialog(final DrumCloud drumCloud,final float BPM,final boolean[][] samplesPerBeat){
final Dialog dialog = new Dialog(drumCloud);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.midipreload);
dialog.setCancelable(true);
final Button midiLoadButton = (Button) dialog.findViewById(R.id.midiLoadButton);
final Button midiCancelButton = (Button) dialog.findViewById(R.id.midiCancelButton);
Spinner spinner = (Spinner) dialog.findViewById(R.id.Spinner01);
File midiFolderFile=new File(midiFolder);
File[] midis=midiFolderFile.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
return filename.toLowerCase().endsWith(".midi");
}
});
String[] midiFiles = new String[midis.length];
for (int i = 0; i < midis.length; i++) midiFiles[i] = midis[i].getName();
ArrayAdapter<CharSequence> adapter = new ArrayAdapter(dialog.getContext(), android.R.layout.simple_spinner_item, midiFiles);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(
new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
fileToLoad=parent.getSelectedItem().toString();
Log.d("FILE","SELECTED:"+fileToLoad);
}
public void onNothingSelected(AdapterView<?> parent) {}
});
midiLoadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
if(fileToLoad.length()>0){
File inputMidiFile=new File(midiFolder+""+fileToLoad);
Log.d("FILE","LOADING MIDI:"+inputMidiFile);
MidiFileIO.load(inputMidiFile,BPM,samplesPerBeat);
}
}
});
midiCancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
dialog.dismiss();
}
});
dialog.show();
}
public static void showMidiPostSaveDialog(final DrumCloud drumCloud,final File savedFile){
final Dialog dialog = new Dialog(drumCloud);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.midipostsave);
dialog.setCancelable(true);
final Button midiShareButton = (Button) dialog.findViewById(R.id.midiShareFileButton);
final Button midiOpenButton = (Button) dialog.findViewById(R.id.midiOpenFileButton);
final Button midiCancelButton = (Button) dialog.findViewById(R.id.midiCancelButton);
midiShareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
Intent i = new Intent(Intent.ACTION_SEND_MULTIPLE);
i.setType("text/plain");
ArrayList<Uri> uris = new ArrayList<Uri>();
uris.add(Uri.fromFile(savedFile));
Uri uri;
File file;
for(int p=0;p<drumCloud.getPlayerKick().length;p++){
Log.d("MAIL FILE","PATH:"+drumCloud.getPlayerKick()[p].getFile().getAbsoluteFile());
file=drumCloud.getPlayerKick()[p].getFile();
if(!file.exists()){
file=copyFileFromAssets(drumCloud,file);
}
uri=Uri.fromFile(file);
Log.d("MAIL FILE","URI:"+uri.toString());
uris.add(uri);
file=drumCloud.getPlayerBass()[p].getFile();
if(!file.exists()){
file=copyFileFromAssets(drumCloud,file);
}
uri=Uri.fromFile(file);
uris.add(uri);
file=drumCloud.getPlayerSnare()[p].getFile();
if(!file.exists()){
file=copyFileFromAssets(drumCloud,file);
}
uri=Uri.fromFile(file);
uris.add(uri);
file=drumCloud.getPlayerHitHat()[p].getFile();
if(!file.exists()){
file=copyFileFromAssets(drumCloud,file);
}
uri=Uri.fromFile(file);
uris.add(uri);
}
Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(drumCloud).getAccounts();
ArrayList<String> emails=new ArrayList<String>();
for (Account account : accounts) {
if (emailPattern.matcher(account.name).matches()) {
if(!emails.contains(account.name))
emails.add(account.name);
}
}
i.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
//i.putExtra(android.content.Intent.EXTRA_CC, new String[]{emailCC});
i.putExtra(android.content.Intent.EXTRA_EMAIL, emails.toArray(new String[emails.size()]));
i.putExtra(android.content.Intent.EXTRA_SUBJECT, drumCloud.getString(R.string.midi_mail_subject));
String emailBody="Date:"+DateFormat.getDateFormat(drumCloud).format(new Date());
emailBody+="\nTime:"+DateFormat.getTimeFormat(drumCloud).format(new Date());
i.putExtra(android.content.Intent.EXTRA_TEXT, emailBody);
drumCloud.startActivity(Intent.createChooser(i, "E-mail"));
}
});
midiOpenButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
if(savedFile.exists() && savedFile.isFile()){
Intent i = new Intent();
i.setAction(android.content.Intent.ACTION_VIEW);
i.setDataAndType(Uri.fromFile(savedFile), "audio/*");
drumCloud.startActivity(i);
}
}
});
midiCancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
dialog.dismiss();
}
});
final TextView tv = (TextView) dialog.findViewById(R.id.midiPostSaveText);
tv.setText(drumCloud.getString(R.string.file_save_text)+"\n\n"+savedFile.getAbsolutePath());
dialog.show();
}
public static void save(File output,float bpm,boolean[][] samplesPerBeat) {
// 1. Create some MidiTracks
MidiTrack tempoTrack = new MidiTrack();
MidiTrack noteTrack = new MidiTrack();
// 2. Add events to the tracks
// 2a. Track 0 is typically the tempo map
TimeSignature ts = new TimeSignature();
ts.setTimeSignature(4, 8, TimeSignature.DEFAULT_METER, TimeSignature.DEFAULT_DIVISION);
Tempo t = new Tempo();
t.setBpm(bpm);
int gridMS=(int) Math.round(7500.0/bpm);//480;
tempoTrack.insertEvent(ts);
tempoTrack.insertEvent(t);
// 3. Create a MidiFile with the tracks we created
ArrayList<MidiTrack> tracks = new ArrayList<MidiTrack>();
tracks.add(tempoTrack);
// 2b. Track 1 will have some notes in it
for(int i = 0; i < samplesPerBeat.length; i++) {
for(int j = 0; j < samplesPerBeat[i].length; j++) {
if(samplesPerBeat[i][j]){
int channel = 9, pitch = basePitch + i, velocity = 100;
NoteOn on = new NoteOn(j*gridMS, channel, pitch, velocity);
NoteOff off = new NoteOff(j*gridMS + 120, channel, pitch, 0);
noteTrack.insertEvent(on);
noteTrack.insertEvent(off);
//There is also a utility function for notes that you should use instead of the above.
//noteTrack.insertNote(channel, pitch, velocity, j*gridMS, 120);
}
}
}
// It's best not to manually insert EndOfTrack events; MidiTrack will
// call closeTrack() on itself before writing itself to a file
tracks.add(noteTrack);
MidiFile midi = new MidiFile(MidiFile.DEFAULT_RESOLUTION, tracks);
// 4. Write the MIDI data to a file
//File output = new File("exampleout.mid");
try {
File midiFolder=new File(Environment.getExternalStorageDirectory().getPath()+"/drumcloud/midi/");
if(midiFolder.exists() && midiFolder.isDirectory()){
midi.writeToFile(output);
}else{
if(midiFolder.mkdirs())
midi.writeToFile(output);
}
} catch(IOException e) {
System.err.println(e);
}
}
public static void load(File input,Float bpm,boolean[][] samplesPerBeat) {
// 1. Open up a MIDI file
MidiFile mf = null;
try {
mf = new MidiFile(input);
} catch(IOException e) {
System.err.println("Error parsing MIDI file:");
e.printStackTrace();
return;
}
MidiTrack tempoTrack = mf.getTracks().get(0);
Iterator<MidiEvent> it = tempoTrack.getEvents().iterator();
while(it.hasNext()) {
MidiEvent E = it.next();
if(E.getClass().equals(Tempo.class)) {
Tempo tempo = (Tempo)E;
bpm=tempo.getBpm();
}
}
int gridMS=(int) Math.round(7500.0/bpm);
// 2. Do some editing to the file
// 2a. Strip out anything but notes from track 1
MidiTrack track = mf.getTracks().get(1);
// It's a bad idea to modify a set while iterating, so we'll collect
// the events first, then remove them afterwards
it = track.getEvents().iterator();
for(int i = 0; i < samplesPerBeat.length; i++) {
for(int j = 0; j < samplesPerBeat[i].length; j++) {
samplesPerBeat[i][j]=false;
}
}
while(it.hasNext()) {
MidiEvent E = it.next();
if(E.getClass().equals(NoteOn.class) && !E.getClass().equals(NoteOff.class)) {
NoteOn noteOn=(NoteOn)E;
int pos=(int) (noteOn.getTick()/gridMS);
int trackSel=noteOn.getNoteValue()-basePitch;
Log.d("MIDI LOAD","Activating ["+trackSel+"]["+pos+"]");
if(pos>=0 && trackSel>=0 && samplesPerBeat.length>trackSel && samplesPerBeat[trackSel].length>pos){
samplesPerBeat[trackSel][pos]=true;
}
}
}
}
private static File copyFileFromAssets(Context context,File file) {
AssetManager am = context.getAssets();
try {
// Create new file to copy into.
File fileDst = new File(DownloadFile.getDownloadPath()+ file.getName());
File folder = new File(DownloadFile.getDownloadPath());
if(!folder.exists())
folder.mkdirs();
fileDst.createNewFile();
//Log.d("COPY FILE","DST:"+fileDst.getAbsolutePath());
OutputStream out = new FileOutputStream(fileDst);
InputStream in = am.open(file.getName());
copyFile(in, out);
return fileDst;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
}
}