Autor |
Beitrag |
C#
      
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: Do 26.12.13 23:28
Hey,
ich habe ma wieder ein Problem und zwar arbeite ich (wie schon in einigen Threads erwähnt  ) an einem SpectrumAnalyzer. Bis jetzt klappt alles relativ gut.
Die Daten verwerten klappt gut aber ich habe mit dem Anzeigen (Balkendiagramm) ein kleines Problem. Ich puffere die Audiodaten noch zusätzlich nach Zeit um einen glatteren Lauf der Balken zu erreichen. Wenn ich den Puffer einschalte (z.B. 0.1s) klappt dass auch soweit. Nach einiger Zeit (ca. 10s-20s) bekomm ich auf einmal eine Fehlermeldung aus heiterem Himmel in der markierten Zeile.
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| private void UpdateTimeBuffer() {
while (spectrumBufferTimes.Count > 0 && spectrumBufferTimes[0] + TimeBuffer < Environment.TickCount) { spectrumBufferTimes.RemoveAt(0); spectrumBuffer.RemoveAt(0); }
if (spectrumBuffer.Count <= 0) { CalculateBars(new float[BarsCount]); return; }
float[] f = new float[spectrumBuffer[0].Length];
ActualBufferSize = f.Length * 4 * spectrumBuffer.Capacity;
for (int i = 0; i < f.Length * spectrumBuffer.Count; i++) f[i / spectrumBuffer.Count] += spectrumBuffer[i % spectrumBuffer.Count][i / spectrumBuffer.Count] / spectrumBuffer.Count; CalculateBars(f); } |
Quelltext 1: 2: 3: 4: 5:
| NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. System.NullReferenceException was unhandled HResult=-2147467261 Message=Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. Source=MovTest |
Alle Arrays haben absolut gültige Werte und ich bin auch nirgends außerhalb des Index...
Kennt jemand das Problem oder weiß woher es kommt??? Ich hab absolut keinen Schimmer...
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Fr 27.12.13 01:16
Zitat: | Alle Arrays haben absolut gültige Werte und ich bin auch nirgends außerhalb des Index... |
Dann würdest du offensichtlich nicht diese Exception bekommen. In erster Instanz solltest du der Runtime trauen und nicht dir
Da du eine NullReferenceException und keine IndexOutOfRangeException bekommst bist du zumindest nicht außerhalb des Arraybereichs bleibt das der Inhalt eines deiner Array Zellen leer ist.
Hast du das überprüft? spectrumBuffer scheint ja ein jagged Array zu sein hast du denn die zweite Dimension initialisiert?
|
|
C# 
      
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: Fr 27.12.13 17:01
Ja die Runtime weiß besser als ich bescheid ich weiß
Ich finds nur komisch, dass wenn die Exception geworfen wird und ich bei dem aktuellen Index nachsehe ein Wert vorhanden ist.
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| public void AddValues(float[] spectrum) { if (TimeBuffer <= 0) CalculateBars(spectrum); else if (spectrum.Max() > 0.05f) { spectrumBuffer.Add(spectrum); spectrumBufferTimes.Add(Environment.TickCount); } } |
Anmerkung
Habe jetzt bei der oben erwähnet Funktion noch ne Prüfung auf float.IsInfinity() und float.IsNaN() gemacht. Dabei registrier ich ca. 1 ungültiges float-Array pro Sekunde (infinity), welches ich dann aber nicht zum spectrumBuffer hinzufüge. Jedoch erhalte ich trotzdem noch den Fehler mit der gleichen Wiederholfrequenz... Wenn ich die Exception abfange und in den Output schreibe, erhalte ich sie genau 1 mal ca. alle 10-20sec.
Ich bin verwirrt
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Fr 27.12.13 21:12
Hallo C#,
arbeitest du mit verschiedenen Threads?
|
|
C# 
      
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: Sa 28.12.13 20:44
Jaein. Ich benutze 2 DispatcherTimer. Einer für's Rendering mit 20ms / 25Hz und einer für die Pufferung mit 10ms / 100Hz. Durch Verwendung des Dispatchers bleibe ich doch auf dem UI Thread, bin also synchron, oder? Ich habs Versuchshlber mal mit lock probiert, gleiches Ergebnis...
Hier mal der komplette Code der Datei
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Runtime.Remoting.Contexts; using System.Threading; using System.Windows; using System.Windows.Forms; using System.Windows.Threading;
namespace MovTest.UI { public class AudioGraph : Control { public Brush Background { get; set; }
public Color MaximumValueColor { get { return _maximumValueColor; } set { foreground = new LinearGradientBrush(DisplayRectangle, value, BarColor, LinearGradientMode.Vertical); _maximumValueColor = value; } }
public Color BarColor { get { return _barColor; } set { foreground = new LinearGradientBrush(DisplayRectangle, MaximumValueColor, value, LinearGradientMode.Vertical); _barColor = value; } }
public int ActualBufferSize { get; private set; }
[DefaultValue(16)] public int BarsCount { get { return _barsCount; } set { _barsCount = value; UpdateValues(); InitTimeBuffer(); } }
[DefaultValue(4.0f)] public float Space { get { return _space; } set { _space = value; UpdateValues(); } }
[DefaultValue(1.0f)] public float VerticalScale { get; set; }
[DefaultValue(0)] public int TimeBuffer { get { return _timeBuffer; } set { if (value < 0 || value > 3000) return; _timeBuffer = value; InitTimeBuffer(); } }
private Color _maximumValueColor, _barColor; private int _timeBuffer; private int _barsCount; private float _space;
private float barWidth; private RectangleF[] bars; private Brush foreground; private List<float[]> spectrumBuffer; private List<int> spectrumBufferTimes;
private BufferedGraphicsContext graphicsContext; private BufferedGraphics graphics; private Graphics g;
private DispatcherTimer guiTrigger, bufferTrigger;
public AudioGraph() { guiTrigger = new DispatcherTimer(new TimeSpan(0, 0, 0, 0, 40), DispatcherPriority.Render, TriggerCallback, Dispatcher.CurrentDispatcher); bufferTrigger = new DispatcherTimer(new TimeSpan(0, 0, 0, 0, 10), DispatcherPriority.Normal, BufferTriggerCallback, Dispatcher.CurrentDispatcher);
VerticalScale = 1.0f; Space = 4.0f; BarsCount = 16; TimeBuffer = 0;
Width = 20; Height = 20;
BarColor = Color.Orange; MaximumValueColor = Color.Red; Background = new SolidBrush(DefaultBackColor); UpdateValues(); InitTimeBuffer(); }
~AudioGraph() { guiTrigger.Stop(); bufferTrigger.Stop(); guiTrigger = null; bufferTrigger = null; }
public void AddValues(float[] spectrum) { if (TimeBuffer <= 0) CalculateBars(spectrum); else { spectrumBuffer.Add(spectrum); spectrumBufferTimes.Add(Environment.TickCount); } }
private void InitTimeBuffer() { if (spectrumBuffer != null) { spectrumBuffer.Clear(); spectrumBufferTimes.Clear(); }
spectrumBuffer = new List<float[]>(); spectrumBufferTimes = new List<int>();
if (TimeBuffer == 0 && bufferTrigger.IsEnabled) bufferTrigger.Stop(); else if (!bufferTrigger.IsEnabled) bufferTrigger.Start(); }
private void InitBuffering() { if (Width == 0 || Height == 0) return;
guiTrigger.Stop();
if (graphics != null) { g.Dispose(); graphics.Dispose(); }
g = CreateGraphics(); graphicsContext = BufferedGraphicsManager.Current; graphicsContext.MaximumBuffer = new System.Drawing.Size(Width + 1, Height + 1); graphics = graphicsContext.Allocate(g, DisplayRectangle);
guiTrigger.Start(); }
private void TriggerCallback(object sender, EventArgs e) { OnPaint(new PaintEventArgs(g, DisplayRectangle)); }
private void BufferTriggerCallback(object sender, EventArgs e) { if (spectrumBuffer.Count > 0) UpdateTimeBuffer(); }
private void UpdateTimeBuffer() {
while (spectrumBufferTimes.Count > 0 && spectrumBufferTimes[0] + TimeBuffer < Environment.TickCount) { spectrumBufferTimes.RemoveAt(0); spectrumBuffer.RemoveAt(0); }
if (spectrumBuffer.Count <= 0) { CalculateBars(new float[BarsCount]); return; }
float[] f = new float[BarsCount];
ActualBufferSize = f.Length * 4 * spectrumBuffer.Capacity;
try { for (int i = 0; i < f.Length * spectrumBuffer.Count; i++) f[i / spectrumBuffer.Count] += spectrumBuffer[i % spectrumBuffer.Count][i / spectrumBuffer.Count] / spectrumBuffer.Count; } catch { Debug.WriteLine("Bars: {0}, Time: {1}, Length: {2}", BarsCount,TimeBuffer, spectrumBuffer.Count); }
CalculateBars(f); }
private void CalculateBars(float[] spectrum) { if (Width == 0 || Height == 0) return;
for (int i = 0; i < spectrum.Length; i++) { float height = Height * VerticalScale * spectrum[i]; bars[i] = new RectangleF((barWidth + Space) * i, Height - height, barWidth, height); }
}
private void UpdateValues() { if (Width == 0 || Height == 0) return;
barWidth = (Width - (BarsCount - 1) * Space) / BarsCount; bars = new RectangleF[BarsCount]; InitBuffering(); foreground = new LinearGradientBrush(DisplayRectangle, MaximumValueColor, BarColor, LinearGradientMode.Vertical); }
protected override void OnResize(EventArgs e) { base.OnResize(e); UpdateValues(); }
protected override void OnPaint(PaintEventArgs e) { if (Width == 0 || Height == 0 || IsDisposed) return;
lock (this) { graphics.Graphics.Clear(BackColor); graphics.Graphics.FillRectangle(Background, DisplayRectangle); graphics.Graphics.FillRectangles(foreground, bars); graphics.Render(); } } } } |
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: So 29.12.13 13:04
Hallo,
da sind einige generelle Dinge, die mir bei deinem Code auffallen:
- der DispatcherTimer ist ja eigentlich für WPF gedacht, warum nimmst du nicht den System.Windows.Forms.Timer?
- statt dem Finalizer ~AudioGraph solltest du besser die Dispose()-Methode überschreiben, s.a. Dispose Pattern
- warum benutzt du die BuffereGraphics-Klasse? Es ist viel einfacher mittels SetStyle(ControlStyles.OptimizedDoubleBuffer, true) DoubleBuffering einzuschalten, s. Hinweis unter BufferedGraphics-Klasse. Und so kannst (und solltest) du auch im OnPaint direkt auf e.Graphics zugreifen (anstatt CreateGraphics zu benutzen - und die Member g und graphics wären dann auch überflüssig).
|
|
C# 
      
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: So 29.12.13 15:13
Danke erstmal für den Tipp. Ich habe jetzt den DispatcherTimer ersetzt und bei OnPaint e.Graphics verwendet.
Wenn ich SetStyle(ControlStyles.OptimizedDoubleBuffer, true) verwende, habe ich ein viel stärkeres Bildflimmern als wenn ich manual buffering verwende...
Mein Problem mit der NullReferenceException nicht zu vergessen...
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: So 29.12.13 15:39
Hallo,
ich hatte gehofft, daß die NullReferenceException verschwindet bei Verwendung des WinForm-Timers. Aber schau mal, wenn die Exception geworfen wurde, im VS unter "Threads" nach, ob nicht doch ein anderer Thread dafür verantwortlich ist.
Und du bist dir sicher, daß die Berechnungsformel bei dir so stimmt (also du die richtigen Indizes verwendet etc.)? Evtl. einfach mal in mehrere Zeilen aufteilen, um dem Fehler auf die Spur zu kommen.
Und bzgl. Flackern schau mal unter [Artikel] Flackernde Controls und flackerndes Zeichnen vermeiden nach, dort sind noch ein paar andere Flags aufgelistet.
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: So 29.12.13 15:47
Habe deinen Control mal auf eine Form geworfen und mit Zufallswerten befüllt. Da knallt nix
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| private static Random rnd = new Random(DateTime.Now.Millisecond); private void button1_Click(object sender, EventArgs e) { while(true) { float[] f = new float[rnd.Next(1,audioGraph1.BarsCount)]; for (int i = 0; i < f.Length; i++) f[i] = (float)rnd.NextDouble(); audioGraph1.AddValues(f); Application.DoEvents(); } } |
Edit : Ich korrigiere es gibt eine IndexOutOfRangeException wenn die Länge der Spektren kleiner BarsCount ist. Das solltest du explizit abfangen. Weiterhin keine NullReferenceException.
|
|
C# 
      
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: So 29.12.13 16:12
Och mann was soll n des jetzt xD
Ja das mit den BarsCount und Pufferlänge ist mir bekannt. Ist ja noch keine final version  . Ich prüfe das momentan noch außerhalb, sodass keine falschen Werte übertragen werden.
Also der Thread der auslöst ist der MainThread. Bei der Exception überprüfe ich die Werte in den Arrays da ist alles ok damit... Und ein Berechnungsfehler kanns eigentlich auch nicht sein, da das ganze ja in 99,9% der Fälle funktioniert. Ich habe ja geschätzt 2000 Arrays pro Sekunde durch den spectrumBuffer laufen und die Ausnahme tritt ja erst nach ca 10 Sekunden auf...
Das mit dem Flickering schau ich mir noch genauer an.
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Palladin007
      
Beiträge: 1282
Erhaltene Danke: 182
Windows 11 x64 Pro
C# (Visual Studio Preview)
|
Verfasst: So 29.12.13 19:22
Nur so ein ganz spontaner Gedanke, aber ich hab in meiner Anfangszeit in der Programmier-Welt viel mit Batch rum gebastelt und bei einem kleinen "Projekt" fest gestellt, dass sich bei Rechenaufwendigen Aufgaben selten auch mal ein Fehler einschleicht.
Ich hab damals auf einem anderen Forum gefragt und wir haben alle gemeinsam den Code doppelt und dreifach durchforstet nur um fest zu stellen, dass es keinen Fehler gibt. Irgendwann meinte dann einer, dass eventuell bei sehr aufwendigen Aufgaben auch mal ein Fehler in der Berechnung passieren kann, was dann nichts mit dem Code zu tun hat.
Ich komme gerade darauf, weil du hier irgendwas schreibst, das mir sehr nach mathematisch aufwendigen Berechnungen klingt und du sagst, rund 2000 Arrays ...
Eventuell ist das ja gar nicht dein Problem, sondern du überforderst irgendwas dazwischen, dass da ein fehler passiert.
Du kannst ja mal probieren, zwischen jedem Array weniger Millisekunden zu warten. Nicht zu viel, sonst fällt das in der Gesamt-Dauer zu deutlich auf.
Ob das funktioniert kann ich dir nicht sagen, es war nur eine spontane Idee. ^^
PS:
Zitat: | Ja das mit den BarsCount und Pufferlänge ist mir bekannt. Ist ja noch keine final version |
Bitte mach das nicht ^^
Ich persönlich versuche immer darauf zu achten, mögliche Exceptions so früh wie möglich abzufangen oder zumindest dick und fett im Code zu markieren, damit ich das nicht vergesse.
Hatte nämlich schon manchmal den Fall, dass ich genauso gedacht habe, wie du jetzt und es dann vergessen habe. Das Suchen nach dem Fehler hat am Ende dann länger gedauert als das vorherige Abfangen. ^^
|
|
|