Eigene Sounds generieren (Riff-Wave-Streams)

    • VB.NET

    Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von FreakJNS.

      Eigene Sounds generieren (Riff-Wave-Streams)

      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.

      VB.NET-Quellcode

      1. 'Copyright © ~blaze~ 2011
      2. Public Class SoundGenerator
      3. Private strmStream As IO.Stream
      4. Private rwfcsChunkSettings As RiffWaveFmtChunkSettings
      5. Private bwWriter As IO.BinaryWriter
      6. Private lngLengthOffset As Long
      7. Private lngDataLengthOffset As Long
      8. Private intCurrentLength As Integer
      9. Public ReadOnly Property ChunkSettings() As RiffWaveFmtChunkSettings
      10. Get
      11. Return rwfcsChunkSettings
      12. End Get
      13. End Property
      14. Public Sub New(ByVal destination As IO.Stream, ByVal bufferSettings As RiffWaveFmtChunkSettings)
      15. strmStream = destination
      16. rwfcsChunkSettings = bufferSettings
      17. bwWriter = New IO.BinaryWriter(destination)
      18. bwWriter.Write(1179011410I) '"RIFF" schreiben
      19. 'Ermitteln der Position, an der die Laenge der gesamten Datei steht
      20. lngLengthOffset = destination.Position
      21. bwWriter.Write(32I) '32 für die aktuelle Datenlänge in Bytes setzen
      22. bwWriter.Write(1163280727I) '"WAVE" schreiben
      23. 'fmt -Chunk
      24. bwWriter.Write(544501094I) '"fmt " schreiben
      25. bwWriter.Write(16I) 'restliche Länge des Blocks setzen
      26. bwWriter.Write(bufferSettings.FormatTag)
      27. bwWriter.Write(bufferSettings.NumberChanels)
      28. bwWriter.Write(bufferSettings.SampleRate)
      29. bwWriter.Write(bufferSettings.BytesPerSecond)
      30. bwWriter.Write(bufferSettings.BlockAlign)
      31. bwWriter.Write(bufferSettings.BitsPerSample)
      32. 'data-Chunk
      33. bwWriter.Write(1635017060I) '"data" schreiben
      34. 'ermitteln der Position, an der die Laenge des Data-Chunks steht
      35. lngDataLengthOffset = destination.Position
      36. bwWriter.Write(0I)
      37. End Sub
      38. Public Sub Append(ByVal elongations() As Byte)
      39. If rwfcsChunkSettings.BitsPerSample = 8S Then
      40. Dim dataPos As Long = strmStream.Position
      41. Dim data(Buffer.ByteLength(elongations) - 1) As Byte
      42. Buffer.BlockCopy(elongations, 0, data, 0, data.Length) 'Inhalt aus Elongations in data kopieren
      43. strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
      44. bwWriter.Write(intCurrentLength + data.Length + 32I)
      45. strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
      46. bwWriter.Write(intCurrentLength + data.Length)
      47. strmStream.Seek(dataPos, IO.SeekOrigin.Begin)
      48. bwWriter.Write(data)
      49. Else
      50. Throw New FormatException("The current sound generator does not support elongations of the type System.Byte.")
      51. End If
      52. End Sub
      53. Public Sub Append(ByVal elongations() As Short)
      54. If rwfcsChunkSettings.BitsPerSample = 16S Then
      55. Dim dataPos As Long = strmStream.Position
      56. Dim data(Buffer.ByteLength(elongations) - 1) As Byte
      57. Buffer.BlockCopy(elongations, 0, data, 0, data.Length) 'Inhalt aus Elongations in data kopieren
      58. strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
      59. bwWriter.Write(intCurrentLength + data.Length + 32I)
      60. strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
      61. bwWriter.Write(intCurrentLength + data.Length)
      62. strmStream.Seek(dataPos, IO.SeekOrigin.Begin)
      63. bwWriter.Write(data)
      64. Else
      65. Throw New FormatException("The current sound generator does not support elongations of the type System.Int16.")
      66. End If
      67. End Sub
      68. Public Sub Append(ByVal elongations() As Integer)
      69. If rwfcsChunkSettings.BitsPerSample = 16I Then
      70. Dim dataPos As Long = strmStream.Position
      71. Dim data(Buffer.ByteLength(elongations) - 1) As Byte
      72. Buffer.BlockCopy(elongations, 0, data, 0, data.Length) 'Inhalt aus Elongations in data kopieren
      73. strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
      74. bwWriter.Write(intCurrentLength + data.Length + 32I)
      75. strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
      76. bwWriter.Write(intCurrentLength + data.Length)
      77. strmStream.Seek(dataPos, IO.SeekOrigin.Begin)
      78. bwWriter.Write(data)
      79. Else
      80. Throw New FormatException("The current sound generator does not support elongations of the type System.Int32.")
      81. End If
      82. End Sub
      83. Public Sub Append(Of T)(ByVal elongations() As T)
      84. Dim tp As Type = GetType(T) 'Typ der Arrayelemente
      85. Dim vtSize As Integer = Runtime.InteropServices.Marshal.SizeOf(tp) 'Groesse eines Wertetyps
      86. Dim attributes() As Runtime.InteropServices.StructLayoutAttribute = DirectCast(tp.GetCustomAttributes(GetType(Runtime.InteropServices.StructLayoutAttribute), False), Runtime.InteropServices.StructLayoutAttribute())
      87. If Not tp.IsValueType AndAlso (attributes.Length <> 1 OrElse attributes(0).Value = Runtime.InteropServices.LayoutKind.Auto) Then
      88. 'Layout von T muss klar definiert sein, bzw. T muss ein Wertetyp sein
      89. 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.")
      90. Else
      91. If rwfcsChunkSettings.BitsPerSample = vtSize * 8 Then
      92. Dim dataPos As Long = strmStream.Position
      93. Dim data(Buffer.ByteLength(elongations) - 1) As Byte
      94. 'Inhalt aus Elongations in data kopieren, Achtung: keine Arraylaenge ueber Int32.MaxValue moeglich, da
      95. 'System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement keine Int64 als Parameter unterstuetzt
      96. For i As Integer = 0I To elongations.Length - 1I
      97. Runtime.InteropServices.Marshal.Copy(Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement(elongations, i), data, vtSize * i, vtSize)
      98. Next
      99. 'Laengen updaten
      100. strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
      101. bwWriter.Write(intCurrentLength + data.Length + 32I)
      102. strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
      103. bwWriter.Write(intCurrentLength + data.Length)
      104. 'Daten schreiben
      105. strmStream.Seek(dataPos, IO.SeekOrigin.Begin)
      106. bwWriter.Write(data)
      107. Else
      108. Throw New FormatException(String.Format("The current sound generator does not support elongations of the type {0}.", tp.FullName))
      109. End If
      110. End If
      111. End Sub
      112. Public Sub AppendSine(ByVal frequency As Double, ByVal amplitude As SByte, ByVal duration As TimeSpan)
      113. If rwfcsChunkSettings.BitsPerSample = 8I Then
      114. Dim samples() As Byte = DirectCast(Array.CreateInstance(GetType(Byte), CLng(rwfcsChunkSettings.SampleRate / frequency)), Byte())
      115. Dim data() As Byte
      116. Dim count As Double = duration.TotalSeconds * frequency
      117. Dim dataPos As Long
      118. Dim len As Integer
      119. For l As Long = 0L To samples.LongLength - 1L
      120. samples.SetValue(CByte(128 + CSByte(amplitude * Math.Sin(2 * l * Math.PI * frequency / rwfcsChunkSettings.SampleRate))), l)
      121. Next
      122. data = DirectCast(Array.CreateInstance(GetType(Byte), Buffer.ByteLength(samples)), Byte())
      123. Buffer.BlockCopy(samples, 0, data, 0, data.Length)
      124. dataPos = strmStream.Position
      125. len = CInt(data.Length * count)
      126. 'Laengen updaten
      127. strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
      128. bwWriter.Write(intCurrentLength + len + 32I)
      129. strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
      130. bwWriter.Write(intCurrentLength + len)
      131. For l As Long = 1L To CLng(Math.Floor(count))
      132. bwWriter.Write(data)
      133. Next
      134. bwWriter.Write(data, 0, CInt(data.Length * (count Mod 1)))
      135. Else
      136. Throw New FormatException("The current sound generator does not support elongations of the type System.SByte.")
      137. End If
      138. End Sub
      139. Public Sub AppendSine(ByVal frequency As Double, ByVal amplitude As Short, ByVal duration As TimeSpan)
      140. If rwfcsChunkSettings.BitsPerSample = 16I Then
      141. Dim samples() As Short = DirectCast(Array.CreateInstance(GetType(Short), CLng(rwfcsChunkSettings.SampleRate / frequency)), Short())
      142. Dim data() As Byte
      143. Dim count As Double = duration.TotalSeconds * frequency
      144. Dim dataPos As Long
      145. Dim len As Integer
      146. For l As Long = 0L To samples.LongLength - 1L
      147. samples.SetValue(CShort(amplitude * Math.Sin(2 * l * Math.PI * frequency / rwfcsChunkSettings.SampleRate)), l)
      148. Next
      149. data = DirectCast(Array.CreateInstance(GetType(Byte), Buffer.ByteLength(samples)), Byte())
      150. Buffer.BlockCopy(samples, 0, data, 0, data.Length)
      151. dataPos = strmStream.Position
      152. len = CInt(data.Length * count)
      153. 'Laengen updaten
      154. strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
      155. bwWriter.Write(intCurrentLength + len + 32I)
      156. strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
      157. bwWriter.Write(intCurrentLength + len)
      158. For l As Long = 1L To CLng(Math.Floor(count))
      159. bwWriter.Write(data)
      160. Next
      161. bwWriter.Write(data, 0, CInt(data.Length * (count Mod 1)))
      162. Else
      163. Throw New FormatException("The current sound generator does not support elongations of the type System.Int16.")
      164. End If
      165. End Sub
      166. Public Sub AppendSine(ByVal frequency As Double, ByVal amplitude As Integer, ByVal duration As TimeSpan)
      167. If rwfcsChunkSettings.BitsPerSample = 16I Then
      168. Dim samples() As Integer = DirectCast(Array.CreateInstance(GetType(Integer), CLng(rwfcsChunkSettings.SampleRate / frequency)), Integer())
      169. Dim data() As Byte
      170. Dim count As Double = duration.TotalSeconds * frequency
      171. Dim dataPos As Long
      172. Dim len As Integer
      173. For l As Long = 0L To samples.LongLength - 1L
      174. samples.SetValue(CInt(amplitude * Math.Sin(2 * l * Math.PI * frequency / rwfcsChunkSettings.SampleRate)), l)
      175. Next
      176. data = DirectCast(Array.CreateInstance(GetType(Byte), Buffer.ByteLength(samples)), Byte())
      177. Buffer.BlockCopy(samples, 0, data, 0, data.Length)
      178. dataPos = strmStream.Position
      179. len = CInt(data.Length * count)
      180. 'Laengen updaten
      181. strmStream.Seek(lngLengthOffset, IO.SeekOrigin.Begin)
      182. bwWriter.Write(intCurrentLength + len + 32I)
      183. strmStream.Seek(lngDataLengthOffset, IO.SeekOrigin.Begin)
      184. bwWriter.Write(intCurrentLength + len)
      185. For l As Long = 1L To CLng(Math.Floor(count))
      186. bwWriter.Write(data)
      187. Next
      188. bwWriter.Write(data, 0, CInt(data.Length * (count Mod 1)))
      189. Else
      190. Throw New FormatException("The current sound generator does not support elongations of the type System.Int32.")
      191. End If
      192. End Sub
      193. End Class
      194. Public Structure RiffWaveFmtChunkSettings
      195. Public FormatTag As Short
      196. Public NumberChanels As Short
      197. Public SampleRate As Integer
      198. Public BitsPerSample As Short
      199. Public ReadOnly Property BlockAlign() As Short
      200. Get
      201. Return NumberChanels * BitsPerSample \ 8S
      202. End Get
      203. End Property
      204. Public ReadOnly Property BytesPerSecond() As Integer
      205. Get
      206. Return SampleRate * NumberChanels * BitsPerSample \ 8I
      207. End Get
      208. End Property
      209. Public Sub New(ByVal formatTag As Short, ByVal numberChanels As Short, ByVal sampleRate As Integer, ByVal bitsPerSample As Short)
      210. Me.FormatTag = formatTag
      211. Me.NumberChanels = numberChanels
      212. Me.SampleRate = sampleRate
      213. Me.BitsPerSample = bitsPerSample
      214. End Sub
      215. End Structure


      Die Verwendung in Kombination mit einem System.IO.MemoryStream und einem System.Media.SoundPlayer sieht so aus:

      VB.NET-Quellcode

      1. Dim stream As New IO.MemoryStream
      2. Dim generator As New SoundGenerator(stream, New RiffWaveFmtChunkSettings(1S, 2S, 44100I, 16S))
      3. Dim soundPlayer As New Media.SoundPlayer
      4. generator.AppendSine(440.0, Short.MaxValue, TimeSpan.FromMilliseconds(500.0))
      5. stream.Seek(0, IO.SeekOrigin.Begin)
      6. soundPlayer.Stream = stream
      7. 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