c#: Overlay mp3 file to another - c#

I have merged 3 mp3 files into one mp3 file simply using the following code
using (var fs = File.OpenWrite(Path.Combine(txtOutputFile.Text, "new.mp3")))
{
var buffer = File.ReadAllBytes(Path.Combine(txtIntroLoc.Text, fileName1));
fs.Write(buffer, 0, buffer.Length);
buffer = File.ReadAllBytes(Path.Combine(txtOutroloc.Text, fileName2));
fs.Write(buffer, 0, buffer.Length);
buffer = File.ReadAllBytes(Path.Combine(txtFileThree.Text, fileName3));
fs.Write(buffer, 0, buffer.Length);
fs.Flush();
}
What i want is to overlay another mp3 file which will play in the background of this newly created mp3 file. I know its possible but not getting right way to go to acheive this. Any help will be great.
thanks

On the question of "overlaying":
There is no way of doing this without decoding the original to PCM, mixing in your overlay, and then re-encoding the whole thing to MP3.
On your existing code:
You can just about get away with concatenating MP3 files like this. Usually though I would at recommend ditching the ID3 tags, and just making one file that has the MP3 frames from each file. If you are using NAudio, then you can use the ReadNextFrame() method on Mp3FileReader to get each MP3 frame and write its RawBytes out to the file.
For best results, you'd want all the MP3 files to use the same sample rate and channel count. Also, if these are VBR, you'll be invalidating the information in the XING or VBRI headers, so it might even be best to ditch those as well.

Finally i found a solution. Using NAudio we can mix the wav stream so first converting the mp3 to wav and then mixing the wav files and then re convert the resulted wav file to mp3 using the lame.exe.
Convert MP3 to WAV can be performed using the following piece of code using NAudio library thanks to Mark Heath.
string file = "new.mp3";
Mp3FileReader readers = new Mp3FileReader(file);
WaveFormat targetFormat = new WaveFormat();
WaveStream convertedStream = new WaveFormatConversionStream(targetFormat, readers);
WaveFileWriter.CreateWaveFile("firstwav.wav", convertedStream);
Now mixing it with another wav file can be performed using this code consuming the NAudio classes.
string[] inputFiles = new string[2];
Stream output = new MemoryStream();
inputFiles[0] = "firstwav.wav";
inputFiles[1] = "secondwav.wav";
mixWAVFiles(inputFiles);
The mixWAVFiles Method
public void mixWAVFiles(string[] inputFiles)
{
int count = inputFiles.GetLength(0);
WaveMixerStream32 mixer = new WaveMixerStream32();
WaveFileReader[] reader = new WaveFileReader[count];
WaveChannel32[] channelSteam = new WaveChannel32[count];
mixer.AutoStop = true;
for (int i = 0; i < count; i++)
{
reader[i] = new WaveFileReader(inputFiles[i]);
channelSteam[i] = new WaveChannel32(reader[i]);
mixer.AddInputStream(channelSteam[i]);
}
mixer.Position = 0;
WaveFileWriter.CreateWaveFile("mixedWavFile.wav", mixer);
}
And now finally converting the finalwav file to mp3 using lame.exe found here
public void convertWAVtoMP3(string wavfile)
{
//string lameEXE = #"C:\Users\Jibran\Desktop\MP3 Merger\bin\Debug\lame.exe";
string lameEXE = Path.GetDirectoryName(Application.ExecutablePath) +"/lame.exe";
string lameArgs = "-V2";
string wavFile = wavfile;
string mp3File = "mixed.mp3";
Process process = new Process();
process.StartInfo = new ProcessStartInfo();
process.StartInfo.FileName = lameEXE;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.Arguments = string.Format(
"{0} {1} {2}",
lameArgs,
wavFile,
mp3File);
process.Start();
process.WaitForExit();
int exitCode = process.ExitCode;
}

Related

How to play WAV file from Embedded Resources with NAudio?

I'm using C#, WPF, and NAudio to play a wav file.
I have sound_1.wav in the Resources folder and included in project. Once compiled, it exports the exe and resources to a folder and plays the wav from a path on the hard drive.
string sound1 = "Resources\\sound_1.wav";
NAudio.Wave.WaveFileReader wav = new NAudio.Wave.WaveFileReader(sound1);
WaveOutEvent output = new WaveOutEvent();
output.Init(wav);
output.Play();
But I would like to embed the wav file in the exe and use something like:
UnmanagedMemoryStream sound1 = Properties.Resources.sound_1; //embedded resource
NAudio.Wave.WaveFileReader wav = new NAudio.Wave.WaveFileReader(sound1);
How can I get that to play through WaveFileReader? It only accepts string path.
Solutions
This works, but the sound plays in slow motion and sounds like reverb.
UnmanagedMemoryStream sound1 = Properties.Resources.sound_1;
WaveIn wavin = new WaveIn();
NAudio.Wave.RawSourceWaveStream wav = new NAudio.Wave.RawSourceWaveStream(sound1, wavin.WaveFormat);
WaveOutEvent output = new WaveOutEvent();
output.Init(wav);
output.Play();
This works with loud pop at the end of sound.
Convert Stream to Byte Array
https://stackoverflow.com/a/1080445/6806643
byte[] b = ReadToEnd(sound1);
WaveStream wav = new RawSourceWaveStream(new MemoryStream(b), new WaveFormat(44100, 16, 2));
WaveOutEvent output = new WaveOutEvent();
output.Init(wav);
output.Play();
I figured out how to make it work.
The problem with this solution is high memory usage.
WAV
// embedded resource sound.wav
UnmanagedMemoryStream sound = Properties.Resources.sound;
MemoryStream ms = new MemoryStream(StreamToBytes(sound));
WaveStream ws = new WaveFileReader(ms);
WaveOutEvent output = new WaveOutEvent();
output.PlaybackStopped += new EventHandler<StoppedEventArgs>(Media_Ended);
output.Init(ws);
output.Play();
MP3
// embedded resource sound.mp3
MemoryStream sound = new MemoryStream(Properties.Resources.sound);
MemoryStream ms = new MemoryStream(StreamToBytes(sound));
WaveStream ws = new Mp3FileReader(ms);
WaveOutEvent output = new WaveOutEvent();
output.PlaybackStopped += new EventHandler<StoppedEventArgs>(Media_Ended);
output.Init(ws);
output.Play();
Stream to Byte Array
https://stackoverflow.com/a/1080445/6806643
I used this to convert MemoryStream to byte[], or else it will crash if 2 sounds play at the same time with Exception "Not a WAVE file - no RIFF header".
Dispose
Doesn't seem to have any affect on reducing RAM.
public static void Media_Ended(object sender, EventArgs e)
{
if (output.PlaybackState == PlaybackState.Stopped)
{
if (ms != null)
{
ms.Close();
ms.Flush();
}
if (ws != null)
{
ws.Close();
}
if (output != null)
{
output.Dispose();
}
}
}

How to reference Ghostscript DLL in C#

I'm using a C# wrapper to convert PDFs to images using Ghostscript, however i cant seem to reference the dll correctly.
I have the DLL stored in the bin folder (also don't know if that's the best place to keep it there or not)
Here's my code:
byte[] fileData = null;
using (var binaryReader = new BinaryReader(Request.Files[0].InputStream))
{
fileData = binaryReader.ReadBytes(Request.Files[0].ContentLength);
}
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
Ghostscript.NET.Rasterizer.GhostscriptRasterizer rasterizer = null;
Ghostscript.NET.GhostscriptVersionInfo vesion = new Ghostscript.NET.GhostscriptVersionInfo(new Version(0, 0, 0), path + #"\gsdll64.dll", string.Empty, Ghostscript.NET.GhostscriptLicense.GPL);
Stream inStream = new MemoryStream(fileData);
MemoryStream outStream = new MemoryStream();
List<Image> imageList = new List<Image>();
using (rasterizer = new Ghostscript.NET.Rasterizer.GhostscriptRasterizer())
{
rasterizer.Open(inStream, vesion, false);
for (int i = 1; i <= rasterizer.PageCount; i++)
{
//string pageFilePath = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(file) + "-p" + i.ToString() + ".jpg");
int dpi = 200;
Image img = rasterizer.GetPage(dpi, dpi, i);
img.Save(outStream, ImageFormat.Jpeg);
Image img = new Image
{
imgByteArray = outStream.ToArray()
};
imageList.Add(image);
}
rasterizer.Close();
}
I'm getting the Ghostscript native library could not be found error.
Here's the path I get
I think it has to do with the double / and 'file://' in the DLLPath string. And should I also specify the LipPath as well?
Any help??
In your case you should make ghostscript dll path this way:
string binPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string gsDllPath = Path.Combine(binPath, Environment.Is64BitProcess ? "gsdll64.dll" : "gsdll32.dll");
Even though this does not answer the question directly, I think it's worth mentioning, because I've ran into a problem using the OP's code with Ghostscript.NET version 1.2.1 published in 2016. Here's the fix I found:
In the source code,
Ghostscript.NET\Helpers\StreamHelper.cs:171
n = output.Read(buffer, 0, buffer.Length);
really should've been
n = input.Read(buffer, 0, buffer.Length);
If you're opening a stream instead of a path, without modifying the above mentioned code, you'll get empty result because the input stream did not get copied.
Reading into the code in more depth finds that the input stream is written into a temp file then read again into memory. You'll be better off just using rasterizer.Open by the path instead of stream, if it were a PDF file on disk to begin with.

Mix two or more mp3 files into one, keeping high performance

I have a problem mixing two mp3 files into one. Actually, I've found some test code to do that using NAudio library, but it is too slow for me. I am new to "audio programming" so I need your help, advice how to make an algorithm solving this problem faster than the code below.
The code mixes two files into one in 8+seconds which is much much too slow. Actually, I checked and ConvertWavToMp3() lasts 8 seconds. Files 1.mp3 and 2.mp3 are like 3+ minutes long.
static void Main(string[] args)
{
var watch = Stopwatch.StartNew();
watch.Start();
var path = #"C:\Users\gutek\Desktop\";
// read mp3 files from disk
Mp3FileReader mpbacground = new Mp3FileReader(path + "1.mp3");
Mp3FileReader mpMessage = new Mp3FileReader(path + "2.mp3");
//convert them into wave stream or decode the mp3 file
WaveStream background = WaveFormatConversionStream.CreatePcmStream(mpbacground);
WaveStream message = WaveFormatConversionStream.CreatePcmStream(mpMessage);
var mixer = new WaveMixerStream32();
mixer.AutoStop = true;
var messageOffset = background.TotalTime;
var messageOffsetted = new WaveOffsetStream(
message, TimeSpan.FromSeconds(10), TimeSpan.Zero, message.TotalTime.Subtract(TimeSpan.FromSeconds(30)));
var background32 = new WaveChannel32(background);
background32.PadWithZeroes = false;
// set the volume of background file
background32.Volume = 0.4f;
//add stream into the mixer
mixer.AddInputStream(background32);
var message32 = new WaveChannel32(messageOffsetted);
message32.PadWithZeroes = false;
// set the volume of 2nd mp3 song
message32.Volume = 0.6f;
mixer.AddInputStream(message32);
var wave32 = new Wave32To16Stream(mixer);
//encode the wave stream into mp3
var mp3Stream = ConvertWavToMp3(wave32);
// write mp3 on the disk
File.WriteAllBytes(path + "output.mp3", mp3Stream.ToArray());
watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds);
Console.ReadKey();
}
public static MemoryStream ConvertWavToMp3(Wave32To16Stream wavFile)
{
using (var retMs = new MemoryStream())
using (var wtr = new LameMP3FileWriter(retMs, wavFile.WaveFormat, 128))
{
wavFile.CopyTo(wtr);
return retMs;
}
}

Replacing CRLF in PNG file

I am trying to replace CRLF with LF (see reason # Read binary stdout data from adb shell?)
A brief summary of the above referenced thread is that basically when piping a screenshot from an Android device via the Android debug bridge, it looks like the line feeds line endings in the data stream are being replaced with carriage return line feeds, therefore I am receiving a corrupted file at the other end of the pipe. What has worked for others is undoing the replacement via code, as below, but it doesn't seem to be working from me.
My code is still spitting a corrupted file... any ideas why?
++ I know the code isn't as clean and efficient as it can be, will fix up after so please hold the comments related to my coding skill, or lack thereof.
Thanks
static void Main(string[] args)
{
// adb shell screencap -p > <path>
string path = #"<filepath>\screen.png";
var fs = new FileStream(path, FileMode.Open);
var fsw = new FileStream(path.Replace(".png", "_fixed.png"), FileMode.Create);
var buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Flush();
fs.Close();
var switched = Repair(buffer);
fsw.Write(switched, 0, switched.Length);
fsw.Flush();
fsw.Close();
Console.WriteLine(buffer.Length);
Console.WriteLine(switched.Length);
Console.Read();
}
static byte[] Repair(byte[] enc)
{
var bstr = new MemoryStream();
for (int i = 0; i < enc.Length; i++)
{
if (enc.Length > i + 1 && enc[i] == 0x0d && enc[i + 1] == 0x0a)
{
bstr.WriteByte(0x0a);
i++;
}
else bstr.WriteByte(enc[i]);
}
bstr.Flush();
bstr.Close();
return bstr.ToArray();
}

Windows Phone reading in a pdf using binary reader

(Warning: First time on Stackoverflow) I want to be able to read in a pdf via binary but I encounter an issue when writing it back to the isolated storage. When it is written back to isolated storage and I try to open the file but I get an error message from adobe reader saying this is not a valid pdf. The file is 102 Kbytes but when I write it to isolated storage it is 108 Kbytes.
My reasoning for doing this is that I want to be able to split the pdfs. I have tried PDFsharp (doesn't open all pdf types). Here is my code:
public void pdf_split()
{
string prefix = #"/PDFread;component/";
string fn = originalFile;
StreamResourceInfo sr = Application.GetResourceStream(new Uri(prefix + fn, UriKind.Relative));
IsolatedStorageFile iStorage = IsolatedStorageFile.GetUserStoreForApplication();
using (var outputStream = iStorage.OpenFile(sFile, FileMode.CreateNew))
{
Stream resourceStream = sr.Stream;
long length = resourceStream.Length;
byte[] buffer = new byte[32];
int readCount = 0;
while (readCount < length)
{
int read = sr.Stream.Read(buffer, 0, buffer.Length);
readCount += read;
outputStream.Write(buffer, 0, read);
}
}
}

Categories