/* * 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; import android.util.SparseArray; public enum NoteLength { WHOLE_DOT(4f + 2f), WHOLE(4f), HALF_DOT(2f + 1f), HALF(2f), QUARTER_DOT(1f + 1 / 2f), QUARTER(1f), EIGHT_DOT(1 / 2f + 1 / 4f), EIGHT(1 / 2f), SIXTEENTH(1 / 4f); private static final NoteLength[] SORTED_NOTE_LENGTHS = new NoteLength[] { SIXTEENTH, EIGHT, EIGHT_DOT, QUARTER, QUARTER_DOT, HALF, HALF_DOT, WHOLE, WHOLE_DOT }; private static final long DEFAULT_TICK_DURATION_MODIFIER = 8; private static final NoteLength SMALLEST_NOTE_LENGTH = SIXTEENTH; private static final int MINUTE_IN_SECONDS = 60; private static final int SECOND_IN_MILLISECONDS = 1000; private float length; private static SparseArray<long[]> millisecondsCalculationMap = new SparseArray<>(); NoteLength(float length) { this.length = length; } public static NoteLength getNoteLengthFromTickDuration(long duration, int beatsPerMinute) { NoteLength noteLength = SMALLEST_NOTE_LENGTH; NoteLength[] allNoteLengths = NoteLength.values(); for (int i = (allNoteLengths.length - 1); i >= 0; i--) { long difference = duration - allNoteLengths[i].toTicks(beatsPerMinute); if (difference < 0) { break; } noteLength = allNoteLengths[i]; } return noteLength; } public static NoteLength getNoteLengthFromMilliseconds(long millis, int beatsPerMinute) { long[] calculatedMilliseconds = getMilliseconds(beatsPerMinute); long bottomLimit = calculatedMilliseconds[0]; int bottomIndex = 0; long topLimit = calculatedMilliseconds[calculatedMilliseconds.length - 1]; int topIndex = 0; for (int i = 0; i < calculatedMilliseconds.length; i++) { long calculatedMillis = calculatedMilliseconds[i]; if (millis > calculatedMillis) { bottomLimit = calculatedMillis; bottomIndex = i; } else { topLimit = calculatedMillis; topIndex = i; break; } } long distanceBottom = Math.abs(bottomLimit - millis); long distanceTop = Math.abs(topLimit - millis); if (distanceBottom > distanceTop) { return SORTED_NOTE_LENGTHS[topIndex]; } else { return SORTED_NOTE_LENGTHS[bottomIndex]; } } public long toTicks(int beatsPerMinute) { return Math.round(beatsPerMinute * DEFAULT_TICK_DURATION_MODIFIER * length); } public long toMilliseconds(int beatsPerMinute) { return Math.round(beatsPerMinute * length * SECOND_IN_MILLISECONDS / MINUTE_IN_SECONDS); } public static long tickToMilliseconds(long tick) { return tick * SECOND_IN_MILLISECONDS / MINUTE_IN_SECONDS / DEFAULT_TICK_DURATION_MODIFIER; } public boolean hasStem() { return !((this == WHOLE) || (this == WHOLE_DOT)); } public NoteFlag getFlag() { if (this == SIXTEENTH) { return NoteFlag.DOUBLE_FLAG; } else if ((this == EIGHT) || (this == EIGHT_DOT)) { return NoteFlag.SINGLE_FLAG; } else { return NoteFlag.NO_FLAG; } } public boolean hasDot() { return (WHOLE_DOT == this) || (HALF_DOT == this) || (QUARTER_DOT == this) || (EIGHT_DOT == this); } public boolean isHalfOrHigher() { return (WHOLE_DOT == this) || (WHOLE == this) || (HALF_DOT == this) || (HALF == this); } private static long[] getMilliseconds(int beatsPerMinute) { if (null == millisecondsCalculationMap.get(beatsPerMinute)) { millisecondsCalculationMap.put(beatsPerMinute, calculateMilliseconds(beatsPerMinute)); } return millisecondsCalculationMap.get(beatsPerMinute); } private static long[] calculateMilliseconds(int beatsPerMinute) { long[] milliseconds = new long[SORTED_NOTE_LENGTHS.length]; for (int i = 0; i < milliseconds.length; i++) { NoteLength noteLength = SORTED_NOTE_LENGTHS[i]; milliseconds[i] = noteLength.toMilliseconds(beatsPerMinute); } return milliseconds; } }