public ITask CreateExtract2dx()
        {
            return(Build("Extract 2DX", task =>
            {
                var files = GetInputFiles(task);
                if (!files.Any())
                {
                    task.Message = "No input files.";
                    return false;
                }

                ParallelProgress(task, files, file =>
                {
                    using (var stream = OpenRead(task, file))
                    {
                        var decrypted = _encryptedBeatmaniaPcAudioStreamReader.Decrypt(stream, stream.Length);
                        var sounds = _beatmaniaPcAudioStreamReader.Read(new MemoryStream(decrypted), decrypted.Length);
                        var index = 1;

                        if (EnableExportingSounds)
                        {
                            foreach (var sound in sounds)
                            {
                                var decoded = _beatmaniaPcAudioDecoder.Decode(sound);
                                var outSound = _audioDsp.ApplyEffects(decoded);
                                using (var outStream =
                                           OpenWriteMulti(task, file, i => $"{Alphabet.EncodeAlphanumeric(index, 4)}.wav"))
                                {
                                    var encoded = _riffPcm16SoundEncoder.Encode(outSound);
                                    _riffStreamWriter.Write(outStream, encoded);
                                }

                                index++;
                            }
                        }
                    }
                });

                return true;
            }));
        }
Example #2
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
                    }
                }
            });
        }