/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2016 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.catroid.pocketmusic.note.midi; import android.os.Environment; import com.leff.midi.MidiFile; import com.leff.midi.MidiTrack; import com.leff.midi.event.ChannelEvent; import com.leff.midi.event.ProgramChange; import com.leff.midi.event.meta.Tempo; import com.leff.midi.event.meta.Text; import com.leff.midi.event.meta.TimeSignature; import com.leff.midi.event.meta.TrackName; import org.catrobat.catroid.pocketmusic.note.MusicalBeat; import org.catrobat.catroid.pocketmusic.note.NoteEvent; import org.catrobat.catroid.pocketmusic.note.Project; import org.catrobat.catroid.pocketmusic.note.Track; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class ProjectToMidiConverter { public static final String MIDI_FILE_EXTENSION = ".midi"; public static final String MIDI_FILE_IDENTIFIER = "Musicdroid Midi File"; public static final File MIDI_FOLDER = new File(Environment.getExternalStorageDirectory().toString() + File.separator + "musicdroid"); private static final int MAX_CHANNEL = 16; private NoteEventToMidiEventConverter eventConverter; private int nextChannel; public ProjectToMidiConverter() { eventConverter = new NoteEventToMidiEventConverter(); nextChannel = 0; } public boolean deleteMidiByName(String name) { File file = new File(ProjectToMidiConverter.MIDI_FOLDER, name + ProjectToMidiConverter.MIDI_FILE_EXTENSION); if (file.delete()) { return true; } return false; } public void writeProjectAsMidi(Project project) throws IOException, MidiException { MidiFile midiFile = convertProject(project); checkMidiFolder(); midiFile.writeToFile(getMidiFileFromProjectName(project.getName())); } private static void checkMidiFolder() throws IOException { if (!MIDI_FOLDER.exists()) { boolean success = MIDI_FOLDER.mkdir(); if (!success) { throw new IOException("Could not create folder: " + MIDI_FOLDER); } } } public static File getMidiFileFromProjectName(String name) throws IOException { checkMidiFolder(); return new File(MIDI_FOLDER + File.separator + name + MIDI_FILE_EXTENSION); } public static String removeMidiExtensionFromString(String input) { return input.split(MIDI_FILE_EXTENSION)[0]; } public void writeProjectAsMidi(Project project, File file) throws IOException, MidiException { MidiFile midi = convertProject(project); midi.writeToFile(file); } private MidiFile convertProject(Project project) throws MidiException { for (String trackName : project.getTrackNames()) { Track track = project.getTrack(trackName); if (0 == track.size()) { throw new MidiException("Cannot save a project with an empty track!"); } } ArrayList<MidiTrack> tracks = new ArrayList<MidiTrack>(); MidiTrack tempoTrack = createTempoTrackWithMetaInfo(project.getBeat(), project.getBeatsPerMinute()); tracks.add(tempoTrack); for (String trackName : project.getTrackNames()) { Track track = project.getTrack(trackName); int channel = getNextChannel(); MidiTrack noteTrack = createNoteTrack(trackName, track, channel); tracks.add(noteTrack); } return new MidiFile(MidiFile.DEFAULT_RESOLUTION, tracks); } private int getNextChannel() throws MidiException { if (nextChannel >= MAX_CHANNEL) { throw new MidiException("You cannot have more than " + MAX_CHANNEL + " channels!"); } return nextChannel++; } private MidiTrack createTempoTrackWithMetaInfo(MusicalBeat beat, int beatsPerMinute) { MidiTrack tempoTrack = new MidiTrack(); Text text = new Text(0, 0, MIDI_FILE_IDENTIFIER); tempoTrack.insertEvent(text); Tempo tempo = new Tempo(); tempo.setBpm(beatsPerMinute); tempoTrack.insertEvent(tempo); TimeSignature timeSignature = new TimeSignature(); timeSignature.setTimeSignature(beat.getTopNumber(), beat.getBottomNumber(), TimeSignature.DEFAULT_METER, TimeSignature .DEFAULT_DIVISION); tempoTrack.insertEvent(timeSignature); return tempoTrack; } private MidiTrack createNoteTrack(String trackName, Track track, int channel) throws MidiException { MidiTrack noteTrack = new MidiTrack(); TrackName trackNameEvent = new TrackName(0, channel, trackName); noteTrack.insertEvent(trackNameEvent); ProgramChange program = new ProgramChange(0, channel, track.getInstrument().getProgram()); noteTrack.insertEvent(program); for (long tick : track.getSortedTicks()) { List<NoteEvent> noteEventList = track.getNoteEventsForTick(tick); for (NoteEvent noteEvent : noteEventList) { ChannelEvent channelEvent = eventConverter.convertNoteEvent(tick, noteEvent, channel); noteTrack.insertEvent(channelEvent); } } return noteTrack; } }