Is there any audio/programming-related stack-exchange site?
I'm trying to make a wave form in WinForms
What algorithm should I use?
For example, if I have 200 samples per pixel (vertical line), should I draw the lowest and the highest sample from that portion of 200 samples? Or should I draw average of low and high samples? Maybe both in different colors?
This will help you to generate waveform from audio file using nAudio in C#...
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string strPath = Server.MapPath("audio/060.mp3");
string SongID = "2";
byte[] bytes = File.ReadAllBytes(strPath);
WriteToFile(SongID,strPath, bytes);
Response.Redirect("Main.aspx");
}
private void WriteToFile(string SongID, string strPath, byte[] Buffer)
{
try
{
int samplesPerPixel = 128;
long startPosition = 0;
//FileStream newFile = new FileStream(GeneralUtils.Get_SongFilePath() + "/" + strPath, FileMode.Create);
float[] data = FloatArrayFromByteArray(Buffer);
Bitmap bmp = new Bitmap(1170, 200);
int BORDER_WIDTH = 5;
int width = bmp.Width - (2 * BORDER_WIDTH);
int height = bmp.Height - (2 * BORDER_WIDTH);
NAudio.Wave.Mp3FileReader reader = new NAudio.Wave.Mp3FileReader(strPath, wf => new NAudio.FileFormats.Mp3.DmoMp3FrameDecompressor(wf));
NAudio.Wave.WaveChannel32 channelStream = new NAudio.Wave.WaveChannel32(reader);
int bytesPerSample = (reader.WaveFormat.BitsPerSample / 8) * channelStream.WaveFormat.Channels;
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.White);
Pen pen1 = new Pen(Color.Gray);
int size = data.Length;
string hexValue1 = "#009adf";
Color colour1 = System.Drawing.ColorTranslator.FromHtml(hexValue1);
pen1.Color = colour1;
Stream wavestream = new NAudio.Wave.Mp3FileReader(strPath, wf => new NAudio.FileFormats.Mp3.DmoMp3FrameDecompressor(wf));
wavestream.Position = 0;
int bytesRead1;
byte[] waveData1 = new byte[samplesPerPixel * bytesPerSample];
wavestream.Position = startPosition + (width * bytesPerSample * samplesPerPixel);
for (float x = 0; x < width; x++)
{
short low = 0;
short high = 0;
bytesRead1 = wavestream.Read(waveData1, 0, samplesPerPixel * bytesPerSample);
if (bytesRead1 == 0)
break;
for (int n = 0; n < bytesRead1; n += 2)
{
short sample = BitConverter.ToInt16(waveData1, n);
if (sample < low) low = sample;
if (sample > high) high = sample;
}
float lowPercent = ((((float)low) - short.MinValue) / ushort.MaxValue);
float highPercent = ((((float)high) - short.MinValue) / ushort.MaxValue);
float lowValue = height * lowPercent;
float highValue = height * highPercent;
g.DrawLine(pen1, x, lowValue, x, highValue);
}
}
string filename = Server.MapPath("image/060.png");
bmp.Save(filename);
bmp.Dispose();
}
catch (Exception e)
{
}
}
public float[] FloatArrayFromStream(System.IO.MemoryStream stream)
{
return FloatArrayFromByteArray(stream.GetBuffer());
}
public float[] FloatArrayFromByteArray(byte[] input)
{
float[] output = new float[input.Length / 4];
for (int i = 0; i < output.Length; i++)
{
output[i] = BitConverter.ToSingle(input, i * 4);
}
return output;
}
}
Try dsp.stackexchange.com
At 200 samples per pixel, there are several approaches you can try. Whatever you do, it often works best to draw each vertical line both above and below 0, ie. treat positive and negative sample values seperately. Probably the easiest is to just calculate an RMS. At such a low resolution peak values will probably give you a misleading representation of the waveform.
You can use AudioControl from code project.
and see this one: Generating various audio waveforms in C#
these projects may be useful for you if implement your code originally:
High-Speed-Feature-Rich-and-Easy-To-Use-Graphs
and this
Incase anyone runs into this:
You can treat the samples per pixel as your zoom level, at higher levels (zoomed out more) you will probably want to subsample that for performance reasons.
You will most likely want a fixed width that fits on the screen to draw on and use virtual scrolling (so you don't potentially have a draw area of several million pixels).
You can calculate the value for each pixel by iterating over the audio data with: skip (scroll position * samples per pixel) + (pixel * samples per pixel) take samples per pixel.
This allows for performant infinite zoom and scroll as you only read and draw the minimum amount to fill the view.
The scroll width is calculated with audio data length / samples per pixel.
Audio samples are generally shown in one of two ways, the peak value of the sample range or the rms value. The rms value is calculated by summing the squares of all values in the sample range, divide the sum by sample length, the rms value if the squareroot of this (rms will be a bit higher than average and is a good measure of perceived loudness)
You can increase performance in multiple ways such as increasing sub sampling (causes loss of detail), throttling the scroll and making the draw requests cancelable incase new scroll fires before previous is rendered.
just to document it, if you want to make the audio file fill the width of the output image
samplesPerPixel = (reader.Length / bytesPerSample) / width ;
Related
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Util;
using Emgu.CV.Structure;
namespace DCTTransform
{
public partial class Form1 : Form
{
Image<Bgr, Byte> img;
Image<Ycc, Byte> imgYcc;
Bitmap bmp;
public Form1()
{
InitializeComponent();
}
private void btBrowse_Click(object sender, EventArgs e)
{
OpenFileDialog od = new OpenFileDialog();
if (od.ShowDialog() == DialogResult.OK)
{
img = new Image<Bgr, byte>(od.FileName);
pcCitra.Image = img.ToBitmap();
label1.Text = img.Width.ToString();
}
}
//Split citra
public List<Image> subBlok(Image img, int blokX, int blokY)
{
List<Image> res = new List<Image>();
int pembagiLebar = img.Width / blokX;
int pembagiTinggi = img.Height / blokY;
for (int i = 0; i < blokX; i++)//baris
{
for (int j = 0; j < blokY; j++)//kolom
{
Bitmap bmp = new Bitmap(img.Width / pembagiLebar, img.Height / pembagiTinggi);
//Bitmap bmp = new Bitmap(img.Width, img.Height);
Graphics grp = Graphics.FromImage(bmp);
grp.DrawImage(img, new Rectangle(0,0 , bmp.Width, bmp.Height), new Rectangle(j * bmp.Width, i * bmp.Height, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
res.Add(bmp);
}
}
return res;
}
private void btTransform_Click(object sender, EventArgs e)
{
//SplitImage
List<Image> imgs = subBlok(pcCitra.Image, 8, 8);
pbDCT.Image = imgs[0];
}
}
}
I have made this code to divide an image into 8 x 8 pixels, but the result shows just 1 block (8 x 8) in the upper left corner.
All I want is like this:
|xxxx|xxxx|xxxx|xxxx|.......... until the same width to original image.
Can you help me with this?
If you want to divide your source image into multiple 8x8 tiles, then your transform function's loops are incorrect. If you want to divide your source image into 64 tiles then they are correct, but DCT doesn't usually work with a static set of 64 images. I am unclear on which one you want, but I fixed it up for multiple 8x8 images here:
List<Image> res = new List<Image>();
int pembagiLebar = (int)Math.Ceil((float)img.Width / (float)blokX);
int pembagiTinggi = (int)Math.Ceil((float)img.Height / (float)blokY);
for (int i = 0; i < pembagiLebar ; i++)//baris
{
for (int j = 0; j < pembagiTinggi ; j++)//kolom
{
Bitmap bmp = new Bitmap(blokX, blokY);
using (Graphics grp = Graphics.FromImage(bmp)) {
grp.DrawImage(img, 0, 0, new Rectangle(i * blokX, j * blokY, blokX, blokY), GraphicsUnit.Pixel);
}
res.Add(bmp);
}
}
return res;
The boundary condition on the right and bottom edge is also annoying to take care of (which I have not here), since the source image dimensions may not be multiples of 8.
There are a couple of approach you can do. The easiest way for processing is to store your image values in rows with lengths that are multiples of 8. Pad the data out. Do the same for then do the same for the number of rows.
Then the blocks are (assuming grayscale)
X = starting point
v0 = X
v1 = X + 1
v2 = X + 2
. . .
v8 = X + row length
v9 = X + row length + 1
v10 = X + row length + 2
. . .
v63 = x = 7 * row length + 7
To move to the next block in the row X = X + 8. When you reach the end of the row
X = X + 8 * row length
Keep in mind that your output elements need to be twice the size of the input. If your image data is 8 bits, your DCT output will need to be 16 bits for an integer representation (or a floating point value).
If you cannot pad the data, your best bet is to copy the values to a 64 entry array. You'd do a loop similar to the above but, any time you would need to access a value beyond the width or height of the image, insert a 0 into the temporary array.
I'm trying to merge multiple Images into one image. Problem is that most libraries with such functionality are not available in a Windows 8.1 App. I'd prefer to not have to use external libraries such as WriteableBitmapEx
This is my current code which unfortunately doesn't work:
int count = 4;
int size = 150;
WriteableBitmap destination = new WriteableBitmap(300, 300);
BitmapFrame frame = await (await BitmapDecoder.CreateAsync(randomAccessStream)).GetFrameAsync(0);
PixelDataProvider pixelData = await frame.GetPixelDataAsync();
byte[] test = pixelData.DetachPixelData();
MemoryStream mem = new MemoryStream();
for (int row = 0; row < frame.PixelHeight; row++) {
for (int i = 0; i < count; i++)
{
mem.Write(test, row * (int)frame.PixelWidth * 4, (int)frame.PixelWidth * 4);
}
}
mem.Seek(0, SeekOrigin.Begin);
BitmapImage bmp = new BitmapImage();
bmp.SetSourceAsync(mem.AsRandomAccessStream());
If I set the bmp as the source of an Image UIElement nothing happens.
My Idea was to get the Pixeldata as a byte array and to write it line by line (pixel row of each image, so they'd be next to each other) to a memory stream which is then used as the source of the BitmapImage.
Solved
Thanks to Aditya and Romasz I could solve this.
The problem was that I had to encode the pixel data back to an image.
If anyone has the same Problem the following class merges the pixel data of multiple images and returns a BitmapImage:
public class ImageMerger
{
public static async Task<BitmapImage> MergeImages(int singleWidth, int singleHeight, params byte[][] pixelData)
{
int perRow = (int) Math.Ceiling(Math.Sqrt(pixelData.Length));
byte[] mergedImageBytes = new byte[singleHeight * singleWidth * perRow * perRow * 4];
for (int i = 0; i < pixelData.Length; i++ )
{
LoadPixelBytesAt(ref mergedImageBytes, pixelData[i], (i % perRow) * singleWidth, (i / perRow) * singleHeight, perRow * singleWidth, singleWidth, singleHeight);
}
InMemoryRandomAccessStream mem = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, mem);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)(singleHeight * perRow), (uint)(singleWidth * perRow), 91, 91, mergedImageBytes);
await encoder.FlushAsync();
BitmapImage bmp = new BitmapImage();
bmp.SetSourceAsync(mem);
return bmp;
}
private static void LoadPixelBytesAt(ref byte[] dest, byte[] src, int destX, int destY, int destW, int srcW, int srcH)
{
for (int i = 0; i < srcH; i++)
{
for (int j = 0; j < srcW; j++)
{
if (src.Length < ((i * srcW + j + 1) * 4)) return;
for (int p = 0; p < 4; p++)
dest[((destY + i) * destW + destX + j) * 4 + p] = src[(i * srcW + j) * 4 + p];
}
}
}
}
This takes any number of images and puts them next to each other with around as many images from left to right as from top to bottom.
I.e. for 4 images it would return an image with them aligned like this:
1 2
3 4
Works for all of my images but one. There is one image that looks pretty weird after getting merged with others. Didn't figure out why yet.
This should do it :
byte[] PutOnCanvas(byte[] Canvas,byte[] Image,uint x,uint y,uint imageheight,uint imagewidth,uint CanvasWidth)
{
for (uint row = y; row < y+imageheight; row++)
for (uint col = x; col < x+imagewidth; col++)
for (int i = 0; i < 4; i++)
Canvas[(row * CanvasWidth + col) * 4 + i] = Image[((row-y) * imagewidth + (col - x)) * 4 + i];
return Canvas;
}
Now say I want to put two images (pixelbytes in Image1 and Image2) of 30x30 side by side and have a vertical margin of 10px in between them. I would call the function in the following way:
byte[] Canvas = new byte[30 * 70 * 4];
Canvas=PutOnCanvas(Canvas,Image1,0,0,30,30,70);
Canvas=PutOnCanvas(Canvas,Image2,40,0,30,30,70);
Then convert pixel bytes to BMP and you should be done!
Edit:
And this is the correct way to convert pixel bytes to image:
memStream.Size = 0;
var encoder = await BitmapEncoder.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId, memStream);
encoder.SetPixelData(
Windows.Graphics.Imaging.BitmapPixelFormat.Bgra8,
Windows.Graphics.Imaging.BitmapAlphaMode.Straight,
CanvasWidth, // pixel width
CanvasHeight, // pixel height
96, // horizontal DPI
96, // vertical DPI
PixelData);
try { await encoder.FlushAsync(); }
catch { }
memStream.Dispose();
Tried this method awhile ago and it worked for me.
http://www.codeproject.com/Articles/502249/Combineplusseveralplusimagesplustoplusformplusaplu
One option is to draw them in a Canvas like you normally would and then render that Canvas out. The only problem with this is that they must all be on the screen at the same time.
Unfortunately, that's about it as far as simple solutions without something like WriteableBitmapEx goes. Their BitmapContext class abstracts away a lot of the more complex math that goes on when changing an image's width. You can check out WinRTXamlToolkit's blit implementation here, but it has the limitation that the source and destination files must be the same width (due to the annoying math).
One option may be to try and up the size of the images without scaling, hopefully creating some whitespace in the proper spot, then layering them together using a facsimile of that blit implementation, but this seems like it will be a lot of trouble as well.
Your best bet, IMO, is to cut out the chunks of WriteableBitmapEx that you need, specifically their BitmapContext and the Blit Extensions that they provide, then create a blank image and overlay each image onto the destination image (as you are attempting to do now).
This is not legal advice.
WriteableBitmapEx is Microsoft License, which is very permissive, so you should be okay to do this.
Anyway, it'd likely be easier to just add the reference, but if it's necessary that you don't, you can still cut out the parts that you need (in this case) and use them 'a la carte'.
I'm trying to rotate raw pixel data from a DICOM file by 180 degrees (or flipped). I've successfully flipped the image correctly, however, upon writing the pixel data back to the file (in this case it's a DICOM file) and displaying it. The final output of the image is not correct.
Below is the sample a sample of the image I'm trying to flip 180 /mirror.
Here's the code I'm using to perform the flipping:
string file = #"adicomfile.dcm";
DicomFile df = new DicomFile();
df.Load(file);
// Get the amount of bits per pixel from the DICOM header.
int bitsPerPixel = df.DataSet[DicomTags.BitsAllocated].GetInt32(0, 0);
// Get the raw pixel data from the DICOM file.
byte[] bytes = df.DataSet[DicomTags.PixelData].Values as byte[];
// Get the width and height of the image.
int width = df.DataSet[DicomTags.Columns].GetInt32(0, 0);
int height = df.DataSet[DicomTags.Rows].GetInt32(0, 0);
byte[] original = bytes;
byte[] mirroredPixels = new byte[width * height * (bitsPerPixel / 8)];
width *= (bitsPerPixel / 8);
// The mirroring / image flipping.
for (int i = 0; i < original.Length; i++)
{
int mod = i % width;
int x = ((width - mod - 1) + i) - mod;
mirroredPixels[i] = original[x];
}
df.DataSet[DicomTags.PixelData].Values = mirroredPixels;
df.Save(#"flippedicom.dcm", DicomWriteOptions.Default);
And here's my output (incorrect). The white and distortion is not the desired output.
I'm using ClearCanvas DICOM library, however this shouldn't matter as I'm only trying to manipulate the raw pixel data contained within the file itself.
The desired output would preferably look like the original, but flipped 180 / mirrored.
Some assistance would be greatly appreciated. I've tried my best searching SO, but to no avail.
It took a while, but I ended up solving my problem by using a method from a Java library. You can see the class here.
string file = #"adicomfile.dcm";
DicomFile df = new DicomFile();
df.Load(file);
// Get the amount of bits per pixel from the DICOM header.
int bitsPerPixel = df.DataSet[DicomTags.BitsAllocated].GetInt32(0, 0);
// Get the raw pixel data from the DICOM file.
byte[] bytes = df.DataSet[DicomTags.PixelData].Values as byte[];
// Get the width and height of the image.
int width = df.DataSet[DicomTags.Columns].GetInt32(0, 0);
int height = df.DataSet[DicomTags.Rows].GetInt32(0, 0);
byte[] newBytes = new byte[height * width * (bitsPerPixel / 8)];
int stride = bitsPerPixel / 8;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width * stride; x++)
{
newBytes[((height - y - 1) * (width * stride)) + x] = bytes[(y * (width * stride)) + x];
}
}
// Set patient orientation.
df.DataSet[DicomTags.PatientOrientation].Values = #"A\L";
// The pixel data of the DICOM file to the flipped/mirrored data.
df.DataSet[DicomTags.PixelData].Values = mirroredPixels;
// Save the DICOM file.
df.Save(#"flippedicom.dcm", DicomWriteOptions.Default);
The output was correct and I was able to continue other modifications to the raw pixel data.
Thank you all for the pointers.
Unable to get wave form image for smaller duration audio stream using this code.
I am getting totally blank image. Is there any way to get correct wave form image for smaller duration audio stream. I am using NAudio's AudioFileReader function here.
Bitmap bim = new Bitmap(1800,200);
System.Drawing.Graphics g = Graphics.FromImage(bim);
using (var reader = new AudioFileReader("D:\\Test-Songs\\DawnJay.mp3"))
{
var samples = reader.Length / (reader.WaveFormat.Channels * reader.WaveFormat.BitsPerSample / 8);
var f = 0.0f;
var max = 0.0f;
// waveform will be a maximum of 4000 pixels wide:
var batch = (int)Math.Max(40, samples / 4000);
var mid = 100;
var yScale = 100;
float[] buffer = new float[batch];
int read;
var xPos = 0;
Pen pen = new Pen(Color.Red, 2.0f);
g.Clear(Color.Black);
while ((read = reader.Read(buffer, 0, batch)) == batch)
{
for (int n = 0; n < read; n++)
{
max = Math.Max(Math.Abs(buffer[n]), max);
}
int X1 = xPos;
int X2 = xPos;
float Y1 = mid + (max * yScale);
float Y2 = mid - (max * yScale);
g.DrawLine(pen,X1, Y1, X2, Y2);
max = 0;
xPos++;
}
}
bim.Save("D:\\Images\\waveform.png");
Your code here:
var batch = (int)Math.Max(40, samples / 4000);
This says that you are going to accept a minimum of 40 samples per column. For a small file this might mean that your data gets reduced to only a small number of columns of data in the output bitmap. If you are then scaling that data down to fit into a display area on screen, your audio data might disappear.
Try changing your minimum number of samples per block to a smaller value, which should give you a chance at actually visualizing small audio files. You should probably do full Min-Max calculations, or else your plots for very small files will look totally wrong.
I am trying to convert object of type iTextSharp.text.Image back to System.Drawing.Image.
Here is a chunk of code that is not working:
System.Drawing.Image img = System.Drawing.Image.FromStream(new MemoryStream(itextImg.RawData));
I could be going about this all wrong, but I won't know unless I consult the experts, and after two hours of fruitless searching online, I am finally posting it myself as a question.
I'm pretty sure that will work occasionally, but will fail in the general case... it depends on what compression filters the image is using.
I believe JPEG image streams are exactly what you'd see in a .jpeg file... but for most (all?) other compression types, the image information (height, width, bits per component, number of components, etc) is vital.
So it'll be possible, but Not Like That.
PS: There's at least one image format that iText cannot decompress, CITTFAXDecode (JBIG2, probably others). In those cases, you'll need some Other Software that will get the raw pixel data out so you can wrap it in a Drawing.Image.
Yes, I found the solution by rewriting BarcodeQRCode class of ITextSharp and GetImageMethod() as described below:
using System;
using System.Collections.Generic;
using System.Drawing;
using iTextSharp.text;
using iTextSharp.text.pdf.qrcode;
using iTextSharp.text.pdf.codec;
/*
Class rewritted to Convert ByteMatrix to BMP image by rewritting GetImage method
from ITextSharp
author: Luis Claudio Souza
*/
namespace iTextSharp.text.pdf{
/**
* A QRCode implementation based on the zxing code.
* #author Paulo Soares
* #since 5.0.2
*/
public class BarcodeQRCode {
ByteMatrix bm;
/**
* Creates the QR barcode. The barcode is always created with the smallest possible size and is then stretched
* to the width and height given. Set the width and height to 1 to get an unscaled barcode.
* #param content the text to be encoded
* #param width the barcode width
* #param height the barcode height
* #param hints modifiers to change the way the barcode is create. They can be EncodeHintType.ERROR_CORRECTION
* and EncodeHintType.CHARACTER_SET. For EncodeHintType.ERROR_CORRECTION the values can be ErrorCorrectionLevel.L, M, Q, H.
* For EncodeHintType.CHARACTER_SET the values are strings and can be Cp437, Shift_JIS and ISO-8859-1 to ISO-8859-16. The default value is
* ISO-8859-1.
* #throws WriterException
*/
public BarcodeQRCode(String content, int width, int height, IDictionary<EncodeHintType, Object> hints) {
QRCodeWriter qc = new QRCodeWriter();
bm = qc.Encode(content, width, height, hints);
}
private byte[] GetBitMatrix() {
int width = bm.GetWidth();
int height = bm.GetHeight();
int stride = (width + 7) / 8;
byte[] b = new byte[stride * height];
sbyte[][] mt = bm.GetArray();
for (int y = 0; y < height; ++y) {
sbyte[] line = mt[y];
for (int x = 0; x < width; ++x) {
if (line[x] != 0) {
int offset = stride * y + x / 8;
b[offset] |= (byte)(0x80 >> (x % 8));
}
}
}
return b;
}
/** Gets an <CODE>Image</CODE> with the barcode.
* #return the barcode <CODE>Image</CODE>
* #throws BadElementException on error
*/
public void GetImage()
{
sbyte[][] imgNew = bm.GetArray();
Bitmap bmp1 = new Bitmap(bm.GetWidth(), bm.GetHeight());
Graphics g1 = Graphics.FromImage(bmp1);
g1.Clear(Color.White);
for (int i = 0; i <= imgNew.Length - 1; i++)
{
for (int j = 0; j <= imgNew[i].Length - 1; j++)
{
if (imgNew[j][i] == 0)
{
g1.FillRectangle(Brushes.Black, i, j, 1, 1);
}
else
{
g1.FillRectangle(Brushes.White, i, j, 1, 1);
}
}
}
bmp1.Save("D:\\QREncode.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
After that, I used this code to call the method:
var hints = new Dictionary<EncodeHintType, object>();
hints.Add(EncodeHintType.ERROR_CORRECTION, iTextSharp.text.pdf.qrcode.ErrorCorrectionLevel.H);
BarcodeQRCode code = new BarcodeQRCode("98134979213479523874952873", 100, 100, hints);
code.GetImage();
In new GetImage method, you can choose what to do with the bmp class. In this case, it saves a JPEG image file but the method can either return a memorystream to be used by the caller.