f@0: /*
f@0: Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
f@0:
f@0: Copyright (C) 2015 Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
f@0:
f@0: This program is free software: you can redistribute it and/or modify
f@0: it under the terms of the GNU General Public License as published by
f@0: the Free Software Foundation, either version 3 of the License, or
f@0: (at your option) any later version.
f@0:
f@0: This program is distributed in the hope that it will be useful,
f@0: but WITHOUT ANY WARRANTY; without even the implied warranty of
f@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@0: GNU General Public License for more details.
f@0:
f@0: You should have received a copy of the GNU General Public License
f@0: along with this program. If not, see .
f@0: */
f@0: package uk.ac.qmul.eecs.depic.daw;
f@0:
f@0: import uk.ac.qmul.eecs.depic.patterns.Range;
f@0:
f@1: /**
f@1: *
f@1: * A Clip is a piece of audio Sample. Clips representing different parts of the same audio sample all share
f@1: * a reference to the same Sample. This way manipulating clips doesn't involve manipulating all the samples but
f@1: * just references to them.
f@1: *
f@1: */
f@0: public class Clip extends Range implements Comparable {
f@0: private Sample sample;
f@0: private int sampleStart;
f@0: private float startMs;
f@0: private float endMs;
f@0: private float millisecPerChunk;
f@0: private float sampleStartMs;
f@0:
f@0: public Clip(int start, int end, Sample sample, int sampleStart, float millisecPerChunk) {
f@0: super(start, end);
f@0: this.sample = sample;
f@0: this.sampleStart = sampleStart;
f@0:
f@0: this.millisecPerChunk = millisecPerChunk;
f@0: updateTimeMs();
f@0: sampleStartMs = sampleStart * millisecPerChunk;
f@0: }
f@0:
f@0: /**
f@0: *
f@0: * Copy constructor
f@0: *
f@0: * @param c another instance of {@code Clip}
f@0: */
f@0: public Clip(Clip c){
f@0: this(c.start,c.end,c.sample,c.sampleStart,c.millisecPerChunk);
f@0: }
f@0:
f@0: private void updateTimeMs(){
f@0: startMs = start * millisecPerChunk;
f@0: endMs = end * (millisecPerChunk) + millisecPerChunk;
f@0: }
f@0:
f@0: /**
f@0: * Shifts the clips of the amount given as argument
f@0: * @param amount
f@0: */
f@0: public void shift(int amount){
f@0: start += amount;
f@0: end = end += amount;
f@0:
f@0: updateTimeMs();
f@0: }
f@0:
f@0: public void setStartAt(int position){
f@0: int diff = position - start;
f@0: start = position;
f@0: end = end + diff;
f@0:
f@0: updateTimeMs();
f@0: }
f@0:
f@0: public Sample getSample() {
f@0: return sample;
f@0: }
f@0:
f@0: /**
f@0: * Returns the chunks index where this selection starts relative to the start of the sample.
f@0: *
f@0: * This is different from {@code getStart()}, as {@code getStart()} returns the
f@0: * absolute position (a.k.a. chunk index) of the selection start
f@0: *
f@0: * @return
f@0: */
f@0: public int getSampleStart(){
f@0: return sampleStart;
f@0: }
f@0:
f@0: public float getSampleStartMs(){
f@0: return sampleStartMs;
f@0: }
f@0:
f@0: public float getStartTimeMs(){
f@0: return startMs;
f@0: }
f@0:
f@0: public float getEndTimeMs(){
f@0: return endMs;
f@0: }
f@0:
f@0: public float getMillisecPerChunk(){
f@0: return millisecPerChunk;
f@0: }
f@0:
f@0: /**
f@0: * Checks whether {@code p} is contained in the selection,
f@0: * that is greater than {@code getStart()} and lower than {@code getEnd()}.
f@0: *
f@0: *
f@0: * @param p the integer value to check for
f@0: * @return {@code true} if {@code pos} is contained in the selection, {@code false} otherwise
f@0: */
f@0: public boolean contains(int pos){
f@0: return ((pos>=getStart()) && (pos<=getEnd()));
f@0: }
f@0:
f@0: public boolean containsTime(float time){
f@0: return time >= startMs && time <= endMs;
f@0: }
f@0:
f@0: /**
f@0: * Splits the selection around the given position.
f@0: *
f@0: * If {@code pos} is less than the clip start or greater than the clip end, then the array will have size 1 and will
f@0: * contain this Clip. Conversely if {@code pos} is contained in this clip an array with two new instances of {@code Clip} is returned. The clip at index 0, will range
f@0: * from {@code geStart()} to {@code pos-1}, whereas the clip at index 1 will range from {@code pos} to {@code getEnd()}
f@0: *
f@0: *
f@0: * @param pos the split position
f@0: * @return an array of one or two clips, resulted from a split at position {@code pos}
f@0: */
f@0: public Clip [] split(int pos){
f@0: /* if pos can go from start+1 to end *
f@0: * if pos == start+1, the split will be [start,start] and [start+1,end] *
f@0: * if pos == end, the split will be [start,end-1] and [end,end] */
f@0: if(pos <= getStart() || pos > getEnd()){
f@0: return new Clip [] {this};
f@0: }else{
f@0: return new Clip [] {
f@0: new Clip(getStart(),pos-1,sample,sampleStart,millisecPerChunk),
f@0: new Clip(pos,getEnd(),sample,sampleStart+pos-getStart(),millisecPerChunk)
f@0: };
f@0: }
f@0: }
f@0:
f@0: @Override
f@0: public int compareTo(Clip s) {
f@0: return getStart().compareTo(s.getStart());
f@0: }
f@0:
f@0: @Override
f@0: public String toString(){
f@0: return super.toString()+ ", selection in Ms:["+startMs+","+endMs+"], sample:"+ sample+ ", sample start:" +sampleStart;
f@0: }
f@0:
f@0: public static Clip join(Clip clip1, Clip clip2){
f@0: /* nulls not joinable */
f@0: if(clip1 == null || clip2 == null){
f@0: return null;
f@0: }
f@0:
f@0: /* clips with different sample or different resolution not joinable */
f@0: if(!clip1.sample.equals(clip2.sample) || clip1.millisecPerChunk != clip2.millisecPerChunk ){
f@0: return null;
f@0: }
f@0:
f@0: /* try clip2 after clip1 */
f@0: if(clip1.end+1 == clip2.start){
f@0: int clip1Len = clip1.end - clip1.start;
f@0:
f@0: if(clip1.sampleStart + clip1Len + 1 == clip2.sampleStart){
f@0: return new Clip(
f@0: clip1.start,
f@0: clip2.end,
f@0: clip1.sample,
f@0: clip1.sampleStart,
f@0: clip1.millisecPerChunk
f@0: );
f@0: }
f@0: /* try clip1 after clip2 */
f@0: }else if(clip2.end+1 == clip1.start){
f@0: int clip2Len = clip2.end - clip2.start;
f@0:
f@0: if(clip2.sampleStart + clip2Len + 1 == clip2.sampleStart){
f@0: return new Clip(
f@0: clip2.start,
f@0: clip1.end,
f@0: clip2.sample,
f@0: clip2.sampleStart,
f@0: clip2.millisecPerChunk
f@0: );
f@0: }
f@0: }
f@0:
f@0: /* not joinable */
f@0: return null;
f@0: }
f@0:
f@0: }