Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Sleep() | nieregularne odstępy czasu

Ostatnio zmodyfikowano 2017-03-02 13:08
Autor Wiadomość
Nazgul
Temat założony przez niniejszego użytkownika
Sleep() | nieregularne odstępy czasu
» 2017-02-20 21:16:43
Dzień dobry,
Nie mogłem znaleźć metronomu offline na komputer, więc postanowiłem sam napisać
(metronom to urządzenie służące do wystukiwania tempa (ew. rytmu) w muzyce. Żeby każdy wiedział o co chodzi.. Metronom Online)
Proste urządzenie sprawia mi trudność.. po zaimplementowaniu programu okazuje się, że czasami metronom nierówno pyka. Pewnie jest to spowodowane przywróceniem działania wątku.. jednak nie wiem jak rozwiązać ten problem;)

Kod programu:
C/C++
#include <Windows.h>

int main() {
    int click_frequency = 800;
    int click_duration = 80;
    double click_BPM = 60; // beat per minute(ilosc uderzen na minute)
   
    unsigned __int64 time_Frequency;
    unsigned __int64 time_KnockCountStart;
    unsigned __int64 time_KnockCountStop;
    unsigned __int64 expectedKnocks;
    unsigned __int64 resultKnock;
    QueryPerformanceFrequency( reinterpret_cast < LARGE_INTEGER *>( & time_Frequency ) );
    expectedKnocks = 60 * time_Frequency / click_BPM;
   
   
    while( true )
    {
        QueryPerformanceCounter( reinterpret_cast < LARGE_INTEGER *>( & time_KnockCountStart ) );
       
        Beep( click_frequency, click_duration );
       
        QueryPerformanceCounter( reinterpret_cast < LARGE_INTEGER *>( & time_KnockCountStop ) ); // STOP
        resultKnock = time_KnockCountStop - time_KnockCountStart;
        if( resultKnock < expectedKnocks )
             Sleep(( DWORD )((( double )( expectedKnocks - resultKnock ) /( double ) time_Frequency ) * 1000.0f ) );
       
    }
}
Będę wdzięczny za każdą radę;)
Podobny algorytm stosuję jak piszę grę, teraz się okazało, że to jednak nie jest tylko w mojej głowie, że obraz "skacze";)
P-158067
pekfos
» 2017-02-20 21:37:12
Sleep() nie nadaje się do gier i innych zastosowań wymagających precyzji, bo nie gwarantuje wznowienia wątku o czasie. Możesz liczyć najwyżej na to, że nie zostanie wznowiony wcześniej. Poczytaj jak jest realizowane odtwarzanie dźwięku, bo w gruncie rzeczy to właśnie chcesz zaimplementować, a jakoś muzyka odtwarza się płynnie niezależnie od obciążenia komputera (no może poza skrajnymi przypadkami).
P-158068
Nazgul
Temat założony przez niniejszego użytkownika
» 2017-02-20 21:49:36
Byłoby super jakby ktoś podał źródło, gdzie takie informacje znajdę, za samą wskazówkę jak szukać będę bardzo wdzięczny;D
Szczerze mówiąc wielokrotnie już szukałem takich informacji, jednak znajdywałem jedynie biblioteki do odtwarzania stricte dźwięku (np. z formatu wav),
a z tego co mówisz, to są sposoby na własnoręczne implementowane tego;D (pomijam to, że w assemblerze można wszystko implementować;) )

/EDIT
Wiem że można wygenerować strumień z dźwiękiem i go odpowiednio zapętlić, jednak szukam informacji jak 'spauzować' procesor zachowując precyzję
P-158070
mokrowski
» 2017-02-20 22:27:25
Możesz go wcale w tym wątku nie "pałzować". Bufor dźwięku wypełniaj próbką z dźwiękiem "puknięcia" o znanym czasie trwania oraz dźwiękiem ciszy także o znanym czasie trwania. Bufor uniemożliwi Ci przepełnienie a w konsekwencji zapewni regularny czas pracy metronomu.
P-158076
Nazgul
Temat założony przez niniejszego użytkownika
» 2017-02-20 23:18:48
No właśnie chodziło mi o to że szukam informacji jak pauzować z zachowaniem precyzji;D Przyda się chociażby przy pisaniu gry z ograniczeniem FPS;D
P-158081
j23
» 2017-02-20 23:50:37
W ograniczaniu FPS-ów nie musisz mieć jakiejś wielkiej precyzji, w przypadku metronomu już tak. Zrób tak, jak pisał mokrowski, albo użyj strumieni MIDI.
P-158085
Monika90
» 2017-02-21 22:25:00
Metronom można zrealizować przy użyciu dowolnej biblioteki audio, np. SFML
C/C++
#include <SFML/Audio.hpp>
#include <iostream>

class Biquad
{
public:
    Biquad( float b1, float b2 )
        : b1_( b1 )
         , b2_( b2 )
    { }
   
    float operator ()( float x )
    {
        const auto t = x + b1_ * d1_ + b2_ * d2_;
        const auto y = t - d2_;
        d2_ = d1_;
        d1_ = t;
        return y;
    }
   
private:
    float b1_, b2_;
    float d1_ = 0, d2_ = 0;
};

class Click
{
public:
    void click() { y_ = 0.0012; }
   
    float operator ()()
    {
        const auto y = y_;
        y_ = 1e - 10;
        return sections_[ 0 ]( sections_[ 1 ]( sections_[ 2 ]( y ) ) );
    }
   
private:
    float y_ = 0;
    Biquad sections_[ 3 ] = { { 1.97628, - 0.9801 }, { 1.9807, - 0.993012 }, { 1.95213, - 0.990025 } };
};

class Metronome
{
public:
    explicit Metronome( double rate, double bpm )
        : dt_( bpm / 60 / rate )
    {
        click_.click();
    }
   
    float operator ()()
    {
        t_ += dt_;
        if( t_ >= 1 )
        {
            t_ -= 1;
            click_.click();
        }
       
        return click_();
    }
   
private:
    double t_ = 0;
    double dt_;
    Click click_;
};

class Driver
    : public sf::SoundStream
{
public:
    Driver() { initialize( 1, rate_ ); }
    ~Driver() { stop(); }
   
private:
    bool onGetData( Chunk & chunk ) override
    {
        chunk.samples = buff_;
        chunk.sampleCount = size_;
        for( auto & x: buff_ )
             x = metronome_() * 9000;
       
        return true;
    }
   
    void onSeek( sf::Time ) override { }
   
    static constexpr int rate_ = 48000;
    static constexpr int size_ = 2048;
    sf::Int16 buff_[ size_ ];
    Metronome metronome_ { rate_, 72 };
};

int main()
{
    Driver d;
    d.play();
    std::cin.get();
}


UWAGA, tam gdzie w programie jest
1e - 10
 powinno być 1e-10, formater psuje kod.

edit: dopisałam destruktor do klasy Driver, żeby uniknąć UB
P-158122
mokrowski
» 2017-02-22 15:28:24
@Nagul, dalej nie sądzę abyś w tym przypadku tego potrzebował ale można "naiwnie" np. tak...
C/C++
#include <iostream>
#include <thread>
#include <chrono>

void showX() {
    std::putchar( 'X' );
    std::putchar( '\n' );
}
int main() {
    using namespace std;
    using namespace std::chrono_literals;
    for(;; ) {
        showX();
        std::this_thread::sleep_for( 1s );
    }
}
W tym przypadku będzie to oczekiwanie "nieco więcej niż 1 sec." bo należy doliczyć jeszcze czas pracy pętli.
Nieco dokładniej można tak... (ale dalej mało dokładnie)
C/C++
#include <iostream>
#include <thread>
#include <chrono>

void showX() {
    std::putchar( 'X' );
    std::putchar( '\n' );
}
int main() {
    using namespace std;
    using namespace std::chrono;
    using namespace std::chrono_literals;
    std::chrono::seconds sec( 1 );
    for(;; ) {
        showX();
        auto start = high_resolution_clock::now();
        auto time_point = start + sec;
        std::this_thread::sleep_until( time_point );
    }
}
A już bardzo dokładnie to szukaj w systemie. W systemach Posix masz alert i pokrewne dla settimer() ... Najlepiej uruchomić to w oddzielnym wątku i nie pozwalać na przełączenie kontekstu. Lub zdać się na określoną bibliotekę obsługi dźwięku.
P-158129
« 1 » 2
  Strona 1 z 2 Następna strona