/* public void saveToExternal(WinDataClass wd){ * * string dest = Application.persistentDataPath + "/winData.dat"; * FileStream file; * //string jsonStr = JsonUtility.ToJson(player); * * if(File.Exists(dest)){ * file = File.OpenWrite(dest); * }else{ * file = File.Create(dest); * } * * BinaryFormatter bf = new BinaryFormatter(); * bf.Serialize(file, wd); * file.Close(); * } */ // handles updating the player data after a win // calculating the grade, etc. public void handleGameWin() { print("handleGameWin()"); //decimal moneyEarnedDecimal = (decimal)(Math.Sqrt(_playerScore) * (_playerNotesHit/totalNotes) + _playerMaxCombo)/100; //decimal expEarnedDecimal = (decimal)(Math.Sqrt(_playerScore)) * (_playerNotesHit/totalNotes) + (_playerMaxCombo == 1 ? 0 : _playerMaxCombo); WinDataClass winData = new WinDataClass(0, 0, 0, 0, 0, 0, ""); string calculatedGrade = ""; float notePercentage = (float)((decimal)_playerNotesHit / (decimal)totalNotes); if (notePercentage < 0.5) { calculatedGrade = "D"; } else if (notePercentage >= 0.5 && notePercentage < 0.6) { calculatedGrade = "C"; } else if (notePercentage >= 0.6 && notePercentage < 0.7) { calculatedGrade = "B"; } else if (notePercentage >= 0.7 && notePercentage < 0.8) { calculatedGrade = "A"; } else if (notePercentage >= 0.8 && notePercentage < 0.9) { calculatedGrade = "S"; } else if (notePercentage >= 0.9 && notePercentage < 1) { calculatedGrade = "SS"; } else { calculatedGrade = "ACE"; } winData.score = _playerScore; winData.maxCombo = _playerMaxCombo; winData.notesHit = _playerNotesHit; winData.mapTotalNotes = totalNotes; // can calculate the percentage using this and above winData.moneyEarned = (int)Math.Round((Math.Sqrt(_playerScore) * (float)((decimal)_playerNotesHit / (decimal)totalNotes) + _playerMaxCombo) / 100.0); // round money earned to closest integer winData.expEarned = (float)Math.Round(((Math.Sqrt(_playerScore)) * (float)((decimal)_playerNotesHit / (decimal)totalNotes) + (_playerMaxCombo == 1 ? 0 : _playerMaxCombo)), 2); // round exp to 2 digits winData.grade = calculatedGrade; print("big equation thing: " + Math.Round(((Math.Sqrt(_playerScore) * (float)((decimal)_playerNotesHit / (decimal)totalNotes) + _playerMaxCombo) / 100.0), 2)); /* * print("square root: " + (float)(Math.Sqrt(_playerScore))); * print("percentage: " + (float)((_playerNotesHit/totalNotes)*1.0)); * print("winData.moneyEarned: " + winData.moneyEarned); * print("winData.expEarned: " + winData.expEarned); */ //scoreboard beatMap.addWin(winData); beatMap.save(Application.persistentDataPath); WinDataClassHelper.saveToExternal(winData); _gameState = GameState.win; //SceneManager.LoadScene("VictoryRoyaleScene"); }
// float[] getCombinedSamplesWeb (ref float[] samples) { // } //The B(eat) G(enerating) A(lgorithim) //Background thread to create the beatmap void algorithim() { Debug.Log("algo"); //float[] samples = //getCombinedSamples(ref song_info.samples, song_info.sampleCount, song_info.channels); float[] samples = song_info.samples; Debug.Log("Song samples coverted to mono"); int finalArraySize = song_info.sampleCount / settings.n_bins; Debug.Log(finalArraySize); output.fftData = new float[finalArraySize][]; output.flux = new float[finalArraySize]; output.flux2 = new float[finalArraySize]; output.peaks = new float[finalArraySize]; output.peaks2 = new float[finalArraySize]; output.threshold = new float[finalArraySize]; output.peakThreshold = new float[finalArraySize]; output.drifts = new float[finalArraySize]; output.flySectionIndex = 0; FFTProvider fftProvider = new DSPLibFFTProvider(settings.n_bins); WINDOW_TYPE fftWindow = WINDOW_TYPE.Hamming; Debug.Log("done allocating large amount of ram"); //perform fft using N_BINS at a time for (int i = 0; i < finalArraySize; i++) { float[] currSamples = new float[settings.n_bins]; int startIndex = (i * settings.n_bins); //Grab the current sample (length N_BINS) from the samples data for (int j = startIndex; j < startIndex + settings.n_bins; j++) { currSamples[j - startIndex] = samples[j]; } Debug.Log("doFFT"); output.fftData[i] = fftProvider.doFFT(currSamples, fftWindow); if (i != 0) { //Compute the spectral flux for the current spectrum float flux = 0; for (int j = 0; j < output.fftData[i].Length; j++) { float currFlux = (output.fftData[i][j] - output.fftData[i - 1][j]); //we only want 'rising' spectral flux - since we are detecting beat onset therefore we only care about rise in power if (currFlux > 0) { flux += currFlux; } } output.flux[i] = flux; } else { output.flux[0] = 0; } } //define a window size for the threshold. THRESHOLD_TIME is the length in time that the window should be. int thresholdWindowSize = Mathf.FloorToInt((settings.threshold_time / song_info.sampleLength) / settings.n_bins); Debug.Log("threshold: "); Debug.Log(thresholdWindowSize); //Compute threshold for each flux value for (int i = 0; i < output.flux.Length; i++) { float avg = 0; float count = 0; for (int j = (i - thresholdWindowSize); j < (i + thresholdWindowSize); j++) { if (j < 0 || j >= output.flux.Length) { continue; //todo should be optimized } avg += output.flux[j]; count += 1; } if (count > 0) { output.threshold[i] = (avg / count) * settings.threshold_multiplier; } else { output.threshold[i] = 0f; } } //using the computed threshold, discard any flux values that are below/at the threshold (most likely not a beat as it is below avg) for (int i = 0; i < output.flux.Length; i++) { if (output.flux[i] <= output.threshold[i]) { output.flux2[i] = 0f; } else { output.flux2[i] = output.flux[i]; //subtract avg so we see only the peak } } //Check for peaks: If curr value > next value this is a peak for (int i = 0; i < output.flux2.Length - 1; i++) { if (output.flux2[i] > output.flux2[i + 1]) { output.peaks[i] = output.flux2[i]; //Beat Detected output.totalPeaks += 1; } else { output.peaks[i] = 0f; } } //avg # of beats per second... multiply by 60 to get bpm output.totalPeaksOverTime = output.totalPeaks / song_info.length; Debug.Log("BPM: "); Debug.Log(output.totalPeaksOverTime * 60); //fly detection //define a window size for the threshold. THRESHOLD_TIME is the length in time that the window should be. for now use drift threshold time int flyThresholdWindowSize = Mathf.FloorToInt((settings.fly_threshold_time / song_info.sampleLength) / settings.n_bins) / 2; Debug.Log("threshold3: "); Debug.Log(flyThresholdWindowSize); //only look at the song between 30% and 70% duration, //and only find largest value. float largestDelta = 0; int indexLargest = 0; Debug.Log("Detect fly"); Debug.Log(Mathf.FloorToInt(.30f * output.peaks.Length)); Debug.Log(Mathf.FloorToInt(.70f * output.peaks.Length)); for (int i = Mathf.FloorToInt(.30f * output.peaks.Length); i < Mathf.FloorToInt(.70f * output.peaks.Length); i++) { float avgL = 0; float avgR = 0; float countL = 0; float countR = 0; for (int j = (i - flyThresholdWindowSize); j < (i + flyThresholdWindowSize); j++) { if (j < 0 || j >= output.peaks.Length) { continue; //todo should be optimized } if (j < i) { avgL += output.peaks[j]; countL += 1; } else { avgR += output.peaks[j]; countR += 1; } } if (countL > 0 && countR > 0) { float avg = (avgL / countL) - (avgR / countR); //we are looking for the biggest difference from left to right if (avg > largestDelta) { largestDelta = avg; indexLargest = i; } } } output.flySectionIndex = indexLargest; Debug.Log("Fly section: "); Debug.Log(output.flySectionIndex); //end fly detection //drift detection //define a window size for the threshold. THRESHOLD_TIME is the length in time that the window should be. int driftThresholdWindowSize = Mathf.FloorToInt((settings.drift_threshold_time / song_info.sampleLength) / settings.n_bins) / 2; Debug.Log("threshold2: "); Debug.Log(driftThresholdWindowSize); //Compute a threshold on avg # of peaks at a given time for (int i = 0; i < output.peaks.Length; i++) { float avg = 0; float count = 0; for (int j = (i - driftThresholdWindowSize); j < (i + driftThresholdWindowSize); j++) { if (j < 0 || j >= output.peaks.Length) { continue; //todo should be optimized } avg += output.peaks[j]; count += 1; } if (count > 0) { output.peakThreshold[i] = (avg / count); } else { output.peakThreshold[i] = 0f; } } //select amount_drift_sections from output.peakThreshold, ensuring that each drift section is min_time_between_drift apart //remove all duplicates / close values next to a value, then store remaning values as a GreatestValueElement in a arraylist, then sort the list. List <GreatestValueElement> greatestValues = new List <GreatestValueElement>(); Debug.Log("Start greatestvalues"); int offset = 1; for (int i = 0; i < output.peakThreshold.Length; i += offset) { offset = 1; if ((i - driftThresholdWindowSize) <= 0 || (i + driftThresholdWindowSize) >= output.peakThreshold.Length) //throw out values near beginning and near end { continue; } while (((i + offset) < output.peakThreshold.Length) && Math.Abs(output.peakThreshold[i] - output.peakThreshold[i + offset]) <= delta) { output.peakThreshold[i + offset] = 0; offset += 1; } GreatestValueElement elem = new GreatestValueElement(i, output.peakThreshold[i]); greatestValues.Add(elem); } Debug.Log("Sort greatestvalues"); //todo debug this... try { greatestValues.Sort((x, y) => y.value.CompareTo(x.value)); } catch (Exception e) { Debug.Log("Could not sort greatestValues!"); Debug.Log(e.ToString()); } Debug.Log("Song length:"); Debug.Log(song_info.length); int minLengthBetweenDrift = Mathf.FloorToInt((settings.min_drift_seperation_time / song_info.sampleLength) / settings.n_bins); int totalAllowableDrifts = Mathf.FloorToInt(settings.drifts_per_minute * Mathf.FloorToInt(song_info.length / 60f)); int warmUpTimeLength = Mathf.FloorToInt((settings.warm_up_time / song_info.sampleLength) / settings.n_bins); Debug.Log("drifts per minute:"); Debug.Log(settings.drifts_per_minute); Debug.Log("Drift sections: "); Debug.Log(totalAllowableDrifts); Debug.Log("Min length between drifts: "); Debug.Log(minLengthBetweenDrift); //select elements and make sure they are min_time_between_drift apart int numSelected = 0; List <GreatestValueElement> addedValues = new List <GreatestValueElement>(); bool first = true; foreach (GreatestValueElement elem in greatestValues) { if (numSelected >= totalAllowableDrifts) { break; } if (first) { int offSetIndex = Math.Max(elem.index - driftThresholdWindowSize, 0); if (offSetIndex == 0) { continue; } if (offSetIndex - warmUpTimeLength <= 0) { continue; } output.drifts[offSetIndex] = elem.value; numSelected = 1; first = false; addedValues.Add(elem); } else { bool flag = false; //We need to check if this elem is properly spaced at least min_time_between_drift apart from other drifts //And that the elem is not in the middle of a <fly> section or <time_after_fly> section foreach (GreatestValueElement added in addedValues) { if (Math.Abs(elem.index - added.index) <= minLengthBetweenDrift || Math.Abs(elem.index - output.flySectionIndex) <= (flyThresholdWindowSize * 2.2)) { flag = true; break; } } if (!flag) { int offSetIndex = Math.Max(elem.index - driftThresholdWindowSize, 0); if (offSetIndex == 0) { continue; } if (offSetIndex - warmUpTimeLength <= 0) { continue; } output.drifts[offSetIndex] = elem.value; numSelected += 1; addedValues.Add(elem); } } } //end drift detection //Filter peaks to allowable min_time_between_peaks //Todo this is a bit naive should probably select highest peak or something (but will work for now) //int minLengthBetweenPeaks = Mathf.FloorToInt(Mathf.FloorToInt((settings.min_peak_seperation_time / sampleLength) / settings.n_bins) / 2); if (settings.min_peak_seperation_time > 0) { int minLengthBetweenPeaks = Mathf.FloorToInt((settings.min_peak_seperation_time / song_info.sampleLength) / settings.n_bins); for (int i = 0; i < output.peaks.Length; i++) { if (output.peaks[i] > 0) { output.peaks2[i] = output.peaks[i]; i += minLengthBetweenPeaks - 1; } } } else { for (int i = 0; i < output.peaks.Length; i++) { output.peaks2[i] = output.peaks[i]; } } BeatMap beatMap = makeBeatMap(driftThresholdWindowSize * 2, flyThresholdWindowSize * 2); beatMap.save(persistentDataPath); //todo deal with random stuff below //debug output Debug.Log("BGA Done"); this.state = STATE.DONE; // using (StreamWriter file = new StreamWriter("output.txt")) // { // for (int i=0; i < output.flux.Length; i++) // { // file.WriteLine(output.flux[i]); // } // } // using (StreamWriter file = new StreamWriter("output2.txt")) // { // for (int i = 0; i < output.threshold.Length; i++) // { // file.WriteLine(output.threshold[i]); // } // } // using (StreamWriter file = new StreamWriter("output3.txt")) // { // for (int i = 0; i < output.flux2.Length; i++) // { // file.WriteLine(output.flux2[i]); // } // } // using (StreamWriter file = new StreamWriter("output4.txt")) // { // for (int i = 0; i < output.peaks.Length; i++) // { // file.WriteLine(output.peaks[i]); // } // } // using (StreamWriter file = new StreamWriter("output5.txt")) // { // for (int i = 0; i < output.peaks2.Length; i++) // { // file.WriteLine(output.peaks2[i]); // } // } // using (StreamWriter file = new StreamWriter("output6.txt")) // { // for (int i = 0; i < output.peakThreshold.Length; i++) // { // file.WriteLine(output.peakThreshold[i]); // } // } // using (StreamWriter file = new StreamWriter("output7.txt")) // { // for (int i = 0; i < output.drifts.Length; i++) // { // file.WriteLine(output.drifts[i]); // } // } // using (StreamWriter file = new StreamWriter("output8.txt")) // { // Queue<LaneObject> laneObjects = beatMap.initLaneObjectQueue(); // foreach (LaneObject l in laneObjects) { // file.WriteLine(l.sampleIndex + " " + l.lane + " " + l.time + " " + (l.type == LANE_OBJECT_TYPE.Beat ? "1" : "0")); // } // } // Debug.Log("Output file saved"); //frameScale = (int) (songLength / finalArraySize); //float sampleLength = song_info.length / (float)song_info.sampleCount; //Debug.Log(sampleLength); //Debug.Log(sampleLength * 1000); //Debug.Log(sampleLength * N_BINS); //length per reading //done = true; }