Yardım Programımda Çözülemeyen Hesaplama Hataları Oluşuyor

KindElephant480

Asistan
Katılım
14 Eylül 2017
Mesajlar
363
Reaksiyon puanı
44
Puanları
28
433167396-5170729c-cd2e-47c6-aa82-3231f732ece9.png

1 yıldan fazla bir süredir NeoBleeper isimli SomethingUnreal kullanıcı adına sahip eski bir YouTuber'ın VB6 kullanarak geliştirdiği ve bilgisayarların anakartlarında bulunan sistem hoparlörü (normalde bilgisayarların sadece bip sesi çıkarması için kullanılan bir çeşit hoparlör) ile melodi çalmayı sağlayan Bleeper Music Maker programının C# dili ile yeniden yazdığım ve yapay zekâ destekli resimdeki yeniden yapımı olan bir program geliştiriyorum ancak listedeki notaları çalma özelliğini çalışır duruma getirdiğimden bu yana aşırı yavaş çalınma gibi çeşitli sorunlar yaşadım, bir süre sonra pencereyi gizleyene kadar aşırı yavaş çalınma sorunlarını çözdüm ancak videodaki gibi notaların uzunluklarının senkronize oynatmada senkronizasyonu bozacak şekilde hatalı hesaplandığını fark ettim ve sorunu System.Threading.Timers.Timer kullanan ve arayüz dondurmayan bekleme metodumdaki Timer'ı Stopwatch ile değiştirerek büyük oranda çözdüm.


Ancak videolardaki gibi iki programı karşılaştırdığımda orijinal Bleeper Music Maker programında notalar daha hızlı çalınırken NeoBleeper programımda orijinal Bleeper Music Maker programına göre daha yavaş çalındığını fark ettim ve bu kodlardaki yöntemler üzerinde sonuçları kesme veya yuvarlama işlemleri ve vuruş uzunluğu hesaplamaları konusunda denemeler yaptım ancak hiçbir sonuca ulaşamıyorum.



C#:
private async void play_music(int startIndex)
        {
            bool nonStopping = false;
            EnableDisableCommonControls(false);

            while (listViewNotes.SelectedItems.Count > 0 && is_music_playing)
            {
                nonStopping = trackBar_note_silence_ratio.Value == 100;

                // Calculating note length and line length based on BPM
                decimal millisecondsPerWholeNote = 0m;
                if (Variables.bpm > 0)
                {
                    // 60,000 ms / BPM = whole note duration in milliseconds / 240,000 ms = whole note duration in milliseconds for 4/4 time signature
                    millisecondsPerWholeNote = 240000m / Variables.bpm;
                }

                // Note length and line length calculators
                int noteLength = note_length_calculator((double)millisecondsPerWholeNote);
                int lineLength = line_length_calculator((double)millisecondsPerWholeNote);

                // Note duration and wait duration calculations should be at least 1 ms
                int calculatedNoteDuration = Math.Max(1, noteLength);
                // Line length should also be at least 1 ms
                int calculatedWaitDuration = Math.Max(calculatedNoteDuration, lineLength);

                // Silence duration calculation
                int silenceDuration = calculatedWaitDuration - calculatedNoteDuration;

                // Play with MIDI output if enabled
                if (Program.MIDIDevices.useMIDIoutput)
                {
                    play_note_in_line_from_MIDIOutput(
                        listViewNotes.SelectedIndices[0],
                        checkBox_play_note1_played.Checked,
                        checkBox_play_note2_played.Checked,
                        checkBox_play_note3_played.Checked,
                        checkBox_play_note4_played.Checked,
                        calculatedNoteDuration
                    );
                }

                // Regular note playing logic
                play_note_in_line(
                    checkBox_play_note1_played.Checked,
                    checkBox_play_note2_played.Checked,
                    checkBox_play_note3_played.Checked,
                    checkBox_play_note4_played.Checked,
                    calculatedNoteDuration,
                    nonStopping
                );

                // Apply the note length to the selected line in the ListView
                if (silenceDuration > 0 && !nonStopping)
                {
                    UpdateLabelVisible(false); // Hide the label
                    NonBlockingSleep.Sleep(silenceDuration);
                }

                // Select the next line in the ListView
                await UpdateListViewSelectionSync(startIndex);
            }

            // Clean up after playing
            if (nonStopping)
            {
                stopAllNotesAfterPlaying();
            }

            EnableDisableCommonControls(true);
        }

private int note_length_calculator(double msPerWholeNote)
        {
            if (listViewNotes.SelectedItems == null || listViewNotes.SelectedItems.Count == 0 ||
                listViewNotes.Items == null || listViewNotes.Items.Count == 0)
            {
                return 0;
            }

            int selectedLine = listViewNotes.SelectedIndices[0];
            string noteType = listViewNotes.Items[selectedLine].SubItems[0].Text;
            string modifier = listViewNotes.Items[selectedLine].SubItems[5].Text;
            string articulation = listViewNotes.Items[selectedLine].SubItems[6].Text;

            // Calculate the basic note length based on the note type
            decimal noteFraction = noteType switch
            {
                "Whole" => 1m,      // Whole note
                "Half" => 0.5m,     // Half note
                "Quarter" => 0.25m, // Quarter note
                "1/8" => 0.125m,    // 1/8 note
                "1/16" => 0.0625m,  // 1/16 note
                "1/32" => 0.03125m, // 1/32 note
                _ => 0.25m,         // Default: Quarter note
            };

            // Modifier factor (Dot, Triplet)
            decimal modifierFactor = 1m;
            if (!string.IsNullOrEmpty(modifier))
            {
                if (modifier.ToLowerInvariant().Contains("dot"))
                    modifierFactor = 1.5m; // Dotted note: 1.5x length
                else if (modifier.ToLowerInvariant().Contains("tri"))
                    modifierFactor = 1m / 3m; // Triplet: 1/3x length
            }

            // Articulation factor - only affects fermata
            decimal articulationFactor = 1m;
            if (!string.IsNullOrEmpty(articulation))
            {
                if (articulation.ToLowerInvariant().Contains("sta"))
                    articulationFactor = 0.5m;    // Staccato: 0.5x length
                else if (articulation.ToLowerInvariant().Contains("spi"))
                    articulationFactor = 0.25m;   // Spiccato: 0.25x length
                else if (articulation.ToLowerInvariant().Contains("fer"))
                    articulationFactor = 2m;      // Fermata: 2x length
            }

            // Note-silence ratio (from trackBar)
            decimal silenceRatio = (decimal)trackBar_note_silence_ratio.Value / 100m;

            // Calculate the total note length
            decimal result = (decimal)msPerWholeNote * noteFraction * modifierFactor * articulationFactor * silenceRatio;

            // Should be at least 1 ms
            return Math.Max(1, (int)Math.Round(result, MidpointRounding.AwayFromZero));
        }

private int line_length_calculator(double msPerWholeNote)
        {
            if (listViewNotes.SelectedItems == null || listViewNotes.SelectedItems.Count == 0 ||
                listViewNotes.Items == null || listViewNotes.Items.Count == 0)
            {
                return 0;
            }

            int selectedLine = listViewNotes.SelectedIndices[0];
            string noteType = listViewNotes.Items[selectedLine].SubItems[0].Text;
            string modifier = listViewNotes.Items[selectedLine].SubItems[5].Text;
            string articulation = listViewNotes.Items[selectedLine].SubItems[6].Text;

            // Calculate the basic note length based on the note type
            decimal noteFraction = noteType switch
            {
                "Whole" => 1m,      // Whole note
                "Half" => 0.5m,     // Half note
                "Quarter" => 0.25m, // Quarter note
                "1/8" => 0.125m,    // 1/8 note
                "1/16" => 0.0625m,  // 1/16 note
                "1/32" => 0.03125m, // 1/32 note
                _ => 0.25m,         // Default: Quarter note
            };

            // Modifier factor (Dot, Triplet)
            decimal modifierFactor = 1m;
            if (!string.IsNullOrEmpty(modifier))
            {
                if (modifier.ToLowerInvariant().Contains("dot"))
                    modifierFactor = 1.5m; // Dotted: 1.5x length
                else if (modifier.ToLowerInvariant().Contains("tri"))
                    modifierFactor = 1m / 3m; // Triplet: 1/3x length
            }

            // Articulation factor - only affects fermata
            decimal articulationFactor = 1m;
            if (!string.IsNullOrEmpty(articulation) && articulation.ToLowerInvariant().Contains("fer"))
            {
                articulationFactor = 2m; // Fermata: 2x length
            }
            // Staccato and Spiccato do not affect line length

            // Calculate the total line length (without note-silence ratio)
            decimal result = (decimal)msPerWholeNote * noteFraction * modifierFactor * articulationFactor;

            // Should be at least 1 ms
            return Math.Max(1, (int)Math.Round(result, MidpointRounding.AwayFromZero));
        }

Sizce bu durumda programımın kodundaki hesaplama hataları hangi kısımlarda neden kaynaklanıyor olabilir?
Merak edenler için programımın GitHub sayfasının bağlantısı: NeoBleeper GitHub sayfası
 
Son düzenleme:

crow88

İçerik Ekibi
İçerik Ekibi
Katılım
1 Aralık 2010
Mesajlar
6,838
Çözümler
1
Reaksiyon puanı
3,340
Puanları
1,358
Yaş
59
merhaba
sadece fikrimi belirtmek istiyorum(1989 90) yıllarında basıc cobol fortran eğitimi aldım,uzman değilim))
1-biliyorsunuz program içinde döngüler var,bu döngülerden bazıları program içinde çakışma yapabilir
2-akış algoritmasında programın çalışmasını engellebilecek kod vardır,buda gözünüzden kaçmış olabilir
biliyorsunuzki belli aşamadan sonra gözleriniz,ufak nokta hatalarınıda görmez oluyor,şöyle söyleyim,program bu ufak hatayı görüyor ama engel olmayıp kabul ediyor,buda hataya sebep olabiliyor.
3-üst yazınızda hesaplama hatalarını bulduğunuzu söylediniz,bence bölüm bölüm yeniden derleme yaparak çalıştırmaniz,hatayı bu şekilde bulma imkanınız olabilir
kolay gelsin
 

Alois Alzheimer

Müdavim
Müdavim
Katılım
18 Ocak 2019
Mesajlar
1,702
Reaksiyon puanı
2,315
Puanları
358
Yaş
31
İyi derecede C++ ve Delphi bilen birisi olarak orijinal uygulamanın ve senin yaptığın uygulamanın arayüzlerini karşılaştırdım.

Sanki yeni sürümünü yapmış gibisin, çok güzel olmuş.
 

KindElephant480

Asistan
Katılım
14 Eylül 2017
Mesajlar
363
Reaksiyon puanı
44
Puanları
28
İyi derecede C++ ve Delphi bilen birisi olarak orijinal uygulamanın ve senin yaptığın uygulamanın arayüzlerini karşılaştırdım.

Sanki yeni sürümünü yapmış gibisin, çok güzel olmuş.
Ancak her ne kadar arayüzü benziyor ve bahsettiğim hesaplama hatası neredeyse fark edilmeyecek kadar çok küçük bir hata olsa da bu hata senkronize oynatmada senkronizasyonu birkaç ölçü bozuyor, Math.Round ile farklı orta nokta yuvarlaması stratejileri (AwayFromZero, ToZero, ToEven gibi), Math.Truncate ve Math.Floor yöntemlerini denesem de sonuç hepsinde neredeyse aynıydı ve orijinal program açık kaynak kodlu olmadığı için kendim yazmak zorunda kaldım (çünkü orijinal program VB6 ile yazılmış ve orijinal programı geri derlediğimde çıkan kodlar Assembly dilinde ya da anlamsız isimlere sahip).
 

Alois Alzheimer

Müdavim
Müdavim
Katılım
18 Ocak 2019
Mesajlar
1,702
Reaksiyon puanı
2,315
Puanları
358
Yaş
31
Ancak her ne kadar arayüzü benziyor ve bahsettiğim hesaplama hatası neredeyse fark edilmeyecek kadar çok küçük bir hata olsa da bu hata senkronize oynatmada senkronizasyonu birkaç ölçü bozuyor, Math.Round ile farklı orta nokta yuvarlaması stratejileri (AwayFromZero, ToZero, ToEven gibi), Math.Truncate ve Math.Floor yöntemlerini denesem de sonuç hepsinde neredeyse aynıydı ve orijinal program açık kaynak kodlu olmadığı için kendim yazmak zorunda kaldım (çünkü orijinal program VB6 ile yazılmış ve orijinal programı geri derlediğimde çıkan kodlar Assembly dilinde ya da anlamsız isimlere sahip).
Managed dillerle yazılmış programlar dnSpy ile çözülebilir diye biliyorum.
 

KindElephant480

Asistan
Katılım
14 Eylül 2017
Mesajlar
363
Reaksiyon puanı
44
Puanları
28
Managed dillerle yazılmış programlar dnSpy ile çözülebilir diye biliyorum.
Ancak dnSpy ile bile çözülemiyor çünkü .NET tabanlı değil, MSCOMCTL.OCX (ActiveX) tabanlı bir program ve bu nedenle tamamen sıfırdan geliştirmek ve bazı ince detaylarda yapay zekadan yardım almak zorunda kaldım.
Mesaj otomatik birleştirildi:

Ayrıca bu şekilde sorunu kısmen çözdüm ancak nota uzunlukları hâlen istediğim gibi değil

C#:
private async void play_music(int startIndex)
        {
            bool nonStopping = false;
            EnableDisableCommonControls(false);

            while (listViewNotes.SelectedItems.Count > 0 && is_music_playing)
            {
                nonStopping = trackBar_note_silence_ratio.Value == 100;

                // Calculating note length and line length based on BPM
                decimal millisecondsPerWholeNote = 0m;
                if (Variables.bpm > 0)
                {
                    // 60,000 ms / BPM = whole note duration in milliseconds / 240,000 ms = whole note duration in milliseconds for 4/4 time signature
                    millisecondsPerWholeNote = Math.Truncate(240000m / Variables.bpm);
                }

                // Note length and line length calculators
                int noteLength = note_length_calculator(millisecondsPerWholeNote);
                int lineLength = line_length_calculator(millisecondsPerWholeNote);

                // Note duration and wait duration calculations should be at least 1 ms
                int calculatedNoteDuration = Math.Max(1, noteLength);
                // Line length should also be at least 1 ms
                int calculatedWaitDuration = Math.Max(calculatedNoteDuration, lineLength);

                // Silence duration calculation
                int silenceDuration = calculatedWaitDuration - calculatedNoteDuration;

                // Play with MIDI output if enabled
                if (Program.MIDIDevices.useMIDIoutput)
                {
                    play_note_in_line_from_MIDIOutput(
                        listViewNotes.SelectedIndices[0],
                        checkBox_play_note1_played.Checked,
                        checkBox_play_note2_played.Checked,
                        checkBox_play_note3_played.Checked,
                        checkBox_play_note4_played.Checked,
                        calculatedNoteDuration
                    );
                }

                // Regular note playing logic
                play_note_in_line(
                    checkBox_play_note1_played.Checked,
                    checkBox_play_note2_played.Checked,
                    checkBox_play_note3_played.Checked,
                    checkBox_play_note4_played.Checked,
                    calculatedNoteDuration,
                    nonStopping
                );

                // Apply the note length to the selected line in the ListView
                if (silenceDuration > 0 && !nonStopping)
                {
                    UpdateLabelVisible(false); // Hide the label
                    NonBlockingSleep.Sleep(silenceDuration);
                }

                // Select the next line in the ListView
                await UpdateListViewSelectionSync(startIndex);
            }

            // Clean up after playing
            if (nonStopping)
            {
                stopAllNotesAfterPlaying();
            }

            EnableDisableCommonControls(true);
        }

private int note_length_calculator(decimal msPerWholeNote)
        {
            if (listViewNotes.SelectedItems == null || listViewNotes.SelectedItems.Count == 0 ||
                listViewNotes.Items == null || listViewNotes.Items.Count == 0)
            {
                return 0;
            }

            int selectedLine = listViewNotes.SelectedIndices[0];
            string noteType = listViewNotes.Items[selectedLine].SubItems[0].Text;
            string modifier = listViewNotes.Items[selectedLine].SubItems[5].Text;
            string articulation = listViewNotes.Items[selectedLine].SubItems[6].Text;

            // Calculate the basic note length based on the note type
            decimal noteFraction = noteType switch
            {
                "Whole" => 1m,      // Whole note
                "Half" => 0.5m,     // Half note
                "Quarter" => 0.25m, // Quarter note
                "1/8" => 0.125m,    // 1/8 note
                "1/16" => 0.0625m,  // 1/16 note
                "1/32" => 0.03125m, // 1/32 note
                _ => 0.25m,         // Default: Quarter note
            };

            // Modifier factor (Dot, Triplet)
            decimal modifierFactor = 1m;
            if (!string.IsNullOrEmpty(modifier))
            {
                if (modifier.ToLowerInvariant().Contains("dot"))
                    modifierFactor = 1.5m; // Dotted note: 1.5x length
                else if (modifier.ToLowerInvariant().Contains("tri"))
                    modifierFactor = 1m / 3m; // Triplet: 1/3x length
            }

            // Articulation factor - only affects fermata
            decimal articulationFactor = 1m;
            if (!string.IsNullOrEmpty(articulation))
            {
                if (articulation.ToLowerInvariant().Contains("sta"))
                    articulationFactor = 0.5m;    // Staccato: 0.5x length
                else if (articulation.ToLowerInvariant().Contains("spi"))
                    articulationFactor = 0.25m;   // Spiccato: 0.25x length
                else if (articulation.ToLowerInvariant().Contains("fer"))
                    articulationFactor = 2m;      // Fermata: 2x length
            }

            // Note-silence ratio (from trackBar)
            decimal silenceRatio = trackBar_note_silence_ratio.Value / 100m;

            // Calculate the total note length
            decimal result = msPerWholeNote;
            result = Math.Truncate(result * noteFraction);
            result = Math.Truncate(result * modifierFactor);
            result = Math.Truncate(result * articulationFactor);
            result = Math.Truncate(result * silenceRatio);

            // Should be at least 1 ms
            return Math.Max(1, (int)result);
        }

private int line_length_calculator(decimal msPerWholeNote)
        {
            if (listViewNotes.SelectedItems == null || listViewNotes.SelectedItems.Count == 0 ||
                listViewNotes.Items == null || listViewNotes.Items.Count == 0)
            {
                return 0;
            }

            int selectedLine = listViewNotes.SelectedIndices[0];
            string noteType = listViewNotes.Items[selectedLine].SubItems[0].Text;
            string modifier = listViewNotes.Items[selectedLine].SubItems[5].Text;
            string articulation = listViewNotes.Items[selectedLine].SubItems[6].Text;

            // Calculate the basic note length based on the note type
            decimal noteFraction = noteType switch
            {
                "Whole" => 1m,      // Whole note
                "Half" => 0.5m,     // Half note
                "Quarter" => 0.25m, // Quarter note
                "1/8" => 0.125m,    // 1/8 note
                "1/16" => 0.0625m,  // 1/16 note
                "1/32" => 0.03125m, // 1/32 note
                _ => 0.25m,         // Default: Quarter note
            };

            // Modifier factor (Dot, Triplet)
            decimal modifierFactor = 1m;
            if (!string.IsNullOrEmpty(modifier))
            {
                if (modifier.ToLowerInvariant().Contains("dot"))
                    modifierFactor = 1.5m; // Dotted: 1.5x length
                else if (modifier.ToLowerInvariant().Contains("tri"))
                    modifierFactor = 1m / 3m; // Triplet: 1/3x length
            }

            // Articulation factor - only affects fermata
            decimal articulationFactor = 1m;
            if (!string.IsNullOrEmpty(articulation) && articulation.ToLowerInvariant().Contains("fer"))
            {
                articulationFactor = 2m; // Fermata: 2x length
            }
            // Staccato and Spiccato do not affect line length

            // Calculate the total line length (without note-silence ratio)
            decimal result = msPerWholeNote;
            result = Math.Truncate(result * noteFraction);
            result = Math.Truncate(result * modifierFactor);
            result = Math.Truncate(result * articulationFactor);

            // Should be at least 1 ms
            return Math.Max(1, (int)result);
        }
 
Son düzenleme:
Üst