203 using WallClock = std::chrono::high_resolution_clock;
204 using SteadyClock = std::chrono::steady_clock;
205 using TimePoint = std::chrono::time_point<WallClock>;
206 using Milliseconds = std::chrono::milliseconds;
210 using TimerMap = std::map<TimeoutPublisherHandle, TimeOffset>;
211 using TimerIterator = TimerMap::iterator;
220 bool SleepTillBrooklyn()
222 if (mTimerMap.size() > 0)
224 TimerIterator minIt = GetNextTimeout();
226 TimePoint deadlineTp = TimePoint(Milliseconds(deadline));
227 std::this_thread::sleep_until(deadlineTp);
229 if (minIt->first == mMetronomeId)
232 SetTimeout(mMetronomeTimerPeriod, mMetronomeId);
237 TimeoutExpired(minIt->first);
238 mTimerMap.erase(minIt);
249 return std::chrono::duration_cast<Milliseconds>(WallClock::now().time_since_epoch())
254 return std::chrono::duration_cast<Milliseconds>(SteadyClock::now().time_since_epoch())
260 mTimerMap[aTimerId] = aMillisecondsFromNow + GetMonotonicTime();
264 void StartMetronome(
const TimeOffset aPeriodInMilliseconds)
266 mMetronomeId = GetUniqueTimerHandle();
267 mMetronomeTimerPeriod = aPeriodInMilliseconds;
268 SetTimeout(mMetronomeTimerPeriod, mMetronomeId);
271 void CancelMetronome() { Cancel(mMetronomeId); }
275 TimerIterator GetNextTimeout()
277 return std::min_element(mTimerMap.begin(), mTimerMap.end(),
278 [](
const TimerMap::value_type& t1,
const TimerMap::value_type& t2) ->
bool {
279 return (t1.second < t2.second);
284 std::map<TimeoutPublisherHandle, TimeOffset> mTimerMap;
298 SongLine() : line() {}
299 SongLine(
const std::string& aLine) : line(aLine) {}
300 friend std::ostream& operator<<(std::ostream& os, SongLine s) {
return os << s.line; }
306 SongThemeState(
const std::string& aLine) : SongLine(aLine) {}
312 DontThemeState(
const std::string& aLine) : SongLine(aLine) {}
318 RememberState(
const std::string& aLine) : SongLine(aLine) {}
324 ThenYouState(
const std::string& aLine) : SongLine(aLine) {}
330 PlaybackState(
bool aAutoplay =
false) : autoplay(aAutoplay) {}
347 {
" ... better faster stronger ... ", 40},
349 {
"So let it out and let it in", 2},
352 , kThemeSequence({0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 6, 0, 7, 0, 7, 5, 0, 1, 2, 5, 5})
353 , mBeatsInSectionCount(0)
356 SetupPeriodicPublishing<RhytmBeats>(RhytmBeats::kPeriod, timeService);
357 Subscribe<RhytmBeats>(
this);
358 SetupPublishing<SongThemeState>(
this);
359 mThemeIt = kThemeSequence.begin();
363 virtual void Evaluate(
const RhytmBeats&)
365 if (mBeatsInSectionCount++ == 0)
367 Publish(SongThemeState(kBeatsPerTheme[*mThemeIt].first));
370 if (mBeatsInSectionCount == kBeatsPerTheme[*mThemeIt].second)
373 if (++mThemeIt == kThemeSequence.end()) mThemeIt = kThemeSequence.begin();
374 mBeatsInSectionCount = 0;
378 using BeatsAndThemePair = std::pair<std::string, int>;
379 const std::vector<BeatsAndThemePair> kBeatsPerTheme;
380 const std::vector<int> kThemeSequence;
381 int mBeatsInSectionCount;
382 std::vector<int>::const_iterator mThemeIt;
397 , kResponses({
"make it bad, take a sad song and make it better",
398 "be afraid, you were made to go out and get her",
399 "let me down, you have found her, now go and get her"})
401 Subscribe<SongThemeState>(
this);
402 SetupPublishing<DontThemeState>(
this);
404 Subscribe<DontPause>(
this);
405 SetupTimeoutPublishing<DontPause>(
this, timeService);
408 virtual void Evaluate(
const SongThemeState& aSongThemeState)
411 if (aSongThemeState.line.find(
"don't") != std::string::npos)
413 PublishOnTimeout(DontPause(), RhytmBeats::kPeriod);
416 virtual void Evaluate(
const DontPause&)
418 Publish(DontThemeState(kResponses[mDontIndex]));
420 mDontIndex %= kResponses.size();
423 DontThemeState mDontState;
425 const std::vector<std::string> kResponses;
440 , kResponses({
"let her into your heart",
441 "let her under your skin"})
443 Subscribe<SongThemeState>(
this);
444 SetupPublishing<RememberState>(
this);
446 Subscribe<RememberPause>(
this);
447 SetupTimeoutPublishing<RememberPause>(
this, timeService);
450 virtual void Evaluate(
const SongThemeState& aSongThemeState)
453 if (aSongThemeState.line.find(
"Remember") != std::string::npos)
455 PublishOnTimeout(RememberPause(), RhytmBeats::kPeriod);
458 virtual void Evaluate(
const RememberPause&)
460 Publish(RememberState(kResponses[mRememberIndex]));
462 mRememberIndex %= kResponses.size();
466 const std::vector<std::string> kResponses;
469 class ThenYouDetector :
public Detector 477 , kResponses({
"can start",
"begin"})
478 , kResponseEnd(
"to make it better")
480 Subscribe<SongThemeState>(
this);
481 SetupTimeoutPublishing<ThenYouState>(
this, timeService);
484 virtual void Evaluate(
const SongThemeState& aSongThemeState)
487 if (aSongThemeState.line.find(
"then you") != std::string::npos)
489 PublishOnTimeout(ThenYouState(kResponses[mThenYouIndex] +
" " + kResponseEnd), RhytmBeats::kPeriod);
491 mThenYouIndex %= kResponses.size();
496 const std::vector<std::string> kResponses;
497 const std::string kResponseEnd;
507 Subscribe<SongThemeState>(
this);
508 SetupPublishing<PlaybackState>(
this);
511 virtual void Evaluate(
const SongThemeState& aSongThemeState)
513 if (aSongThemeState.line.find(
"Na") != std::string::npos)
515 if (++nananaCount == kTotalNa)
517 Publish(PlaybackState(
false));
524 const int kTotalNa = 4;
532 : mTimeService(mGraph)
533 , mThemeDetector(&mGraph, &mTimeService)
534 , mDontDetector(&mGraph, &mTimeService)
535 , mRememberDetector(&mGraph, &mTimeService)
536 , mThenYouDetector(&mGraph, &mTimeService)
537 , mTooManyNaNaNasDetector(&mGraph)
538 , mCurrentPlaybackState()
542 SleepBasedTimeoutPublisherService mTimeService;
543 ThemeDetector mThemeDetector;
544 DontDetector mDontDetector;
545 RememberDetector mRememberDetector;
546 ThenYouDetector mThenYouDetector;
547 TooManyNaNaNasDetector mTooManyNaNaNasDetector;
549 PlaybackState mCurrentPlaybackState;
553 mTimeService.StartPeriodicPublishing();
554 while(mCurrentPlaybackState.autoplay && mTimeService.SleepTillBrooklyn())
560 virtual void ProcessOutput()
562 auto themeTopic = mGraph.ResolveTopic<SongThemeState>();
563 if (themeTopic->HasNewValue())
565 cout << themeTopic->GetNewValue() << endl;
568 auto dontsTopic = mGraph.ResolveTopic<DontThemeState>();
569 if (dontsTopic->HasNewValue())
571 cout << dontsTopic->GetNewValue() << endl;
574 auto rememberTopic = mGraph.ResolveTopic<RememberState>();
575 if (rememberTopic->HasNewValue())
577 cout << rememberTopic->GetNewValue() << endl;
580 auto thenYouTopic = mGraph.ResolveTopic<ThenYouState>();
581 if (thenYouTopic->HasNewValue())
583 cout << thenYouTopic->GetNewValue() << endl;
586 auto playbackTopic = mGraph.ResolveTopic<PlaybackState>();
587 if (playbackTopic->HasNewValue())
589 mCurrentPlaybackState = playbackTopic->GetNewValue();
596 BeatMachine beatMachine;
598 beatMachine.ProcessData(PlaybackState(
true));
599 beatMachine.StartLoop();
602 analyzer.GenerateDotFile(
"beat_machine.dot");
Push data to a topic when timer expires.
Implements a graph of Topics & Detectors with Input/Output APIs.
A Base class for a basic Graph container.
A service that provides Timer function to DetectorGraph Detectors.
int TimeoutPublisherHandle
Base struct for topic data types.
Base class that implements a Publisher behavior.
A unit of logic in a DetectorGraph.
A Pure interface that declares the Subscriber behavior.
Class that provides debugging/diagnostics to a DetectorGraph detector graph.