Track mit NAudio in seperatem Thread abspielen

  • VB.NET

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von thefiloe.

    Track mit NAudio in seperatem Thread abspielen

    Hallo,
    ich hab mal wieder ne Frage:
    Wenn ich eine Aktion ausführe und sich der GUI-Thread kurz aufhängt, bleibt ebenfalls der Track von NAudio stehen. Bei Bass.Net ist dies nicht so. Gibt es irgendeine Möglichkeit, das zu verhindern? Kann man den Track in einem extra Thread ausführen?

    Hier mein Code:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Collections.Generic
    2. Imports System.ComponentModel
    3. Imports System.Windows.Threading
    4. Imports NAudio.Wave
    5. Imports WPFSoundVisualizationLib
    6. Imports System.Windows.Interop
    7. Public Class NAudioEngine
    8. Implements INotifyPropertyChanged
    9. Implements ISpectrumPlayer
    10. Implements IWaveformPlayer
    11. Implements IDisposable
    12. #Region "Fields"
    13. Private _NewTrackDelegate As NewTrackPlay
    14. Private Shared m_instance As NAudioEngine
    15. Private ReadOnly positionTimer As New DispatcherTimer(DispatcherPriority.ApplicationIdle)
    16. Private ReadOnly waveformGenerateWorker As New BackgroundWorker()
    17. Private ReadOnly wavefftDataSize As Integer = CInt(FFTDataSize.FFT2048)
    18. Private disposed As Boolean
    19. Private m_canPlay As Boolean
    20. Private m_canPause As Boolean
    21. Private m_canStop As Boolean
    22. Private m_isPlaying As Boolean
    23. Private inChannelTimerUpdate As Boolean
    24. Private m_channelLength As Double
    25. Private m_channelPosition As Double
    26. Private inChannelSet As Boolean
    27. Private waveOutDevice As WaveOut = New WaveOut()
    28. Private m_activeStream As WaveStream
    29. Private inputStream As WaveChannel32
    30. Private sampleAggregator As SampleAggregator
    31. Private waveformAggregator As SampleAggregator
    32. Private pendingWaveformPath As String
    33. Private fullLevelData As Single()
    34. Private m_waveformData As Single()
    35. Private repeatStart As TimeSpan
    36. Private repeatStop As TimeSpan
    37. Private inRepeatSet As Boolean
    38. Private _currentTrack As Track
    39. Private _volume As Single
    40. #End Region
    41. #Region "Events"
    42. Public Event ChannelPositionChanged(sender As Object, e As EventArgs)
    43. Public Event ChannelLengthChange(sender As Object, e As EventArgs)
    44. Public Event PlayStateChanged(sender As Object, e As EventArgs)
    45. Public Event TrackEnd(sender As Object, e As EventArgs)
    46. #End Region
    47. #Region "Constants"
    48. Private Const waveformCompressedPointCount As Integer = 2000
    49. Private Const repeatThreshold As Integer = 200
    50. #End Region
    51. #Region "Singleton Pattern"
    52. Public Shared ReadOnly Property Instance() As NAudioEngine
    53. Get
    54. If m_instance Is Nothing Then
    55. m_instance = New NAudioEngine()
    56. End If
    57. Return m_instance
    58. End Get
    59. End Property
    60. #End Region
    61. #Region "Constructor"
    62. Private Sub New()
    63. positionTimer.Interval = TimeSpan.FromMilliseconds(50)
    64. AddHandler positionTimer.Tick, AddressOf positionTimer_Tick
    65. AddHandler waveformGenerateWorker.DoWork, AddressOf waveformGenerateWorker_DoWork
    66. AddHandler waveformGenerateWorker.RunWorkerCompleted, AddressOf waveformGenerateWorker_RunWorkerCompleted
    67. waveformGenerateWorker.WorkerSupportsCancellation = True
    68. End Sub
    69. #End Region
    70. #Region "IDisposable"
    71. Public Sub Dispose() Implements IDisposable.Dispose
    72. Dispose(True)
    73. GC.SuppressFinalize(Me)
    74. End Sub
    75. Protected Overridable Sub Dispose(disposing As Boolean)
    76. If Not disposed Then
    77. If disposing Then
    78. StopAndCloseStream()
    79. If waveformGenerateWorker.IsBusy Then
    80. waveformGenerateWorker.CancelAsync()
    81. End If
    82. waveformGenerateWorker.Dispose()
    83. End If
    84. disposed = True
    85. End If
    86. End Sub
    87. #End Region
    88. #Region "INotifyPropertyChanged"
    89. Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    90. Private Sub OnPropertyChanged(info As String)
    91. RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
    92. End Sub
    93. #End Region
    94. #Region "Private Utility Methods"
    95. Private Sub StopAndCloseStream()
    96. If waveOutDevice IsNot Nothing Then
    97. waveOutDevice.[Stop]()
    98. End If
    99. If m_activeStream IsNot Nothing Then
    100. inputStream.Close()
    101. inputStream = Nothing
    102. ActiveStream.Close()
    103. ActiveStream = Nothing
    104. End If
    105. If waveOutDevice IsNot Nothing Then
    106. waveOutDevice.Dispose()
    107. waveOutDevice = Nothing
    108. End If
    109. End Sub
    110. #End Region
    111. #Region "Public Methods"
    112. Public Delegate Sub NewTrackPlay(track As Track)
    113. Public Sub RegisterNewTrackDelegate(mydelegate As NewTrackPlay)
    114. _NewTrackDelegate = mydelegate
    115. End Sub
    116. Public Sub UpdateTimeStrings()
    117. OnPropertyChanged("ChannelLengthTimeToString")
    118. OnPropertyChanged("ChannelPositionTimeToString")
    119. End Sub
    120. Public Sub [Stop]()
    121. If waveOutDevice IsNot Nothing Then
    122. waveOutDevice.[Stop]()
    123. End If
    124. IsPlaying = False
    125. CanStop = False
    126. CanPlay = False
    127. CanPause = False
    128. StopAndCloseStream()
    129. Me.CurrentTrack = Nothing
    130. End Sub
    131. Public Sub Pause()
    132. If IsPlaying AndAlso CanPause Then
    133. waveOutDevice.Pause()
    134. IsPlaying = False
    135. CanPlay = True
    136. CanPause = False
    137. End If
    138. End Sub
    139. Public Sub Play()
    140. If CanPlay Then
    141. waveOutDevice.Play()
    142. IsPlaying = True
    143. CanPause = True
    144. CanPlay = False
    145. CanStop = True
    146. End If
    147. End Sub
    148. Public Overloads Sub OpenFile(t As Track)
    149. [Stop]()
    150. If ActiveStream IsNot Nothing Then
    151. SelectionBegin = TimeSpan.Zero
    152. SelectionEnd = TimeSpan.Zero
    153. ChannelPosition = 0
    154. End If
    155. StopAndCloseStream()
    156. If System.IO.File.Exists(t.Path) Then
    157. Try
    158. Dim mainWindow As Window = Application.Current.MainWindow
    159. Dim interopHelper As New WindowInteropHelper(mainWindow)
    160. waveOutDevice = New WaveOut(interopHelper.Handle) With { _
    161. .DesiredLatency = 100, _
    162. .Volume = Volume _
    163. }
    164. ActiveStream = New Mp3FileReader(t.Path)
    165. inputStream = New WaveChannel32(ActiveStream)
    166. sampleAggregator = New SampleAggregator(wavefftDataSize)
    167. AddHandler inputStream.Sample, AddressOf inputStream_Sample
    168. waveOutDevice.Init(inputStream)
    169. ChannelLength = inputStream.TotalTime.TotalSeconds
    170. GenerateWaveformData(t.Path)
    171. CanPlay = True
    172. If _NewTrackDelegate IsNot Nothing Then
    173. _NewTrackDelegate.Invoke(t)
    174. End If
    175. CurrentTrack = t
    176. Catch
    177. ActiveStream = Nothing
    178. CanPlay = False
    179. End Try
    180. End If
    181. End Sub
    182. #End Region
    183. #Region "Public Properties"
    184. Public Property CurrentTrack As Track
    185. Get
    186. Return _currentTrack
    187. End Get
    188. Protected Set(value As Track)
    189. If value IsNot _currentTrack Then
    190. _currentTrack = value
    191. OnPropertyChanged("CurrentTrack")
    192. End If
    193. End Set
    194. End Property
    195. Public Property ActiveStream() As WaveStream
    196. Get
    197. Return m_activeStream
    198. End Get
    199. Protected Set(value As WaveStream)
    200. Dim oldValue As WaveStream = m_activeStream
    201. m_activeStream = value
    202. If oldValue IsNot m_activeStream Then
    203. OnPropertyChanged("ActiveStream")
    204. End If
    205. End Set
    206. End Property
    207. Public Property CanPlay() As Boolean
    208. Get
    209. Return m_canPlay
    210. End Get
    211. Protected Set(value As Boolean)
    212. Dim oldValue As Boolean = m_canPlay
    213. m_canPlay = value
    214. If oldValue <> m_canPlay Then
    215. OnPropertyChanged("CanPlay")
    216. End If
    217. End Set
    218. End Property
    219. Public Property CanPause() As Boolean
    220. Get
    221. Return m_canPause
    222. End Get
    223. Protected Set(value As Boolean)
    224. Dim oldValue As Boolean = m_canPause
    225. m_canPause = value
    226. If oldValue <> m_canPause Then
    227. OnPropertyChanged("CanPause")
    228. End If
    229. End Set
    230. End Property
    231. Public Property CanStop() As Boolean
    232. Get
    233. Return m_canStop
    234. End Get
    235. Protected Set(value As Boolean)
    236. Dim oldValue As Boolean = m_canStop
    237. m_canStop = value
    238. If oldValue <> m_canStop Then
    239. OnPropertyChanged("CanStop")
    240. End If
    241. End Set
    242. End Property
    243. Public Property IsPlaying() As Boolean Implements IWaveformPlayer.IsPlaying
    244. Get
    245. Return m_isPlaying
    246. End Get
    247. Protected Set(value As Boolean)
    248. Dim oldValue As Boolean = m_isPlaying
    249. m_isPlaying = value
    250. If oldValue <> m_isPlaying Then
    251. OnPropertyChanged("IsPlaying")
    252. RaiseEvent PlayStateChanged(Me, EventArgs.Empty)
    253. End If
    254. positionTimer.IsEnabled = value
    255. End Set
    256. End Property
    257. Private _ChannelLengthTimeToString As String
    258. Public ReadOnly Property ChannelLengthTimeToString As String
    259. Get
    260. If ActiveStream Is Nothing Then Return "00:00:00"
    261. Return ActiveStream.TotalTime.ToString("hh\:mm\:ss")
    262. End Get
    263. End Property
    264. Private _ChannelPositionTimeToString As String
    265. Public ReadOnly Property ChannelPositionTimeToString As String
    266. Get
    267. If ActiveStream Is Nothing Then Return "00:00:00"
    268. Return ActiveStream.CurrentTime.ToString("hh\:mm\:ss")
    269. End Get
    270. End Property
    271. Public Property Volume As Single
    272. Get
    273. Return _volume
    274. End Get
    275. Set(value As Single)
    276. _volume = value
    277. If waveOutDevice IsNot Nothing Then waveOutDevice.Volume = value
    278. OnPropertyChanged("Volume")
    279. End Set
    280. End Property
    281. #End Region
    282. #Region "Event Handlers"
    283. Private Sub inputStream_Sample(sender As Object, e As SampleEventArgs)
    284. sampleAggregator.Add(e.Left, e.Right)
    285. Dim repeatStartPosition As Long = CLng((SelectionBegin.TotalSeconds / ActiveStream.TotalTime.TotalSeconds) * ActiveStream.Length)
    286. Dim repeatStopPosition As Long = CLng((SelectionEnd.TotalSeconds / ActiveStream.TotalTime.TotalSeconds) * ActiveStream.Length)
    287. If ((SelectionEnd - SelectionBegin) >= TimeSpan.FromMilliseconds(repeatThreshold)) AndAlso ActiveStream.Position >= repeatStopPosition Then
    288. sampleAggregator.Clear()
    289. ActiveStream.Position = repeatStartPosition
    290. End If
    291. End Sub
    292. Private Sub waveStream_Sample(sender As Object, e As SampleEventArgs)
    293. waveformAggregator.Add(e.Left, e.Right)
    294. End Sub
    295. Private Sub positionTimer_Tick(sender As Object, e As EventArgs)
    296. inChannelTimerUpdate = True
    297. ChannelPosition = (CDbl(ActiveStream.Position) / CDbl(ActiveStream.Length)) * ActiveStream.TotalTime.TotalSeconds
    298. If _ChannelLengthTimeToString <> ChannelLengthTimeToString Then
    299. OnPropertyChanged("ChannelLengthTimeToString")
    300. _ChannelLengthTimeToString = ChannelLengthTimeToString
    301. End If
    302. If _ChannelPositionTimeToString <> ChannelPositionTimeToString Then
    303. OnPropertyChanged("ChannelPositionTimeToString")
    304. _ChannelPositionTimeToString = ChannelPositionTimeToString
    305. End If
    306. If ChannelPosition = ActiveStream.TotalTime.TotalSeconds Then
    307. IsPlaying = False
    308. CanPlay = True
    309. CanPause = False
    310. CanStop = False
    311. RaiseEvent TrackEnd(Me, EventArgs.Empty)
    312. End If
    313. inChannelTimerUpdate = False
    314. End Sub
    315. #End Region
    316. End Class


    Danke für eure Antworten :)
    Mfg
    Vincent

    Hatte ich auch zuerst vor, nur schmeiß ich dann meinen ganzen Visualizer übern Haufen... Naja, Thema hat sich erledigt, Danke :)

    EDIT:// Eine Frage hätte ich noch: Geht das denn alles schon mit CScore, also Multihreading, oder muss das thefileoe erst noch implementieren?
    Mfg
    Vincent

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „VincentTB“ ()

    Geht und die Visualisierung kannst übernehmen. Außerdem unterscheidet sich cscore abgesehen von der grundlegenden Architektur doch recht stark. Zudem nicht, dass ein falscher Eindruck entsteht... Ich habe den gesamten Code selbst geschrieben außer ein paar enumerationen, etc. Diese teile sind aber auch gekennzeichnet.

    Aber wie immer danke für die Werbung @artentus.
    Und ja es ist das notwendige schon implementiert.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.