/// <summary> /// Analyzes the song data for beat information /// </summary> /// <returns>A beatmap of the song.</returns> /// <param name="data">Raw data.</param> /// <param name="clip">Audio clip to analyze.</param> /// <param name="thresholdModifier">Threshold value modifier</param> public BeatMap AnalyzeData(double[] data, AudioClip clip, float thresholdModifier) { _beatList = new BeatMap(clip.name, clip.length); int numParts = (int)clip.length; int partitionSize = (data.Length + 1) / numParts; //We only care about the magnitude of our data, so we get the absolute values before doing analysis data = data.ToList().Select(i => (double)Mathf.Abs((float)i)).ToArray(); for (int i = 0; i < data.Length - (int)(partitionSize); i += (int)(partitionSize)) { //finds the average value of the sub-partition starting at index i and of size partitionSize double avg = data.Skip(i).Take(partitionSize).Average(); //finds the highest energy sample in the current partition //int largest = i; //calculate the average energy variance in the partition double variance = 0; for (int j = 0; j < partitionSize; j++) { variance += (data[i + j] - avg) * (data[i + j] - avg); } variance /= partitionSize; //calculate the base threshold using some magic numbers and the variance double thresh = (-0.0025714 * variance) + 1.5142857; thresh *= thresholdModifier; //if the any sample is threshold percent larger than the average, then we mark it as a beat. for (int j = 0; j < partitionSize; j++) { if (data[i + j] > thresh * avg) { _beatList.AddBeat(((float)(i + j) / data.Length) * clip.length, 1f, data[i + j]); } } } //eliminate double positives (the same beat occurring in two overlapping partitions, for example) by removing beats with extremely similar timestamps for (int i = 0; i < _beatList.beats.Count - 1; i++) { if ((_beatList.beats[i + 1].timeStamp - _beatList.beats[i].timeStamp) < .1) { if (_beatList.beats[i + 1].energy > _beatList.beats[i].energy) { _beatList.beats.RemoveAt(i); } else { _beatList.beats.RemoveAt(i + 1); } i--; } } return(_beatList); }
/// <summary> /// Writes a beatmap to xml file from BPM and song length. /// </summary> /// <param name="name">Xml file name.</param> /// <param name="bpm">Beats per minute.</param> /// <param name="numBeats">Number of beats.</param> public static void BeatMapFromBPM(string name, float bpm, int numBeats) { float beatStep = 60f / bpm; float length = (float)numBeats * beatStep; BeatMap beats = new BeatMap(name, length); for (float i = 0f; i < length; i += beatStep) { beats.AddBeat(i, 1f, 1); } BeatMapSerializer.BeatMapWriter.WriteBeatMap(beats); Debug.LogFormat("Coda: Created beatmap {0} with BPM of {1} and running time of {2} seconds ({3} beats long).", name, bpm, length, numBeats); }
/// <summary> /// Analyzes the song data for beat information /// </summary> /// <returns>A beatmap of the song.</returns> /// <param name="data">Raw data.</param> /// <param name="clip">Audio clip to analyze.</param> /// <param name="thresholdModifier">Threshold value modifier</param> public BeatMap AnalyzeData(double[] data, AudioClip clip, float thresholdModifier) { _beatList = new BeatMap(clip.name, clip.length); int numParts = (int)clip.length; int partitionSize = (data.Length+1)/numParts; //We only care about the magnitude of our data, so we get the absolute values before doing analysis data = data.ToList().Select(i => (double)Mathf.Abs((float)i)).ToArray(); for(int i = 0; i < data.Length-(int)(partitionSize); i += (int)(partitionSize)) { //finds the average value of the sub-partition starting at index i and of size partitionSize double avg = data.Skip(i).Take(partitionSize).Average(); //finds the highest energy sample in the current partition //int largest = i; //calculate the average energy variance in the partition double variance = 0; for(int j = 0; j < partitionSize; j++) { variance += (data[i + j] - avg) * (data[i + j] - avg); } variance /= partitionSize; //calculate the base threshold using some magic numbers and the variance double thresh = (-0.0025714 * variance) + 1.5142857; thresh *= thresholdModifier; //if the any sample is threshold percent larger than the average, then we mark it as a beat. for (int j = 0; j < partitionSize; j++) { if (data[i+j] > thresh * avg) { _beatList.AddBeat(((float)(i + j) / data.Length) * clip.length, 1f, data[i + j]); } } } //eliminate double positives (the same beat occurring in two overlapping partitions, for example) by removing beats with extremely similar timestamps for(int i = 0; i < _beatList.beats.Count-1; i++) { if((_beatList.beats[i+1].timeStamp - _beatList.beats[i].timeStamp) < .1) { if(_beatList.beats[i + 1].energy > _beatList.beats[i].energy) { _beatList.beats.RemoveAt(i); } else { _beatList.beats.RemoveAt(i + 1); } i--; } } return _beatList; }