Hi
Da in einem Thema gefragt wurde, wie man Wave-Dateien schreibt, habe ich die letzte Stunde verwendet, um einen kleinen Sourcecode zu schreiben. Hier ein einigermaßen dokumentiertes Beispiel. Zur Anmerkung: Der Programmierstil ist nicht so gehalten, dass die Datei um weitere Chunks ergänzt werden kann. Es ist lediglich eine Veranschaulichung, wie man Waves schreibt, nicht die Anleitung für Riffs.
Übrigens sind alle Teile für Byte, Short (Int16) und Integer (Int32) verfügbar. Zusätzlich können Daten auch als Objekt mit einem definierten Layout bzw. Wertetyp übergeben werden. Dabei werden allerdings lediglich die Daten aus dem unverwalteten Memory verwendet. Dieses Vorgehen garantiert die Unterstützung von Longs, Decimals, usw., wenn diese unterstützt werden (die Samplerate muss an diese angepasst werden).
Außerdem wird ein Beispiel gegeben, wie man einen Sinuston ausgibt. Die Frequenz (frequency) wird in Hertz angegeben, die Dauer (duration) als Timespan.
Die Verwendung in Kombination mit einem System.IO.MemoryStream und einem System.Media.SoundPlayer sieht so aus:
Ausgegeben wird hierbei ein a' von der Dauer 500 Millisekunden ausgegeben, das die Frequenz 440 Hz hat. Die Amplitude ist hierbei maximal ausgereitzt (Short.MaxValue). Bei Verringerung kann die Lautstärke verändert werden.
Gruß
~blaze~
Da in einem Thema gefragt wurde, wie man Wave-Dateien schreibt, habe ich die letzte Stunde verwendet, um einen kleinen Sourcecode zu schreiben. Hier ein einigermaßen dokumentiertes Beispiel. Zur Anmerkung: Der Programmierstil ist nicht so gehalten, dass die Datei um weitere Chunks ergänzt werden kann. Es ist lediglich eine Veranschaulichung, wie man Waves schreibt, nicht die Anleitung für Riffs.
Übrigens sind alle Teile für Byte, Short (Int16) und Integer (Int32) verfügbar. Zusätzlich können Daten auch als Objekt mit einem definierten Layout bzw. Wertetyp übergeben werden. Dabei werden allerdings lediglich die Daten aus dem unverwalteten Memory verwendet. Dieses Vorgehen garantiert die Unterstützung von Longs, Decimals, usw., wenn diese unterstützt werden (die Samplerate muss an diese angepasst werden).
Außerdem wird ein Beispiel gegeben, wie man einen Sinuston ausgibt. Die Frequenz (frequency) wird in Hertz angegeben, die Dauer (duration) als Timespan.
VB.NET-Quellcode
- 'Copyright © ~blaze~ 2011
- Public Class SoundGenerator
- Private strmStream As IO.Stream
- Private rwfcsChunkSettings As RiffWaveFmtChunkSettings
- Private bwWriter As IO.BinaryWriter
- Private lngLengthOffset As Long
- Private lngDataLengthOffset As Long
- Private intCurrentLength As Integer
- Public ReadOnly Property ChunkSettings() As RiffWaveFmtChunkSettings
- Get
- Return rwfcsChunkSettings
- End Get
- End Property
- Public Sub New(ByVal destination As IO.Stream, ByVal bufferSettings As RiffWaveFmtChunkSettings)
- strmStream = destination
- rwfcsChunkSettings = bufferSettings
- bwWriter = New IO.BinaryWriter(destination)
- bwWriter.Write(1179011410I) '"RIFF" schreiben
- 'Ermitteln der Position, an der die Laenge der gesamten Datei steht
- lngLengthOffset = destination.Position
- bwWriter.Write(32I) '32 für die aktuelle Datenlänge in Bytes setzen
- bwWriter.Write(1163280727I) '"WAVE" schreiben
- 'fmt -Chunk
- bwWriter.Write(544501094I) '"fmt " schreiben
- bwWriter.Write(16I) 'restliche Länge des Blocks setzen
- bwWriter.Write(bufferSettings.FormatTag)
- bwWriter.Write(bufferSettings.NumberChanels)
- bwWriter.Write(bufferSettings.SampleRate)
- bwWriter.Write(bufferSettings.BytesPerSecond)
- bwWriter.Write(bufferSettings.BlockAlign)
- bwWriter.Write(bufferSettings.BitsPerSample)
- 'data-Chunk
- bwWriter.Write(1635017060I) '"data" schreiben
- 'ermitteln der Position, an der die Laenge des Data-Chunks steht
- lngDataLengthOffset = destination.Position
- bwWriter.Write(0I)
- End Sub
- Public Sub Append(ByVal elongations() As Byte)
- If rwfcsChunkSettings.BitsPerSample = 8S Then
- Dim dataPos As Long = strmStream.Position
- Dim data(Buffer.ByteLength(elongations) - 1) As Byte
- Buffer.BlockCopy(elongations, 0, data, 0, data.Length) 'Inhalt aus Elongations in data kopieren
- strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + data.Length + 32I)
- strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + data.Length)
- strmStream.Seek(dataPos, IO.SeekOrigin.Begin)
- bwWriter.Write(data)
- Else
- Throw New FormatException("The current sound generator does not support elongations of the type System.Byte.")
- End If
- End Sub
- Public Sub Append(ByVal elongations() As Short)
- If rwfcsChunkSettings.BitsPerSample = 16S Then
- Dim dataPos As Long = strmStream.Position
- Dim data(Buffer.ByteLength(elongations) - 1) As Byte
- Buffer.BlockCopy(elongations, 0, data, 0, data.Length) 'Inhalt aus Elongations in data kopieren
- strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + data.Length + 32I)
- strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + data.Length)
- strmStream.Seek(dataPos, IO.SeekOrigin.Begin)
- bwWriter.Write(data)
- Else
- Throw New FormatException("The current sound generator does not support elongations of the type System.Int16.")
- End If
- End Sub
- Public Sub Append(ByVal elongations() As Integer)
- If rwfcsChunkSettings.BitsPerSample = 16I Then
- Dim dataPos As Long = strmStream.Position
- Dim data(Buffer.ByteLength(elongations) - 1) As Byte
- Buffer.BlockCopy(elongations, 0, data, 0, data.Length) 'Inhalt aus Elongations in data kopieren
- strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + data.Length + 32I)
- strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + data.Length)
- strmStream.Seek(dataPos, IO.SeekOrigin.Begin)
- bwWriter.Write(data)
- Else
- Throw New FormatException("The current sound generator does not support elongations of the type System.Int32.")
- End If
- End Sub
- Public Sub Append(Of T)(ByVal elongations() As T)
- Dim tp As Type = GetType(T) 'Typ der Arrayelemente
- Dim vtSize As Integer = Runtime.InteropServices.Marshal.SizeOf(tp) 'Groesse eines Wertetyps
- Dim attributes() As Runtime.InteropServices.StructLayoutAttribute = DirectCast(tp.GetCustomAttributes(GetType(Runtime.InteropServices.StructLayoutAttribute), False), Runtime.InteropServices.StructLayoutAttribute())
- If Not tp.IsValueType AndAlso (attributes.Length <> 1 OrElse attributes(0).Value = Runtime.InteropServices.LayoutKind.Auto) Then
- 'Layout von T muss klar definiert sein, bzw. T muss ein Wertetyp sein
- Throw New ArgumentException("T is not a value type or its System.Runtime.InteropServices.StructLayoutAttribute is not specified in a correct way. System.Runtime.InteropServices.StructLayoutAttribute.Value mustnot be System.Runtime.InteropServices.LayoutKind.Auto.")
- Else
- If rwfcsChunkSettings.BitsPerSample = vtSize * 8 Then
- Dim dataPos As Long = strmStream.Position
- Dim data(Buffer.ByteLength(elongations) - 1) As Byte
- 'Inhalt aus Elongations in data kopieren, Achtung: keine Arraylaenge ueber Int32.MaxValue moeglich, da
- 'System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement keine Int64 als Parameter unterstuetzt
- For i As Integer = 0I To elongations.Length - 1I
- Runtime.InteropServices.Marshal.Copy(Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement(elongations, i), data, vtSize * i, vtSize)
- Next
- 'Laengen updaten
- strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + data.Length + 32I)
- strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + data.Length)
- 'Daten schreiben
- strmStream.Seek(dataPos, IO.SeekOrigin.Begin)
- bwWriter.Write(data)
- Else
- Throw New FormatException(String.Format("The current sound generator does not support elongations of the type {0}.", tp.FullName))
- End If
- End If
- End Sub
- Public Sub AppendSine(ByVal frequency As Double, ByVal amplitude As SByte, ByVal duration As TimeSpan)
- If rwfcsChunkSettings.BitsPerSample = 8I Then
- Dim samples() As Byte = DirectCast(Array.CreateInstance(GetType(Byte), CLng(rwfcsChunkSettings.SampleRate / frequency)), Byte())
- Dim data() As Byte
- Dim count As Double = duration.TotalSeconds * frequency
- Dim dataPos As Long
- Dim len As Integer
- For l As Long = 0L To samples.LongLength - 1L
- samples.SetValue(CByte(128 + CSByte(amplitude * Math.Sin(2 * l * Math.PI * frequency / rwfcsChunkSettings.SampleRate))), l)
- Next
- data = DirectCast(Array.CreateInstance(GetType(Byte), Buffer.ByteLength(samples)), Byte())
- Buffer.BlockCopy(samples, 0, data, 0, data.Length)
- dataPos = strmStream.Position
- len = CInt(data.Length * count)
- 'Laengen updaten
- strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + len + 32I)
- strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + len)
- For l As Long = 1L To CLng(Math.Floor(count))
- bwWriter.Write(data)
- Next
- bwWriter.Write(data, 0, CInt(data.Length * (count Mod 1)))
- Else
- Throw New FormatException("The current sound generator does not support elongations of the type System.SByte.")
- End If
- End Sub
- Public Sub AppendSine(ByVal frequency As Double, ByVal amplitude As Short, ByVal duration As TimeSpan)
- If rwfcsChunkSettings.BitsPerSample = 16I Then
- Dim samples() As Short = DirectCast(Array.CreateInstance(GetType(Short), CLng(rwfcsChunkSettings.SampleRate / frequency)), Short())
- Dim data() As Byte
- Dim count As Double = duration.TotalSeconds * frequency
- Dim dataPos As Long
- Dim len As Integer
- For l As Long = 0L To samples.LongLength - 1L
- samples.SetValue(CShort(amplitude * Math.Sin(2 * l * Math.PI * frequency / rwfcsChunkSettings.SampleRate)), l)
- Next
- data = DirectCast(Array.CreateInstance(GetType(Byte), Buffer.ByteLength(samples)), Byte())
- Buffer.BlockCopy(samples, 0, data, 0, data.Length)
- dataPos = strmStream.Position
- len = CInt(data.Length * count)
- 'Laengen updaten
- strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + len + 32I)
- strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + len)
- For l As Long = 1L To CLng(Math.Floor(count))
- bwWriter.Write(data)
- Next
- bwWriter.Write(data, 0, CInt(data.Length * (count Mod 1)))
- Else
- Throw New FormatException("The current sound generator does not support elongations of the type System.Int16.")
- End If
- End Sub
- Public Sub AppendSine(ByVal frequency As Double, ByVal amplitude As Integer, ByVal duration As TimeSpan)
- If rwfcsChunkSettings.BitsPerSample = 16I Then
- Dim samples() As Integer = DirectCast(Array.CreateInstance(GetType(Integer), CLng(rwfcsChunkSettings.SampleRate / frequency)), Integer())
- Dim data() As Byte
- Dim count As Double = duration.TotalSeconds * frequency
- Dim dataPos As Long
- Dim len As Integer
- For l As Long = 0L To samples.LongLength - 1L
- samples.SetValue(CInt(amplitude * Math.Sin(2 * l * Math.PI * frequency / rwfcsChunkSettings.SampleRate)), l)
- Next
- data = DirectCast(Array.CreateInstance(GetType(Byte), Buffer.ByteLength(samples)), Byte())
- Buffer.BlockCopy(samples, 0, data, 0, data.Length)
- dataPos = strmStream.Position
- len = CInt(data.Length * count)
- 'Laengen updaten
- strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + len + 32I)
- strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
- bwWriter.Write(intCurrentLength + len)
- For l As Long = 1L To CLng(Math.Floor(count))
- bwWriter.Write(data)
- Next
- bwWriter.Write(data, 0, CInt(data.Length * (count Mod 1)))
- Else
- Throw New FormatException("The current sound generator does not support elongations of the type System.Int32.")
- End If
- End Sub
- End Class
- Public Structure RiffWaveFmtChunkSettings
- Public FormatTag As Short
- Public NumberChanels As Short
- Public SampleRate As Integer
- Public BitsPerSample As Short
- Public ReadOnly Property BlockAlign() As Short
- Get
- Return NumberChanels * BitsPerSample \ 8S
- End Get
- End Property
- Public ReadOnly Property BytesPerSecond() As Integer
- Get
- Return SampleRate * NumberChanels * BitsPerSample \ 8I
- End Get
- End Property
- Public Sub New(ByVal formatTag As Short, ByVal numberChanels As Short, ByVal sampleRate As Integer, ByVal bitsPerSample As Short)
- Me.FormatTag = formatTag
- Me.NumberChanels = numberChanels
- Me.SampleRate = sampleRate
- Me.BitsPerSample = bitsPerSample
- End Sub
- End Structure
Die Verwendung in Kombination mit einem System.IO.MemoryStream und einem System.Media.SoundPlayer sieht so aus:
VB.NET-Quellcode
- Dim stream As New IO.MemoryStream
- Dim generator As New SoundGenerator(stream, New RiffWaveFmtChunkSettings(1S, 2S, 44100I, 16S))
- Dim soundPlayer As New Media.SoundPlayer
- generator.AppendSine(440.0, Short.MaxValue, TimeSpan.FromMilliseconds(500.0))
- stream.Seek(0, IO.SeekOrigin.Begin)
- soundPlayer.Stream = stream
- soundPlayer.Play() 'PlaySync, etc.
Ausgegeben wird hierbei ein a' von der Dauer 500 Millisekunden ausgegeben, das die Frequenz 440 Hz hat. Die Amplitude ist hierbei maximal ausgereitzt (Short.MaxValue). Bei Verringerung kann die Lautstärke verändert werden.
Gruß
~blaze~
Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „~blaze~“ () aus folgendem Grund: Edit1: Neuer Titel, Tags hinzugefügt Edit2: Falscher Aufruf verbessert