Autor Beitrag
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: So 23.04.17 23:26 
Ich habe auf die schnelle FlacBox gefunden. Damit kannst du FLAC lesen und schreiben.

@Frühlingsrolle: es geht hier nicht um Decoder, sondern Encoder. Einen Decoder bietet CSCore auch an. Aber zum schreiben braucht man einen Encoder.

Nachtrag
Vom Überfliegen des FlacBox Codes könntest du deine Aufnahme so speichern:

Samples -> WaveEncoder (CSCore) -> MemoryStream -> WaveOverFlacStream (FlacBox) -> Datei

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler


Zuletzt bearbeitet von C# am So 23.04.17 23:43, insgesamt 1-mal bearbeitet

Für diesen Beitrag haben gedankt: hydemarie
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: So 23.04.17 23:33 
- Nachträglich durch die Entwickler-Ecke gelöscht -

Für diesen Beitrag haben gedankt: hydemarie
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 413
Erhaltene Danke: 51



BeitragVerfasst: So 23.04.17 23:46 
FlacBox sieht mir nach einem toten Projekt aus. :(

Das ist alles ein ziemliches Gefrickel, wie mir scheint. CSCore selbst hätte Unterstützung für FFMPEG, aber nach Überfliegen des Quellcodes sieht das auch nur nach einem Decoder aus. Allmählich habe ich das Gefühl, das ist wieder mal so ein Anwendungsfall, den vor mir noch keiner hatte (oder lieber in C oder C++ umgesetzt hat)... :?
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: So 23.04.17 23:53 
- Nachträglich durch die Entwickler-Ecke gelöscht -
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 413
Erhaltene Danke: 51



BeitragVerfasst: So 23.04.17 23:54 
Haha, nein, keine Sorge - ich habe durchaus schon Gründe, zur Abwechslung mal was in C# zu machen. :)
Schlaf gut!
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: So 23.04.17 23:55 
Ja FlacBox ist tot, aber scheint ja zu funktionieren.

Dein Anwendungsfall ist auch etwas ungewöhnlich :D. Warum muss es denn Ogg oder FLAC sein? Das Ding ist halt, dass für die meisten Anwendungsfälle Wave und MP3 völlig ausreichen.
Mehr Codecs (Edit: bzw. Encoder) braucht man idR nur, wenn man DSP Programme schreibt. Und da ist C++ (oder jede andere unmanaged Sprache) einfach besser weil sie deutlich flexibler (und schneller) auf dem Speicher operieren kann.

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 413
Erhaltene Danke: 51



BeitragVerfasst: Mo 24.04.17 00:04 
user profile iconC# hat folgendes geschrieben Zum zitierten Posting springen:
Warum muss es denn Ogg oder FLAC sein?


Das sind tatsächlich die Audioformate, die ich am häufigsten verwende. Der MP3-Codec neigt nicht gerade zu filigranen Tönen. Bislang benutze ich dafür XRECODE, aber ich mache mir das Leben ja gerne einfacher.

user profile iconC# hat folgendes geschrieben Zum zitierten Posting springen:
Das Ding ist halt, dass für die meisten Anwendungsfälle Wave und MP3 völlig ausreichen.


Wie gesagt: Ich kann auch einfach einen WaveOutStream (?) nehmen und einen externen Konverter dahinterklemmen, das ist dann halt nur hässlich und irgendwie unpraktisch, finde ich. Aber wenn es nicht geht, dann geht es nicht. Damit muss ich dann klarkommen.

user profile iconC# hat folgendes geschrieben Zum zitierten Posting springen:
Und da ist C++ (oder jede andere unmanaged Sprache) einfach besser weil sie deutlich flexibler (und schneller) auf dem Speicher operieren kann.


Der kritische Teil hierbei dürfte das Auslesen vom Gerät sein, die Ausgabe in eine Datei kann ja durchaus mit beliebiger Verzögerung passieren. Ob das in der Praxis eine Rolle spielt, weiß ich nicht. Ich würde annehmen, dass die Core-Audio-Anbindung von CSCore halbwegs alltagstauglich (unmanaged?) ist. Ab welcher Eingabebitrate wird das eigentlich zu einem Problem?
Nachtrag: Ja, da scheint einiges "unmanaged" zu sein.
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Mo 24.04.17 05:17 
Also die CoreAudioAPI von CSCore ist ja einfach ein Wrapper zur WinApi bzw. WASAPI. Das Problem in C# ist, dass Arrays oft unnötig kopiert werden (z.B. beim Marshaling) und sehr viele Validierungen ablaufen. Wenn du z.B. auf ein Array zugreifst wird bei jedem Index geprüft, ob er in den Grenzen des Arrays liegt. Bist du außerhalb dieser Grenzen wird eine Exception geworfen. Bei C++ hingegen kannst du zugreifen wie du lustig bist. Wenn du den Index falsch berechnest stört sich C++ da nicht dran. Später bekommst du dann halt ne Heap Corruption oder ein Segmentation Fault.
Ein anderes Beispiel wäre auch das reinterpretieren des Speichers. Wenn du z.B. eine Funktion Record(byte[] buffer) hast, musst du bei C# auch ein Byte-Array übergeben. Wenn du aber weißt, dass dein Format z.B. float ist, muss das Byte-Array in ein Float-Array konvertiert werden, was bei C# idR dazu führt, dass das Array kopiert wird (man könnte natürlich auch mittels unsafe einen Float-Pointer drüber legen, aber das widerstrebt den Prinzipien von C#). In C++ kannst du einfach ein char-Pointer übergeben und mit reinterpret_cast<float*>(buffer) bekommst du dann dein Float-Pointer ohne irgendwas kopieren zu müssen.

Bei welchen Bitraten das Problematisch wird, hängt stark von deinem System ab:
- CPU Leistung
- RAM Geschwindigkeit
- HDD / SSD
- Soundkarte (unterstützte Sample Rates)

Üblich ist eine Sample Rate von 44.1kHz bzw 48kHz. Bei einer Stereoaufnahme mit 16 Bit kommst du schon auf eine Bitrate von 44.1 * 2 * 16 = 1411,2 kb/s (CD-Standard). Bei Studioaufnahmen mit 192kHz, Stereo und 24 Bit bist du dann sogar bei 192 * 2 * 24 = 9216 kb/s.

Angenommen du machst eine Aufnahme mit Standard CD Qualität, dann bekommst du alle 1/(44.1kHz)\approx 22µs zwei 16 Bit Werte (je einen für links und rechts bei Stereoaufnahmen). Das sollte C# noch locker verkraften.
Sehen wir uns nun die Studioqualität an: alle 1/(192kHz) \approx 5µs bekommst du zwei 24 Bit Werte.
Da Events und Methodenaufrufe auch einen ordentlichen Overhead verursachen, werden die Samples in den meisten Fällen intern gecached, sodass du z.B. alle 10ms ein 1.8kB (CD) bzw. 12kB (Studio) Array (kB = Kilobyte) bekommst, indem alle Samples der letzten 10ms abgelegt sind. Dieses Array muss nun reinterpretiert werden (z.B. uintushort oder float, damit die Codecs entsprechende Komprimierungen anwenden können (es sei denn du benutzt Wave, dann kannst du dein Byte-Array direkt in den Stream schieben).

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler

Für diesen Beitrag haben gedankt: hydemarie
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Mo 24.04.17 06:00 
Ich habe mal ein kleines Benchmark mit C# gemacht und bin zu einem erstaunlichen Ergebnis gekommen.
In dem Benchmark werden zufällige Samples als Byte-Array generiert. Anschließend findet eine Kovertierung zu einem Short-Array (Int16) statt und zuletzt wird noch eine kleine Funktion ausgeführt die über das Array iteriert und ein paar arithmetische Operationen durchführt (DoSomething(...) im Test Code).

Das Benchmark bietet folgende Parameter:
- SampleRate: die Sample Rate der fiktiven Aufnahme in Hz (z.B. 44100)
- ChannelCount: die Anzahl der Kanäle (1 für Mono, 2 für Stereo, 5+ für Sorround Sound)
- UpdateInterval: die Zeitspanne in der die Samples von dem Aufnahmegerät geliefert werden (z.B. alle 10ms kommt ein ganzes Paket von Samples)
- Duration: die Gesamtdauer eines Tests für eine einzelnes Konvertierungsverfahren

Verglichen werden drei Verfahren:
1. BitConverter: Jedes Sample wird einzeln in den BitConverter geworfen (siehe ConvertUsingBitConverter(...) im Test Code)
2. Marshaling: Es wird ein short-Array erzeugt, dann wird das byte-Array gepinnt und ein mittels Marshaling kopiert (siehe ConvertUsingMarshaling(...) im Test Code)
3. Unsafe Code: Das byte-Array wird gepinnt und ein Pointer darauf wird erstellt. Dieser wird an eine Wrapper-Klasse übergeben über diese dann auf dem Original-Array operiert werden kann (siehe ConvertUsingUnsafeCode(...) im Test Code)

Ich habe Tests für 44.1kHz und 192kHz gemacht (leider kann ich hier nur 44.1kHz zeigen, weil ein Beitrag maximal drei Anhänge haben kann :motz:)

mean44100 median44100 performance44100

Getestet wurde im Debug und Release Mode sowohl mit, als auch ohne Debugger. Das erste Diagramm zeigt die durchschnittliche Laufzeit für eine Konvertierung + DoSomething(...). Das zweite Diagramm zeigt den Median davon und das Dritte zeigt die "Over performance". Damit ist das Zeitverhältnis zwischen einem Update Interval und einer tatsächlichen Iteration der Testfunktion gemeint. Wenn z.B. ein Update Intervall von 10ms festgelegt ist und die Konvertierung schon nach 10µs abgeschlossen ist ergibt sich daraus eine "Over performance" von 10ms / 10µs = 1.000 = 100.000%. Würde die Konvertierung 10ms dauern, läge die "Over performance" bei 1,0 = 100%.

Hier ist der verwendete Code:
ausblenden volle Höhe 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:
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:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsolePlayground
{
    unsafe delegate void UnsafeOperation(short* samples, int size);

    class Program
    {
        // depth is fixed 16 bit (short)
        private const int SampleRate = 44100;

        private const int ChannelCount = 2;
        private static readonly TimeSpan UpdateInterval = TimeSpan.FromMilliseconds(10);

        private static readonly TimeSpan Duration = TimeSpan.FromSeconds(10);


        private static readonly Random Random = new Random();

        static void Main(string[] args)
        {
            Trace.Listeners.Clear();

            var twtl = new TextWriterTraceListener("../../Data/Log.txt")
            {
                Name = "TextLogger",
            };

            var ctl = new ConsoleTraceListener(false);

            Trace.Listeners.Add(twtl);
            Trace.Listeners.Add(ctl);
            Trace.AutoFlush = true;
            
            Task.Run(Run).Wait();
        }

        static async Task Run()
        {
            Trace.WriteLine($"================== Benchmark Begin ==================");
#if DEBUG
            Trace.WriteLine($"Mode:            {(Environment.Is64BitProcess ? "x64" : "x32")} Debug {(Debugger.IsAttached ? "+ Debugger" : "")}");
#else
            Trace.WriteLine($"Mode:            {(Environment.Is64BitProcess ? "x64" : "x32")} Release {(Debugger.IsAttached ? "+ Debugger" : "")}");
#endif

            Trace.WriteLine($"Sample Rate:     {SampleRate}Hz");
            Trace.WriteLine($"Channel Count:   {ChannelCount}");
            Trace.WriteLine($"Update Interval: {UpdateInterval.TotalMilliseconds}ms");
            Trace.WriteLine("");

            var sampleCount = (int) (UpdateInterval.TotalSeconds * SampleRate * sizeof(short) * ChannelCount);


            Trace.WriteLine($"Testing \"{nameof(ConvertUsingBitConverter)}\" for {Duration.TotalSeconds:f3}s...");

            var generator = GenerateSamples(sampleCount);
            var totalWatch = Stopwatch.StartNew();
            var watch = new Stopwatch();
            var records = new List<TimeSpan>((int) (Duration.Ticks / UpdateInterval.Ticks));

            while (totalWatch.Elapsed < Duration)
            {
                watch.Restart();
                var buffer = await generator;
                generator = GenerateSamples(sampleCount);

                var samples = ConvertUsingBitConverter(buffer);
                DoSomething(samples);
                watch.Stop();

                records.Add(watch.Elapsed);
            }

            PrintResults(totalWatch.Elapsed, records);


            Trace.WriteLine($"Testing \"{nameof(ConvertUsingMarshaling)}\" for {Duration.TotalSeconds:f3}s...");

            generator = GenerateSamples(sampleCount);
            totalWatch = Stopwatch.StartNew();
            records.Clear();

            while (totalWatch.Elapsed < Duration)
            {
                watch.Restart();
                var buffer = await generator;
                generator = GenerateSamples(sampleCount);

                var samples = ConvertUsingMarshaling(buffer);
                DoSomething(samples);
                watch.Stop();

                records.Add(watch.Elapsed);
            }

            PrintResults(totalWatch.Elapsed, records);


            Trace.WriteLine($"Testing \"{nameof(ConvertUsingUnsafeCode)}\" for {Duration.TotalSeconds:f3}s...");

            generator = GenerateSamples(sampleCount);
            totalWatch = Stopwatch.StartNew();
            records.Clear();


            while (totalWatch.Elapsed < Duration)
            {
                watch.Restart();
                var buffer = await generator;
                generator = GenerateSamples(sampleCount);

                unsafe
                {
                    ConvertUsingUnsafeCode(buffer, DoSomething);
                }

                watch.Stop();

                records.Add(watch.Elapsed);
            }

            PrintResults(totalWatch.Elapsed, records);

            Trace.WriteLine($"================== Benchmark End ==================");
            Trace.WriteLine("");

            if (Debugger.IsAttached)
            {
                Console.WriteLine("Press any key to exit...");
                Console.ReadKey(false);
            }
        }

        static void PrintResults(TimeSpan totalTime, List<TimeSpan> records)
        {
            Trace.WriteLine($"Actual Duration:  {totalTime.TotalMilliseconds,6:#}ms");
            Trace.WriteLine($"Max allowed time: {UpdateInterval.TotalMilliseconds * 1000,6:#}µs");
            Trace.WriteLine($"Mean time:        {records.Average(r => r.TotalMilliseconds) * 1000,6:#}µs");
            Trace.WriteLine($"Median time:      {records.OrderBy(r => r).ElementAt(records.Count / 2).TotalMilliseconds * 1000,6:#}µs");
            Trace.WriteLine(
                $"Min time:         {records.Min().TotalMilliseconds * 1000,6:#}µs at iteration {records.IndexOf(records.Min())}");
            Trace.WriteLine(
                $"Max time:         {records.Max().TotalMilliseconds * 1000,6:#}µs at iteration {records.IndexOf(records.Max())}");
            Trace.WriteLine($"Iterations:       {records.Count,6}");
            Trace.WriteLine($"Over performance: {records.Count / (Duration.Ticks / UpdateInterval.Ticks) * 100,6:#}%");
            Trace.WriteLine("");
        }

        static void DoSomething(short[] samples)
        {
            unchecked
            {
                for (int i = 1; i < samples.Length - 1; i++)
                {
                    samples[i] = (short) (samples[i - 1] ^ samples[i + 1]);
                    samples[i] *= (short) (samples[i] + 1220);
                    samples[i] <<= i % 4;
                    samples[i] >>= i % 3;
                }
            }
        }

        static unsafe void DoSomething(short* samples, int size)
        {
            unchecked
            {
                for (int i = 1; i < size - 1; i++)
                {
                    samples[i] = (short) (samples[i - 1] ^ samples[i + 1]);
                    samples[i] *= (short) (samples[i] + 1220);
                    samples[i] <<= i % 4;
                    samples[i] >>= i % 3;
                }
            }
        }

        static async Task<byte[]> GenerateSamples(int count)
        {
            return await Task.Run(() =>
            {
                var buffer = new byte[count];
                Random.NextBytes(buffer);
                return buffer;
            });
        }

        static short[] ConvertUsingBitConverter(byte[] buffer)
        {
            var result = new short[buffer.Length / sizeof(short)];

            for (int i = 0; i < result.Length; i++)
                result[i] = BitConverter.ToInt16(buffer, i * sizeof(short));

            return result;
        }

        static short[] ConvertUsingMarshaling(byte[] buffer)
        {
            var result = new short[buffer.Length / sizeof(short)];

            var pinnedArray = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            var pointer = pinnedArray.AddrOfPinnedObject();
            Marshal.Copy(pointer, result, 0, result.Length);
            pinnedArray.Free();

            return result;
        }
        
        static unsafe void ConvertUsingUnsafeCode(byte[] buffer, UnsafeOperation operation)
        {
            fixed (byte* ptr = buffer)
            {
                operation((short*) ptr, buffer.Length / sizeof(short));
            }
        }
    }
}


Update

Ich habe den Code leicht angepasst, um die Wrapper-Klasse für den unsafe Part zu eliminieren. Die Diagramme enthalten die neuen Ergebnisse des Benchmarks.
Einloggen, um Attachments anzusehen!
_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler


Zuletzt bearbeitet von C# am Mo 24.04.17 15:08, insgesamt 2-mal bearbeitet

Für diesen Beitrag haben gedankt: hydemarie
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 413
Erhaltene Danke: 51



BeitragVerfasst: Mo 24.04.17 09:12 
Weia, da hast du dir jetzt Arbeit gemacht. Danke!

Das heißt, dieser besonders fürsorgliche Umgang mit Datentypen ist performanter (typo korrigiert) als Zeigerschieben? Liegt das an C# oder an den Zeigern?
Ich rechne als Eingabe ungefähr mit einem Plattenspieler oder einem Mikrofon, das sollte also bei Weitem langen... :)


Zuletzt bearbeitet von hydemarie am Mo 24.04.17 14:49, insgesamt 1-mal bearbeitet
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Mo 24.04.17 14:46 
Kein Problem. Mich haben die Verhältnisse sowieso interessiert. Ich habe den Test Code etwas abgeändert und die neuen Diagramme hochgeladen. Sieht jetzt schon besser aus für den unsafe Part.

Ich vermute das der Flaschenhals bei den unmanaged-Operationen am pinnen liegt. Da .NET managed ist, werden auch mal Objekte im Speicher verschoben um die Speicherzugriffe zu optimieren. Sobald du jedoch ein Pointer verwendest, darf .NET das entsprechende Objekt nicht mehr anrühren, weil du ja über die Adresse darauf zugreifen willst. Ich vermute, dass genau dieser Teil relativ viel Performance kostet.

Was dein Plattenspieler angeht: da solltest du fein raus sein :D. Doch lass dich von dem Benchmark nicht täuschen. In der Realität wird es wahrscheinlich passieren, dass du mehr als einmal über die Samples iterieren musst. Außerdem wird der Callstack in den meisten fällen auch deutlich größer. Da kann sich das Komma bei den Werten schnell mal um ein paar Stellen verschieben.

Ich habe die Sample Rate mal um den Faktor 1000 erhöht (auf 44.100.000Hz). Die Werte sind zwar unrealistisch, aber man sieht wie unsafe mit sehr großen Arrays operiert.
performance

Die Over Performance liegt nun bei unter 100% beim managed Code, was bedeutet, dass hier die Samples schneller kommen als sie verarbeitet werden können. Der unsafe Part hat hingegen noch etwas Luft und ist jetzt fast doppelt so schnell wie die beiden managed Methoden.
Einloggen, um Attachments anzusehen!
_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler

Für diesen Beitrag haben gedankt: hydemarie
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 413
Erhaltene Danke: 51



BeitragVerfasst: Mo 24.04.17 14:50 
Gibt es überhaupt Audioeingabegeräte, die realistisch mit 44 MHz laufen? :shock:
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Mo 24.04.17 14:59 
Nein :D. Wie gesagt, dass war nur um das byte-Array mal exorbitant groß zu machen um managed vs unmanaged auf großen Arrays zu vergleichen (in diesem Fall ca. 1.7MB).

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler

Für diesen Beitrag haben gedankt: hydemarie
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 24.04.17 15:01 
- Nachträglich durch die Entwickler-Ecke gelöscht -
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Mo 24.04.17 15:11 
:nixweiss: ganz irrelevant ist es aber auch nicht. Geht ja um die Performance der Sample Konvertierung. Die ursprüngliche Frage ist ja schon lange geklärt.

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler

Für diesen Beitrag haben gedankt: hydemarie
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 413
Erhaltene Danke: 51



BeitragVerfasst: Mo 24.04.17 15:13 
Ich finde es für mein Problem eigentlich auch ganz hilfreich bis jetzt.

Für diesen Beitrag haben gedankt: C#