public void Test4() { var streamer = Resolve <ITwinkleBeatmaniaStreamReader>(); var decoder = Resolve <ITwinkleBeatmaniaDecoder>(); var renderer = Resolve <IChartRenderer>(); var dsp = Resolve <IAudioDsp>(); var options = new ChartRendererOptions(); using (var stream = File.OpenRead(@"Z:\Bemani\Beatmania Non-PC\iidx7th.zip")) using (var zipStream = new ZipArchive(stream, ZipArchiveMode.Read)) { var entry = zipStream.Entries.Single(); using (var entryStream = entry.Open()) { var chunks = streamer.Read(entryStream, entry.Length, true); foreach (var chunk in chunks.AsParallel()) { var archive = decoder.Decode(chunk); if (archive == null) { continue; } foreach (var chart in archive.Charts.AsParallel()) { var rendered = dsp.Normalize(renderer.Render(chart.Events, archive.Samples, options), 1.0f, false); this.WriteSound(rendered, Path.Combine($"twinkle7\\{chunk.Index:D4}_{(int) chart[NumericData.Id]:D2}.wav")); } } } } }
public void Test3() { // Arrange. var data = GetArchiveResource("Twinkle.8th.zip") .First() .Value; var streamer = Resolve <ITwinkleBeatmaniaStreamReader>(); var decoder = Resolve <ITwinkleBeatmaniaDecoder>(); var renderer = Resolve <IChartRenderer>(); var dsp = Resolve <IAudioDsp>(); var options = new ChartRendererOptions(); // Act. var chunk = streamer.Read(new MemoryStream(data), data.Length, false).First(); var archive = decoder.Decode(chunk); var rendered = dsp.Normalize(renderer.Render(archive.Charts[1].Events, archive.Samples, options), 1.0f, true); // Assert. this.WriteSound(rendered, Path.Combine($"twinkle.wav")); }
public ITask CreateRenderBms() { return(Build("BMS to WAV", task => { var files = GetInputFiles(task); if (!files.Any()) { task.Message = "No input files."; return false; } var options = new ChartRendererOptions { UseSourceDataForSamples = true }; ParallelProgress(task, files, file => { using (var stream = OpenRead(task, file)) { var accessor = new FileAccessor(Path.GetDirectoryName(file.Name)); var commands = _bmsStreamReader.Read(stream); var resolved = _bmsRandomResolver.Resolve(commands); var decoded = _bmsDecoder.Decode(resolved); decoded.Chart.PopulateLinearOffsets(); var sounds = _bmsSoundLoader.Load(decoded.SoundMap, accessor); var rendered = _chartRenderer.Render(decoded.Chart.Events, sounds, options); var normalized = _audioDsp.Normalize(rendered, 1.0f, true); using (var outFile = OpenWriteSingle(task, file, i => $"{i}.render.wav")) { var riff = _riffPcm16SoundEncoder.Encode(normalized); _riffStreamWriter.Write(outFile, riff); outFile.Flush(); } } }); return true; })); }
public ISound Render(IEnumerable <IEvent> inEvents, IEnumerable <ISound> inSounds, ChartRendererOptions options) { var state = new List <ChannelState>(); var sampleMap = new List <SampleMapping>(); var masterVolume = (float)(options.Volume ?? BigRational.One); var events = inEvents.AsList(); if (events.Any(ev => ev[NumericData.LinearOffset] == null)) { throw new RhythmCodexException("Can't render without all events having linear offsets."); } var sounds = inSounds .Select(s => _audioDsp.ApplyResampling(_audioDsp.ApplyEffects(s), _resamplerProvider.GetBest(), options.SampleRate)) .Where(s => s != null) .ToArray(); void MapSample(BigRational?player, BigRational?column, BigRational?soundIndex) { var sample = sampleMap.FirstOrDefault(st => st.Player == player && st.Column == column); if (sample == null) { sample = new SampleMapping(); sampleMap.Add(sample); } sample.Player = player; sample.Column = column; sample.SoundIndex = soundIndex; } void StartUserSample(BigRational?player, BigRational?column) { var sample = sampleMap.FirstOrDefault(sm => sm.Player == player && sm.Column == column); ISound sound = null; if (sample?.SoundIndex != null) { sound = sounds.FirstOrDefault(s => s[NumericData.Id] == sample.SoundIndex); } var v = (float)Math.Sqrt(0.5f); var channel = sound?[NumericData.Channel]; ChannelState st = null; if (channel != null && channel >= 0 && channel < 255) { st = state.FirstOrDefault(s => s.Channel == channel); } if (st == null) { st = new ChannelState { Channel = channel }; state.Add(st); } st.Sound = sound; st.Offset = 0; st.LeftLength = sound?.Samples[0].Data.Count ?? 0; st.RightLength = sound?.Samples[1].Data.Count ?? 0; st.LeftVolume = v * masterVolume; st.RightVolume = v * masterVolume; st.Playing = true; } void StartBgmSample(BigRational?soundIndex, BigRational?panning = null) { var sound = sounds.FirstOrDefault(s => s[NumericData.Id] == soundIndex); var channel = sound?[NumericData.Channel]; var rightVolume = (float)Math.Sqrt((float)(panning ?? BigRational.OneHalf)); var leftVolume = (float)Math.Sqrt(1f - (float)(panning ?? BigRational.OneHalf)); ChannelState st = null; if (channel != null && channel >= 0 && channel < 255) { st = state.FirstOrDefault(s => s.Channel == channel); } if (st == null) { st = new ChannelState { Channel = channel }; state.Add(st); } st.Sound = sound; st.Offset = 0; st.LeftLength = sound?.Samples[0].Data.Count ?? 0; st.RightLength = sound?.Samples[1].Data.Count ?? 0; st.LeftVolume = leftVolume * masterVolume; st.RightVolume = rightVolume * masterVolume; st.Playing = true; } var mixdownLeft = new List <float>(); var mixdownRight = new List <float>(); void Mix() { var finalMixLeft = 0f; var finalMixRight = 0f; foreach (var ch in state) { if (!ch.Playing) { continue; } if (ch.Sound == null || (ch.Offset >= ch.LeftLength && ch.Offset >= ch.RightLength)) { ch.Playing = false; continue; } if (ch.Offset < ch.LeftLength) { finalMixLeft += ch.Sound.Samples[0].Data[ch.Offset] * ch.LeftVolume; } if (ch.Offset < ch.RightLength) { finalMixRight += ch.Sound.Samples[1].Data[ch.Offset] * ch.RightVolume; } ch.Offset++; } mixdownLeft.Add(finalMixLeft); mixdownRight.Add(finalMixRight); } var lastSample = 0; var eventTicks = events.GroupBy(ev => ev[NumericData.LinearOffset]).OrderBy(g => g.Key).ToList(); foreach (var tick in eventTicks) { var nowSample = (int)(tick.Key.Value * options.SampleRate); var tickEvents = tick.ToArray(); for (; lastSample < nowSample; lastSample++) { Mix(); } foreach (var ev in tickEvents.Where(t => t[NumericData.LoadSound] != null)) { var column = ev[NumericData.Column] ?? BigRational.Zero; if (ev[FlagData.Scratch] == true || ev[FlagData.FreeZone] == true) { column += 1000; } MapSample(ev[NumericData.Player], column, ev[NumericData.LoadSound]); } foreach (var ev in tickEvents.Where(t => t[FlagData.Note] != null)) { var column = ev[NumericData.Column] ?? BigRational.Zero; if (ev[FlagData.Scratch] == true || ev[FlagData.FreeZone] == true) { column += 1000; } if (options.UseSourceDataForSamples && ev[NumericData.SourceData] != null) { MapSample(ev[NumericData.Player], column, ev[NumericData.SourceData]); } StartUserSample(ev[NumericData.Player], column); } foreach (var ev in tick.Where(t => t[NumericData.PlaySound] != null)) { StartBgmSample(ev[NumericData.PlaySound], ev[NumericData.Panning]); } state.RemoveAll(s => !s.Playing); } while (state.Any(s => s.Playing)) { Mix(); } return(new Sound { [NumericData.Rate] = options.SampleRate, Samples = new List <ISample> { new Sample { Data = mixdownLeft }, new Sample { Data = mixdownRight } } }); }