public unsafe static bool Save(Song song, string filename, int sampleRate, int bitRate, int loopCount, int duration, int channelMask) { if (sampleRate < 44100) { sampleRate = 44100; Log.LogMessage(LogSeverity.Warning, $"Sample rate of {sampleRate}Hz is too low for MP3. Forcing 44100Hz."); } var project = song.Project; var player = new WavPlayer(sampleRate, loopCount, channelMask); var wavData = player.GetSongSamples(song, project.PalMode, duration); var mp3Data = new byte[wavData.Length * sizeof(short) * 4]; var mp3Size = 0; fixed(short *wavPtr = &wavData[0]) { fixed(byte *mp3Ptr = &mp3Data[0]) { mp3Size = ShineMp3Encode(sampleRate, 1, wavData.Length, new IntPtr(wavPtr), bitRate, mp3Data.Length, new IntPtr(mp3Ptr)); } } if (mp3Size <= 0) { return(false); } Array.Resize(ref mp3Data, mp3Size); File.WriteAllBytes(filename, mp3Data); return(true); }
public unsafe static void Save(Song song, string filename, int sampleRate, int loopCount, int duration, int channelMask) { var project = song.Project; var player = new WavPlayer(sampleRate, loopCount, channelMask); var samples = player.GetSongSamples(song, project.PalMode, duration); using (var file = new FileStream(filename, FileMode.Create)) { var header = new WaveHeader(); // RIFF WAVE Header header.chunkId[0] = (byte)'R'; header.chunkId[1] = (byte)'I'; header.chunkId[2] = (byte)'F'; header.chunkId[3] = (byte)'F'; header.format[0] = (byte)'W'; header.format[1] = (byte)'A'; header.format[2] = (byte)'V'; header.format[3] = (byte)'E'; // Format subchunk header.subChunk1Id[0] = (byte)'f'; header.subChunk1Id[1] = (byte)'m'; header.subChunk1Id[2] = (byte)'t'; header.subChunk1Id[3] = (byte)' '; header.audioFormat = 1; // FOR PCM header.numChannels = 1; // 1 for MONO, 2 for stereo header.sampleRate = sampleRate; // ie 44100 hertz, cd quality audio header.bitsPerSample = 16; // header.byteRate = header.sampleRate * header.numChannels * header.bitsPerSample / 8; header.blockAlign = (short)(header.numChannels * header.bitsPerSample / 8); // Data subchunk header.subChunk2Id[0] = (byte)'d'; header.subChunk2Id[1] = (byte)'a'; header.subChunk2Id[2] = (byte)'t'; header.subChunk2Id[3] = (byte)'a'; // All sizes for later: // chuckSize = 4 + (8 + subChunk1Size) + (8 + subChubk2Size) // subChunk1Size is constanst, i'm using 16 and staying with PCM // subChunk2Size = nSamples * nChannels * bitsPerSample/8 // Whenever a sample is added: // chunkSize += (nChannels * bitsPerSample/8) // subChunk2Size += (nChannels * bitsPerSample/8) header.subChunk1Size = 16; header.subChunk2Size = samples.Length * sizeof(short); header.chunkSize = 4 + (8 + header.subChunk1Size) + (8 + header.subChunk2Size); var headerBytes = new byte[sizeof(WaveHeader)]; Marshal.Copy(new IntPtr(&header), headerBytes, 0, headerBytes.Length); file.Write(headerBytes, 0, headerBytes.Length); // So lame. foreach (var s in samples) { file.Write(BitConverter.GetBytes(s), 0, sizeof(short)); } } }
public unsafe static void Save(Song song, string filename, int sampleRate, int loopCount, int duration, int channelMask) { var project = song.Project; var player = new WavPlayer(sampleRate, loopCount, channelMask); var samples = player.GetSongSamples(song, project.PalMode, duration); Save(samples, filename, sampleRate); }
public unsafe static void Save(Song song, string filename, int sampleRate, int loopCount, int duration, int channelMask, bool separateFiles, bool separateIntro, Action <short[], string> function) { var project = song.Project; var introDuration = separateIntro ? GetIntroDuration(song, sampleRate) : 0; if (separateFiles) { for (int channelIdx = 0; channelIdx < song.Channels.Length; channelIdx++) { var channelBit = 1 << channelIdx; if ((channelBit & channelMask) != 0) { var player = new WavPlayer(sampleRate, loopCount, channelBit); var samples = player.GetSongSamples(song, project.PalMode, duration); if (introDuration > 0) { var loopSamples = new short[samples.Length - introDuration]; Array.Copy(samples, introDuration, loopSamples, 0, loopSamples.Length); Array.Resize(ref samples, introDuration); var channelIntroFileName = Utils.AddFileSuffix(filename, "_" + song.Channels[channelIdx].ShortName + "_Intro"); var channelLoopFileName = Utils.AddFileSuffix(filename, "_" + song.Channels[channelIdx].ShortName); function(samples, channelIntroFileName); function(loopSamples, channelLoopFileName); } else { var channelFileName = Utils.AddFileSuffix(filename, "_" + song.Channels[channelIdx].ShortName); function(samples, channelFileName); } } } } else { var player = new WavPlayer(sampleRate, loopCount, channelMask); var samples = player.GetSongSamples(song, project.PalMode, duration); if (introDuration > 0) { var loopSamples = new short[samples.Length - introDuration]; Array.Copy(samples, introDuration, loopSamples, 0, loopSamples.Length); Array.Resize(ref samples, introDuration); var introFileName = Utils.AddFileSuffix(filename, "_Intro"); var loopFileName = filename; function(samples, introFileName); function(loopSamples, loopFileName); } else { function(samples, filename); } } }
public static int GetIntroDuration(Song song, int sampleRate) { if (song.LoopPoint > 0) { // Create a shorter version of the song. var songIndex = song.Project.Songs.IndexOf(song); var clonedProject = song.Project.DeepClone(); var clonedSong = clonedProject.Songs[songIndex]; clonedSong.SetLength(song.LoopPoint); var player = new WavPlayer(sampleRate, 1, 0x7fffffff); var samples = player.GetSongSamples(clonedSong, song.Project.PalMode, -1); return(samples.Length); } else { return(0); } }
public unsafe static void Save(Song song, string filename, int sampleRate, int loopCount, int duration, int channelMask, bool separateFiles, bool separateIntro, bool stereo, float[] pan, Action <short[], int, string> function) { var project = song.Project; var introDuration = separateIntro ? GetIntroDuration(song, sampleRate) : 0; if (channelMask == 0) { return; } if (separateFiles) { for (int channelIdx = 0; channelIdx < song.Channels.Length; channelIdx++) { var channelBit = 1 << channelIdx; if ((channelBit & channelMask) != 0) { var player = new WavPlayer(sampleRate, loopCount, channelBit, Settings.SeparateChannelsExportTndMode); var samples = player.GetSongSamples(song, project.PalMode, duration); if (introDuration > 0) { var loopSamples = new short[samples.Length - introDuration]; Array.Copy(samples, introDuration, loopSamples, 0, loopSamples.Length); Array.Resize(ref samples, introDuration); var channelIntroFileName = Utils.AddFileSuffix(filename, "_" + song.Channels[channelIdx].ShortName + "_Intro"); var channelLoopFileName = Utils.AddFileSuffix(filename, "_" + song.Channels[channelIdx].ShortName); function(samples, 1, channelIntroFileName); function(loopSamples, 1, channelLoopFileName); } else { var channelFileName = Utils.AddFileSuffix(filename, "_" + song.Channels[channelIdx].ShortName); function(samples, 1, channelFileName); } } } } else { var numChannels = 1; var samples = (short[])null; if (stereo) { // Get all the samples for all channels. var channelSamples = new short[song.Channels.Length][]; var numStereoSamples = 0; for (int channelIdx = 0; channelIdx < song.Channels.Length; channelIdx++) { var channelBit = 1 << channelIdx; if ((channelBit & channelMask) != 0) { var player = new WavPlayer(sampleRate, loopCount, channelBit, NesApu.TND_MODE_SEPARATE); channelSamples[channelIdx] = player.GetSongSamples(song, project.PalMode, duration); numStereoSamples = Math.Max(numStereoSamples, channelSamples[channelIdx].Length); } } // Mix and interleave samples. samples = new short[numStereoSamples * 2]; for (int i = 0; i < numStereoSamples; i++) { float l = 0; float r = 0; for (int j = 0; j < channelSamples.Length; j++) { if (channelSamples[j] != null) { float sl = 1.0f - Utils.Clamp(2.0f * (pan[j] - 0.5f), 0.0f, 1.0f); float sr = 1.0f - Utils.Clamp(-2.0f * (pan[j] - 0.5f), 0.0f, 1.0f); l += channelSamples[j][i] * sl; r += channelSamples[j][i] * sr; } } samples[i * 2 + 0] = (short)Utils.Clamp((int)Math.Round(l), short.MinValue, short.MaxValue); samples[i * 2 + 1] = (short)Utils.Clamp((int)Math.Round(r), short.MinValue, short.MaxValue); } numChannels = 2; introDuration *= 2; } else { var player = new WavPlayer(sampleRate, loopCount, channelMask); samples = player.GetSongSamples(song, project.PalMode, duration); } if (introDuration > 0) { var loopSamples = new short[samples.Length - introDuration]; Array.Copy(samples, introDuration, loopSamples, 0, loopSamples.Length); Array.Resize(ref samples, introDuration); var introFileName = Utils.AddFileSuffix(filename, "_Intro"); var loopFileName = filename; function(samples, numChannels, introFileName); function(loopSamples, numChannels, loopFileName); } else { function(samples, numChannels, filename); } } }