/// <summary> /// Using audio's voice to generate replay frames /// Logic is copied from <see cref="Waveform"/> /// </summary> /// <param name="beatmap"></param> /// <param name="data"></param> public KaraokeAutoGeneratorBySinger(KaraokeBeatmap beatmap, Stream data) : base(beatmap) { if (data == null) { return; } readTask = Task.Run(() => { int decodeStream = 0; using (var fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(data))) { decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode | BassFlags.Float, fileCallbacks.Callbacks, fileCallbacks.Handle); } Bass.ChannelGetInfo(decodeStream, out ChannelInfo info); var totalLength = Bass.ChannelGetLength(decodeStream); double trackLength = Bass.ChannelBytes2Seconds(decodeStream, totalLength) * 1000; var length = totalLength; long lengthSum = 0; // Microphone at period 10 var bytesPerIteration = 3276 * info.Channels * TrackBass.BYTES_PER_SAMPLE; var pitches = new Dictionary <double, float?>(); var sampleBuffer = new float[bytesPerIteration / TrackBass.BYTES_PER_SAMPLE]; // Read sample data while (length > 0) { length = Bass.ChannelGetData(decodeStream, sampleBuffer, bytesPerIteration); lengthSum += length; // usually sample 1 is vocal var channel0Sample = sampleBuffer.Where((x, i) => i % 2 == 0).ToArray(); //var channel1Sample = sampleBuffer.Where((x, i) => i % 2 != 0).ToArray(); // Convert buffer to pitch data var time = lengthSum * trackLength / totalLength; var pitch = Pitch.FromYin(channel0Sample, info.Frequency, low: 40, high: 1000); pitches.Add(time, pitch == 0 ? default(float?) : pitch); } return(pitches); }, cancelSource.Token); }