/// <summary> /// Returns the fully mixed audio of the Flipnote, Including its Sound Effects. /// Returns Null if no audio exists. /// </summary> /// <param name="flip"></param> /// <param name="sampleRate"></param> /// <returns>Signed 16-bit PCM audio</returns> public byte[] GetWavBGM(PPMFile flip, int sampleRate = 32768) { // start decoding AdpcmDecoder encoder = new AdpcmDecoder(flip); var decoded = encoder.getAudioMasterPcm(sampleRate); if (decoded.Length > 0) { byte[] output = new byte[decoded.Length]; // thank you https://github.com/meemo for (int i = 0; i < decoded.Length; i += 2) { try { output[i] = (byte)(decoded[i + 1] & 0xff); output[i + 1] = (byte)(decoded[i] >> 8); } catch (Exception) { } } var a = new WavePcmFormat(output, 1, (uint)(sampleRate / 2), 16); return(a.ToBytesArray()); } return(null); }
public static PPMFile Create(PPMAuthor author, List <PPMFrame> frames, byte[] audio, bool ignoreMetadata = false) { var file = new PPMFile(); file.FrameCount = (ushort)(frames.Count - 1); file.FormatVersion = 0x24; if (!ignoreMetadata) { file.RootAuthor = author; file.ParentAuthor = author; file.CurrentAuthor = author; string mac6 = string.Join("", BitConverter.GetBytes(author.Id).Take(3).Reverse().Select(t => t.ToString("X2"))); var asm = Assembly.GetEntryAssembly().GetName().Version; var dt = DateTime.UtcNow; var fnVM = ((byte)asm.Major).ToString("X2"); var fnVm = ((byte)asm.Minor).ToString("X2"); var fnYY = (byte)(dt.Year - 2009); var fnMD = dt.Month * 32 + dt.Day; var fnTi = (((dt.Hour * 3600 + dt.Minute * 60 + dt.Second) % 4096) >> 1) + (fnMD > 255 ? 1 : 0); fnMD = (byte)fnMD; var fnYMD = (fnYY << 9) + fnMD; var H6_9 = fnYMD.ToString("X4"); var H89 = ((byte)fnMD).ToString("X2"); var HABC = fnTi.ToString("X3"); string _13str = $"80{fnVM}{fnVm}{H6_9}{HABC}"; string nEdited = 0.ToString().PadLeft(3, '0'); var filename = $"{mac6}_{_13str}_{nEdited}.ppm"; var rawfn = new byte[18]; for (int i = 0; i < 3; i++) { rawfn[i] = byte.Parse("" + mac6[2 * i] + mac6[2 * i + 1], System.Globalization.NumberStyles.HexNumber); } for (int i = 3; i < 16; i++) { rawfn[i] = (byte)_13str[i - 3]; } rawfn[16] = rawfn[17] = 0; file.ParentFilename = new PPMFilename(rawfn); file.CurrentFilename = new PPMFilename(rawfn); var ByteRootFileFragment = new byte[8]; for (int i = 0; i < 3; i++) { ByteRootFileFragment[i] = byte.Parse("" + mac6[2 * i] + mac6[2 * i + 1], System.Globalization.NumberStyles.HexNumber); } for (int i = 3; i < 8; i++) { ByteRootFileFragment[i] = (byte)((byte.Parse("" + _13str[2 * (i - 3)], System.Globalization.NumberStyles.HexNumber) << 4) + byte.Parse("" + _13str[2 * (i - 3) + 1], System.Globalization.NumberStyles.HexNumber)); } file.RootFileFragment = new PPMFileFragment(ByteRootFileFragment); file.Timestamp = new PPMTimestamp((uint)((dt - new DateTime(2000, 1, 1, 0, 0, 0)).TotalSeconds)); file.Thumbnail = new PPMThumbnail(new byte[0x600]); file.ThumbnailFrameIndex = 0; } // write the audio data uint animDataSize = (uint)(8 + 4 * frames.Count); file.AnimationFlags = 0x43; file.FrameOffsetTableSize = (ushort)(4 * frames.Count); file.Frames = new PPMFrame[frames.Count]; //SoundEffectFlags = new byte[frames.Count]; for (int i = 0; i < frames.Count; i++) { file.Frames[i] = frames[i]; animDataSize += (uint)file.Frames[i].ToByteArray().Length; } while ((animDataSize & 0x3) != 0) { animDataSize++; } file.AnimationDataSize = animDataSize; file.Audio = new PPMAudio(); file.Audio.SoundData.RawBGM = audio; file.Audio.SoundHeader.BGMTrackSize = (uint)file.Audio.SoundData.RawBGM.Length; file.Audio.SoundHeader.SE1TrackSize = 0; file.Audio.SoundHeader.SE2TrackSize = 0; file.Audio.SoundHeader.SE2TrackSize = 0; file.SoundDataSize = (uint)file.Audio.SoundData.RawBGM.Length; file.Audio.SoundHeader.CurrentFramespeed = 0; file.Audio.SoundHeader.RecordingBGMFramespeed = 0; return(file); }