Denkfehler beim Durchlaufen von For...Next-Loops?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    Denkfehler beim Durchlaufen von For...Next-Loops?

    Hallo liebe Community,

    ich habe eine AngewendeteEffekte As ObservableCollection(Of PlugInsViewModel) und eine KopieAngewendeteEffelte As List(Of PlugInsViewMoel) und einen Zähler:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private KopieAngewendeteEffekte As New List(Of ViewModel.PlugInsViewModel)
    2. Private AktuelleID As Integer = 0
    3. Private _AngewendeteEffekte As ObservableCollection(Of ViewModel.PlugInsViewModel)
    4. Public Property AngewendeteEffekte As ObservableCollection(Of ViewModel.PlugInsViewModel)
    5. Get
    6. Return _AngewendeteEffekte
    7. End Get
    8. Set(value As ObservableCollection(Of ViewModel.PlugInsViewModel))
    9. _AngewendeteEffekte = value
    10. RaisePropertyChanged()
    11. End Set
    12. End Property


    Die Klasse PlugInsViewModel sieht so aus, ist aber glaube ich nicht von Belang:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Windows.Media
    3. Public Class PlugInsViewModel
    4. Inherits ViewModel.Instrastructure.ViewModelBase
    5. Friend ReadOnly ModelObjekt As New Model.PlugInsModel
    6. Public Sub New()
    7. End Sub
    8. Public Sub New(argID As Integer, argPfad As String, argName As String, argBypass As Boolean, argStreamHandle As Integer, argUIHandle As List(Of IntPtr), argUIOffen As Boolean, argByPassColorBrush As SolidColorBrush, argParameter As List(Of Single))
    9. ModelObjekt = New Model.PlugInsModel(argID, argPfad, argName, argBypass, argStreamHandle, argUIHandle, argUIOffen, argByPassColorBrush, argParameter)
    10. End Sub
    11. Friend Sub New(_ModelObjekt As Model.PlugInsModel)
    12. ModelObjekt = _ModelObjekt
    13. End Sub
    14. Public Property ID As Integer
    15. Get
    16. Return ModelObjekt.ID
    17. End Get
    18. Set(value As Integer)
    19. ModelObjekt.ID = value
    20. RaisePropertyChanged()
    21. End Set
    22. End Property
    23. Public Property Pfad As String
    24. Get
    25. Return ModelObjekt.Pfad
    26. End Get
    27. Set(value As String)
    28. ModelObjekt.Pfad = value
    29. RaisePropertyChanged()
    30. End Set
    31. End Property
    32. Public Property Name As String
    33. Get
    34. Return ModelObjekt.Name
    35. End Get
    36. Set(value As String)
    37. ModelObjekt.Name = value
    38. RaisePropertyChanged()
    39. End Set
    40. End Property
    41. Public Property Bypass As Boolean
    42. Get
    43. Return ModelObjekt.Bypass
    44. End Get
    45. Set(value As Boolean)
    46. ModelObjekt.Bypass = value
    47. RaisePropertyChanged()
    48. End Set
    49. End Property
    50. Public Property Handle As Integer
    51. Get
    52. Return ModelObjekt.StreamHandle
    53. End Get
    54. Set(value As Integer)
    55. ModelObjekt.StreamHandle = value
    56. RaisePropertyChanged()
    57. End Set
    58. End Property
    59. Public Property UIHandle As List(Of IntPtr)
    60. Get
    61. Return ModelObjekt.UIHandle
    62. End Get
    63. Set(value As List(Of IntPtr))
    64. ModelObjekt.UIHandle = value
    65. RaisePropertyChanged()
    66. End Set
    67. End Property
    68. Public Property UIOffen As Boolean
    69. Get
    70. Return ModelObjekt.UIOffen
    71. End Get
    72. Set(value As Boolean)
    73. ModelObjekt.UIOffen = value
    74. RaisePropertyChanged()
    75. End Set
    76. End Property
    77. Public Property ByPassColorBrush As SolidColorBrush
    78. Get
    79. Return ModelObjekt.ByPassColorBrush
    80. End Get
    81. Set(value As SolidColorBrush)
    82. ModelObjekt.ByPassColorBrush = value
    83. RaisePropertyChanged()
    84. End Set
    85. End Property
    86. Public Property Parameter As List(Of Single)
    87. Get
    88. Return ModelObjekt.Parameter
    89. End Get
    90. Set(value As List(Of Single))
    91. ModelObjekt.Parameter = value
    92. RaisePropertyChanged()
    93. End Set
    94. End Property
    95. End Class


    Die Listen beinhalten Daten zu den auf eine Audiospur angewendeten VST-Effekt-PlugIns. Der Zähler wird zur eindeutigen Identifikation benutzt in der Eigenschaft ID und wird folgrndermassen gesetzt, wenn man einen Effekt zu AngewendeteEffekte zufügt:

    VB.NET-Quellcode

    1. Dim EffektAktuell As PlugInsViewModel = New PlugInsViewModel(AktuelleID, GewaehlterEffekt.Pfad, GewaehlterEffekt.Name, False, Nothing, Nothing, False, CType(System.Windows.Application.Current.Resources("VordergrundfarbeBrush"), SolidColorBrush), New List(Of Single))
    2. AktuelleID += 1
    3. AngewendeteEffekte.Add(EffektAktuell)


    Dann habe ich folgende Funktion, die immer ausgeführt wird, wenn ein Item zu AngewendeteEffekte hinzugefügt, eins entfernt oder eins nach oben oder unten in der Liste verschoben wird:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Sub InitilaisiereVSTEffekte(sender As Object, e As NotifyCollectionChangedEventArgs)
    2. For Each item In KopieAngewendeteEffekte
    3. Dim index As Integer = AngewendeteEffekte.ToList.FindIndex(Function(x) x.ID = item.ID) 'wenn IDs übereinstimmen dann...
    4. If index <> -1 Then
    5. For i = 0 To BassVst.BASS_VST_GetParamCount(item.Handle) - 1 'einzelne Parameter durchlaufen und Werte ermitteln
    6. Dim Wert As Single = BassVst.BASS_VST_GetParam(item.Handle, i)
    7. Debug.WriteLine("GetParam: " & Bass.BASS_ErrorGetCode)
    8. AngewendeteEffekte(index).Parameter.Add(Wert) 'einzelne Parameter speichern
    9. item.Parameter.Add(Wert)
    10. Next
    11. End If
    12. BassVst.BASS_VST_EmbedEditor(item.Handle, Nothing) 'UI des PlugIns schliessen
    13. BassVst.BASS_VST_ChannelRemoveDSP(sourcechannel, item.Handle) 'EffektPlugIn disposen
    14. Next
    15. For i = AngewendeteEffekte.Count - 1 To 0 Step -1 'rückwärts zählen, um die Priorität der Effekte richtig zu setzen
    16. AngewendeteEffekte(i).Handle = BassVst.BASS_VST_ChannelSetDSP(sourcechannel, AngewendeteEffekte(i).Pfad, 0, AngewendeteEffekte.Count - i) 'Effekt initialisieren, Priorität: letztes Argument
    17. If AngewendeteEffekte(i).Parameter.Count = 0 Then 'wenn keine Parameter gespeichert dann...
    18. BassVst.BASS_VST_SetParamRestoreDefaults(AngewendeteEffekte(i).Handle) '...Defaultwerte verwenden
    19. Else
    20. For Each item In KopieAngewendeteEffekte
    21. If item.ID = AngewendeteEffekte(i).ID Then 'wenn ID übereinstimmt dann...
    22. For j = 0 To item.Parameter.Count - 1 '...gespeicherte Paramter anwenden auf PlugIn
    23. BassVst.BASS_VST_SetParam(AngewendeteEffekte(i).Handle, j, AngewendeteEffekte(i).Parameter(j))
    24. Debug.WriteLine("SetParam: " & Bass.BASS_ErrorGetCode)
    25. Next
    26. End If
    27. Next
    28. End If
    29. If AngewendeteEffekte(i).UIOffen Then
    30. BassVst.BASS_VST_EmbedEditor(AngewendeteEffekte(i).Handle, AngewendeteEffekte(i).UIHandle(0)) 'Effekt-PlugIn-UI wieder öffnen, wenn es bereits vorher offen war
    31. End If
    32. Next
    33. For Each item In AngewendeteEffekte 'Aktuelle Werte in KopieAngewendeteEffekte speichern für den nächsten Durchlauf der Sub
    34. Dim itemFarbe As New Media.SolidColorBrush
    35. If item.Bypass Then
    36. itemFarbe = CType(Application.Current.Resources("AbsetzfarbeBrush"), Media.SolidColorBrush)
    37. Else
    38. itemFarbe = CType(Application.Current.Resources("VordergrundfarbeBrush"), Media.SolidColorBrush)
    39. End If
    40. KopieAngewendeteEffekte.Add(New PlugInsViewModel(item.ID, item.Pfad, item.Name, item.Bypass, item.Handle, item.UIHandle, item.UIOffen, itemFarbe, item.Parameter))
    41. Next
    42. End Sub


    Ziel dieser Funktion soll es sein, wenn sich etwas an AngewendeteEffekte ändert, die PlugIns neu initialisiert werden sollen. KopieAngewendeteEffekte soll nur eine "Hilfsliste" sein.
    Also z.B. wenn der Benutzer die UI des PlugIns schon offen hat (Eigenschaft UIOffen), dass diese UI dann nach dem re-initialisieren wieder geöffnet wird und auch die in Parameter gespeicherten Ändeungen (also z.B. der Pitch des PlugIns) übernommen werden sollen.

    Klappt soweit auch ganz gut, bloss bei der Parameter-Übernahme habe ich noch Probleme. Diese werden nur übernommen, wenn mehrere UIs offen sind und dann auch nur das, was als letztes zugefügt wurde (man beachte: die Schleife zählt rückwärts, s. Kommentare!!).

    Ich vermute ich habe einen Denkfehler da drinnen. Aber ich komm nicht wirklich drauf, habs mir immer wieder angeschaut und auch zwei Nächte drüber geschlafen...

    Beim Debuggen hab ich aber schon feststellen können, dass die Aufrufe zum Auslesen und wieder Setzen der Paramter jedenfalls keine Fehler verursachen, also das sollte klappen.

    Wenn jemand den Fehler entdeckt wäre das gut, ich wäre aber auch dankbar für einen Ansatz beim Debuggen (was würdet ihr (als erstes) überprüfen?). Oder was würdet ihr sogar anders machen?

    Edit: Hab mal versucht statt KopieAngewendeteEffekte mit e.OldItems zu arbeiten, da werden die Parameter richtig übernommen, aber die Eigenschaft .Handle nicht, zumindest glauch ich dass es das ist, die Effekte werden jedenfalls nicht richtig disposed... Ich guck mir das mal weiter an..

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „kafffee“ ()

    ich würd das erstmal leserlich machen. Da taucht zigmal der Bandwurm-Ausdruck AngewendeteEffekte(i) auf - den könnte man in eine lokale Variable mit kurzem Namen tun, und die dann verwenden.
    AngewendeteEffekte(i) -> ae
    Wirst staunen, wie verständlich dir das dann vorkommt.


    VB.NET-Quellcode

    1. Dim index As Integer = AngewendeteEffekte.ToList.FindIndex(Function(x) x.ID = item.ID) 'wenn IDs übereinstimmen dann...
    2. If index <> -1 Then
    Das geht auch schöner, zB mit .FirstOrDefault()


    zum eiglichen Problem weissich nix, ich finde
    Klappt soweit auch ganz gut, bloss bei der Parameter-Übernahme habe ich noch Probleme. Diese werden nur übernommen, wenn mehrere UIs offen sind und dann auch nur das, was als letztes zugefügt wurde
    mir scheint, damit kann verschiedenerlei gemeint sein.

    @ErfinderDesRades

    Okay danke für die Tipps.

    Hast du meinen Edit schon gesehen? Ich bin mal auf die Idee gekommen, KopieAngewendeteEffekte rauszuschmeissen und stattdessen e.OldItems zu verwenden. Jetzt klappt das mit der Übernahme der PlugIn-Einstellungen, wenn ich aber Effekte entferne, werden diese hörbar nicht disposed:

    Ich muss mir das auch nochmal anschauen, ist ja jetzt schon etwass übersichtlicher...:

    VB.NET-Quellcode

    1. Public Sub InitilaisiereVSTEffekte(sender As Object, e As NotifyCollectionChangedEventArgs)
    2. Dim AlteItems As New List(Of ViewModel.PlugInsViewModel)
    3. If (e IsNot Nothing) AndAlso (e.OldItems IsNot Nothing) Then
    4. AlteItems = e.OldItems.Cast(Of ViewModel.PlugInsViewModel).ToList()
    5. For Each item In AlteItems
    6. For i = 0 To BassVst.BASS_VST_GetParamCount(item.Handle) - 1
    7. Dim Wert As Single = BassVst.BASS_VST_GetParam(item.Handle, i)
    8. Debug.WriteLine("GetParam: " & Bass.BASS_ErrorGetCode)
    9. item.Parameter.Add(Wert)
    10. Next
    11. BassVst.BASS_VST_EmbedEditor(item.Handle, Nothing)
    12. BassVst.BASS_VST_ChannelRemoveDSP(sourcechannel, item.Handle)
    13. Debug.WriteLine("RemoveDSP: " & Bass.BASS_ErrorGetCode)
    14. Next
    15. End If
    16. For i = AngewendeteEffekte.Count - 1 To 0 Step -1
    17. Dim ae As PlugInsViewModel = AngewendeteEffekte(i)
    18. ae.Handle = BassVst.BASS_VST_ChannelSetDSP(sourcechannel, ae.Pfad, 0, AngewendeteEffekte.Count - i)
    19. If ae.Parameter.Count = 0 Then
    20. BassVst.BASS_VST_SetParamRestoreDefaults(ae.Handle)
    21. Else
    22. For Each item In AlteItems
    23. If item.ID = ae.ID Then
    24. For j = 0 To item.Parameter.Count - 1
    25. BassVst.BASS_VST_SetParam(ae.Handle, j, ae.Parameter(j))
    26. Debug.WriteLine("SetParam: " & Bass.BASS_ErrorGetCode)
    27. Next
    28. End If
    29. Next
    30. End If
    31. If ae.UIOffen Then
    32. BassVst.BASS_VST_EmbedEditor(ae.Handle, ae.UIHandle(0))
    33. End If
    34. Next
    35. End Sub

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kafffee“ ()

    Okay hab die Lösung gefunden. Hab vergessen vor dem Einlesen der Parameter As List(Of Single) diese mit .Clear zu leeren... :)

    Hab schon nicht mehr geglaubt allein auf die Lösung zu kommen, mein Blick ist eher zufällig auf diese Zeile gestossen.

    Allerdings bleibe ich bei den KopieAngewendeteEfffekte weil mit e.oldItems immer nur ein Item ausgespuckt hat, warum das so ist da müsste ich eine Weile brutzeln, wahrscheinlich weil die mir nur geänderte und entfernte Items ausspuckt, nicht neu geaddete...
    @ErfinderDesRades

    Doch bei beiden hats bei mir in der nächsten Zeile gekracht, sonst hätte ich das auch so gemacht.

    Diese Sub wird so aufgerufen:

    AddHander AngewendeteEffekte.CollectionChanged, AddressOf InitialisereVSTEffekte()

    Ist so ein Event jeder ObservableCollection... Kennste?

    Und wenn du jetzt einen Datensatz addest, ist klar dass OldItems erstmal leer ist, weils ja vor der Änderung keine Items gab...