📄 wavedisplaycontrol.cs
字号:
using System;
using System.IO;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Drawing;
using System.Drawing.Imaging;
using System.Data;
using System.Text;
using System.Windows.Forms;
namespace SteganoTape {
public partial class WaveDisplayControl : UserControl {
private String currentFile;
private WaveSound waveSound;
private WaveOutPlayer player;
private Stream playerStream;
private float zoomPercent;
private short maxSampleValue;
private Collection<CheckBox> checkBoxes;
private String soxPath;
/// <summary>Is called after a wave file has been opened</summary>
public event EventHandler Open;
/// <summary>Returns path and name of the currently displayed file</summary>
public String CurrentFile{
get { return currentFile; }
}
/// <summary>Gets or sets path and name of sox.exe</summary>
/// <remarks>Sound Exchange is only used to convert 8 bit files to 16 bit.</remarks>
public String SoxPath
{
get { return soxPath; }
set { soxPath = value; }
}
/// <summary>Gets or sets the zoom factor</summary>
public float ZoomPercent
{
get { return zoomPercent; }
set {
zoomPercent = value;
tstZoom.Text = zoomPercent.ToString("n2", CultureInfo.CurrentCulture);
if (waveSound != null) {
DrawWave();
}
}
}
/// <summary>Gets the duration of the displayed file in seconds</summary>
public float Duration{
get{
return (float)waveSound.Count / (float)waveSound.Format.SamplesPerSec;
}
}
/// <summary>Gets the value of the loudest sample</summary>
public int MaxSampleValue
{
get
{
return maxSampleValue;
}
}
/// <summary>Gets or sets the displayed wave sound</summary>
/// <remarks>Setting the sound raises an "Open" event</remarks>
public WaveSound WaveSound
{
get { return waveSound; }
set {
waveSound = value;
foreach (CheckBox chk in checkBoxes) {
chk.Parent.Controls.Remove(chk);
}
checkBoxes.Clear();
if (waveSound != null) {
DrawWave();
}
tsbPlay.Enabled = true;
tsbStop.Enabled = true;
if (Open != null) {
Open(this, EventArgs.Empty);
}
}
}
/// <summary>Gets the selected beeps</summary>
public Collection<Beep> SelectedItems
{
get
{
Collection<Beep> selectedItems = new Collection<Beep>();
foreach (CheckBox checkBox in checkBoxes) {
if (checkBox.Checked) {
selectedItems.Add((Beep)checkBox.Tag);
}
}
return selectedItems;
}
}
/// <summary>Constructor</summary>
public WaveDisplayControl()
{
InitializeComponent();
checkBoxes = new Collection<CheckBox>();
zoomPercent = int.Parse(tstZoom.Text);
currentFile = String.Empty;
tsbPlay.Enabled = false;
tsbStop.Enabled = false;
}
/// <summary>Create a WaveUtility for the displayed sound</summary>
/// <returns></returns>
public WaveUtility CreateWaveUtility(){
WaveUtility waveUtility = new WaveUtility(waveSound);
waveUtility.BeepFound += new BeepFoundHandler(HandleBeepFound);
return waveUtility;
}
/// <summary>Handle "BeepFound" events from a WaveUtility</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void HandleBeepFound(object sender, BeepFoundEventArgs e)
{
MarkBeep(e);
Application.DoEvents();
}
/// <summary>Forget all beeps</summary>
public void ClearBeeps()
{
foreach (CheckBox chk in checkBoxes) { panelContainer.Controls.Remove(chk); }
checkBoxes.Clear();
DrawWave();
}
/// <summary>Draw the wave and display statistics</summary>
public void DrawWave()
{
Control topLevelControl = this.FindForm();
if (topLevelControl == null) { topLevelControl = this; };
topLevelControl.Cursor = Cursors.WaitCursor;
try {
short[] channelSamples = waveSound.Samples;
float zoom = zoomPercent / 100;
maxSampleValue = 0;
//remove existing checkboxes
foreach (CheckBox chk in checkBoxes) { panelContainer.Controls.Remove(chk); }
//resize picturebox
int scaledWidth = (int)((float)channelSamples.Length * zoom);
picWave.Width = scaledWidth;
//Skalierung berechnen
picWave.Height = panelContainer.Height - SystemInformation.HorizontalScrollBarHeight;
int samplesPerPixel;
int spacePerSample;
if (picWave.Width > channelSamples.Length) {
//mehrere Pixel pro Sample
samplesPerPixel = 1;
spacePerSample = picWave.Width / channelSamples.Length;
} else {
//mehrere Samples pro Pixel
spacePerSample = 1;
samplesPerPixel = channelSamples.Length / picWave.Width;
}
//Breite einer Sekunde berechnen
int pixelsPerSecond = (int)(waveSound.Format.SamplesPerSec * spacePerSample / samplesPerPixel);
Bitmap bitmap = new Bitmap(picWave.Width, picWave.Height, PixelFormat.Format24bppRgb);
Graphics graphics = Graphics.FromImage(bitmap);
Pen pen = new Pen(Color.Black);
graphics.Clear(Color.White);
int absValue;
for (int channelSamplesIndex = 0; channelSamplesIndex < channelSamples.Length; channelSamplesIndex++) {
absValue = Math.Abs((int)channelSamples[channelSamplesIndex]);
if (absValue > maxSampleValue) {
maxSampleValue = (absValue > short.MaxValue) ? (short)(absValue - 1) : (short)absValue;
}
}
float yOffset = bitmap.Height / 2;
if (maxSampleValue != 0) { //not trying to display silence (all pixels == 0)
float yScale = yOffset / maxSampleValue;
float xPosition = 0;
int pixelMaximum = 0;
int pixelMinimum = 0;
short currentSample;
PointF previousPoint = new PointF(0, yOffset);
for (int n = 0; n < channelSamples.Length; n += samplesPerPixel) {
currentSample = channelSamples[n];
pixelMaximum = 0;
pixelMinimum = 0;
for (int sampleIndex = n; sampleIndex < (n + samplesPerPixel); sampleIndex++) {
if (currentSample > pixelMaximum) { pixelMaximum = currentSample; }
if (currentSample < pixelMinimum) { pixelMinimum = currentSample; }
}
pixelMaximum = (int)(pixelMaximum * yScale);
pixelMinimum = (int)(pixelMinimum * yScale);
graphics.DrawLine(pen, previousPoint.X, previousPoint.Y, xPosition, yOffset + pixelMinimum);
graphics.DrawLine(pen, xPosition, yOffset + pixelMaximum, xPosition, yOffset + pixelMinimum);
previousPoint.X = xPosition;
previousPoint.Y = yOffset + pixelMaximum;
xPosition += spacePerSample;
}
}
//show seconds
int second = 0;
Brush brush = new SolidBrush(Color.Blue);
Font boldFont = new Font(this.Font, FontStyle.Bold);
for (int n = 0; n < picWave.Width; n += pixelsPerSecond) {
graphics.DrawString(second.ToString(), boldFont, brush, n, picWave.Height - 15);
second++;
}
pen.Color = Color.Red;
graphics.DrawLine(pen, 0, yOffset, bitmap.Width, yOffset);
graphics.Dispose();
picWave.Image = bitmap;
//show statistics
tsslLength.Text = this.Duration.ToString("n2", CultureInfo.CurrentCulture);
tsslVolume.Text = maxSampleValue.ToString(CultureInfo.CurrentCulture);
//re-add existing checkboxes
foreach (CheckBox chk in checkBoxes) { MarkBeep(new BeepFoundEventArgs((Beep)chk.Tag), chk); }
} finally {
topLevelControl.Cursor = Cursors.Default;
}
}
/// <summary>Display a beep and add a new CheckBox</summary>
/// <param name="eventArgs"></param>
private void MarkBeep(BeepFoundEventArgs eventArgs)
{
MarkBeep(eventArgs, new CheckBox());
}
/// <summary>Display a beep</summary>
/// <param name="eventArgs"></param>
/// <param name="checkBox"></param>
private void MarkBeep(BeepFoundEventArgs eventArgs, CheckBox checkBox)
{
int samplesPerPixel;
int spacePerSample;
int imageWidth = picWave.Image.Width;
if (imageWidth > waveSound.Count) {
//mehrere Pixel pro Sample
samplesPerPixel = 1;
spacePerSample = imageWidth / waveSound.Count;
} else {
//mehrere Samples pro Pixel
spacePerSample = 1;
samplesPerPixel = waveSound.Count / imageWidth;
}
Graphics graphics = Graphics.FromImage(picWave.Image);
Pen pen = new Pen(Color.Red);
Brush brush = new SolidBrush(Color.Red);
//start index
int startPixelIndex = spacePerSample * (int)Math.Floor((float)eventArgs.Beep.StartSampleIndex / (float)samplesPerPixel);
graphics.DrawLine(pen, startPixelIndex, 0, startPixelIndex, picWave.Height);
//left arrow
graphics.DrawPolygon(pen, new Point[3] {
new Point(startPixelIndex, 0),
new Point(startPixelIndex + 4, 4),
new Point(startPixelIndex, 8) });
//end index
int endPixelIndex = spacePerSample * (int)Math.Floor((float)eventArgs.Beep.EndSampleIndex / (float)samplesPerPixel);
graphics.DrawLine(pen, endPixelIndex, 0, endPixelIndex, picWave.Height);
//right arrow
graphics.DrawPolygon(pen, new Point[3] {
new Point(endPixelIndex, 0),
new Point(endPixelIndex - 4, 4),
new Point(endPixelIndex, 8) });
graphics.DrawString(
String.Format(
CultureInfo.CurrentCulture,
"{0} - {1}",
eventArgs.Beep.StartSecond.ToString("n1", CultureInfo.CurrentCulture),
eventArgs.Beep.EndSecond.ToString("n1", CultureInfo.CurrentCulture)),
this.Font, brush, startPixelIndex, 10);
graphics.Dispose();
picWave.Invalidate();
if (checkBox.Tag == null) {
//the checkbox is new
checkBox.Tag = eventArgs.Beep;
checkBoxes.Add(checkBox);
checkBox.Checked = true;
checkBox.Width = checkBox.Height;
}
checkBox.Left = startPixelIndex + ((endPixelIndex - startPixelIndex) / 2);
checkBox.Top = picWave.Height - checkBox.Height;
checkBox.BackColor = Color.White;
panelContainer.Controls.Add(checkBox);
checkBox.BringToFront();
}
/// <summary>Stop playback, before closing the application</summary>
public void Close()
{
PlayerStop();
}
/// <summary>Change the zoom factor, when the user leaves the zoom box</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tstZoom_Leave(object sender, EventArgs e)
{
OnZoomFactorChanged();
}
/// <summary>Change the zoom factor, when the user leaves the zoom box</summary>
private void OnZoomFactorChanged()
{
float newZoomPercent;
if (float.TryParse(tstZoom.Text, out newZoomPercent)) {
this.ZoomPercent = newZoomPercent;
}
}
/// <summary>Play the wave sound</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbPlay_Click(object sender, EventArgs e)
{
if (waveSound != null) {
playerStream = waveSound.CreateStream();
this.player = new WaveOutPlayer(this.WaveSound.Format, playerStream);
}
}
/// <summary>Stop playback</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbStop_Click(object sender, EventArgs e)
{
PlayerStop();
}
/// <summary>Stop the player and close its stream</summary>
private void PlayerStop()
{
if (player != null) {
try {
player.Dispose();
} finally {
player = null;
}
}
if (playerStream != null) {
playerStream.Close();
}
}
/// <summary>Open a sound from a wave file</summary>
/// <param name="fileName"></param>
public void OpenFromFile(String fileName)
{
currentFile = fileName;
WaveUtility waveUtility = new WaveUtility(fileName);
if (waveUtility.WaveSound.Format.BitsPerSample != 16) {
waveUtility.ConvertToDefaultFormat(
Path.GetTempPath(),
soxPath);
}
this.WaveSound = waveUtility.WaveSound;
}
/// <summary>Let the user select a wave file, and then open it</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbOpen_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "*.wav|*.wav";
if (dlg.ShowDialog() == DialogResult.OK) {
OpenFromFile(dlg.FileName);
}
}
/// <summary>Save the displayed sound to a wave file</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbSave_Click(object sender, EventArgs e)
{
if (waveSound != null) {
SaveFileDialog dlg = new SaveFileDialog();
dlg.Filter = "*.wav|*.wav";
if (dlg.ShowDialog() == DialogResult.OK) {
waveSound.SaveToFile(dlg.FileName);
}
}
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -