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"));
        }
Example #3
0
        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;
            }));
        }
Example #4
0
        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
                    }
                }
            });
        }