diff --git a/CMakeLists.txt b/CMakeLists.txt index b85f1565..9a11fc35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,9 @@ FetchContent_Declare( FetchContent_MakeAvailable(GSL) add_subdirectory(lib) -add_subdirectory(macos) +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + add_subdirectory(macos) +endif() option(AV_SPEECH_IN_NOISE_ENABLE_TESTS "Enables tests for av-speech-in-noise target" OFF) diff --git a/lib/core/CMakeLists.txt b/lib/core/CMakeLists.txt index fa96168c..4aefced8 100644 --- a/lib/core/CMakeLists.txt +++ b/lib/core/CMakeLists.txt @@ -12,7 +12,9 @@ add_library( src/SubmittingFreeResponse.cpp src/SubmittingPassFail.cpp src/SubmittingKeywords.cpp - src/SubmittingConsonant.cpp) + src/SubmittingConsonant.cpp + src/AudioRecording.cpp + src/EyeTracking.cpp) target_include_directories( av-speech-in-noise-core-lib PUBLIC include diff --git a/lib/core/include/av-speech-in-noise/core/AudioRecording.hpp b/lib/core/include/av-speech-in-noise/core/AudioRecording.hpp new file mode 100644 index 00000000..145513e0 --- /dev/null +++ b/lib/core/include/av-speech-in-noise/core/AudioRecording.hpp @@ -0,0 +1,36 @@ +#ifndef AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_AUDIORECORDINGHPP_ +#define AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_AUDIORECORDINGHPP_ + +#include "IRecognitionTestModel.hpp" +#include "IOutputFile.hpp" + +#include + +#include + +namespace av_speech_in_noise { +class AudioRecorder { + public: + AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(AudioRecorder); + virtual void initialize(const LocalUrl &) = 0; + virtual void start() = 0; + virtual void stop() = 0; +}; + +class AudioRecording : public RunningATest::Observer { + public: + AudioRecording(AudioRecorder &, OutputFile &); + void notifyThatNewTestIsReady(std::string_view session) override; + void notifyThatTrialWillBegin(int trialNumber) override; + void notifyThatTargetWillPlayAt(const PlayerTimeWithDelay &) override; + void notifyThatStimulusHasEnded() override; + void notifyThatSubjectHasResponded() override; + + private: + std::string session; + AudioRecorder &audioRecorder; + OutputFile &outputFile; +}; +} + +#endif diff --git a/lib/core/include/av-speech-in-noise/core/EyeTracking.hpp b/lib/core/include/av-speech-in-noise/core/EyeTracking.hpp new file mode 100644 index 00000000..b85f1809 --- /dev/null +++ b/lib/core/include/av-speech-in-noise/core/EyeTracking.hpp @@ -0,0 +1,42 @@ +#ifndef AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_EYETRACKINGHPP_ +#define AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_EYETRACKINGHPP_ + +#include "IMaskerPlayer.hpp" +#include "IOutputFile.hpp" +#include "IRecognitionTestModel.hpp" +#include "ITargetPlayer.hpp" + +#include +#include + +namespace av_speech_in_noise { +class EyeTracker : public Writable { + public: + virtual void allocateRecordingTimeSeconds(double) = 0; + virtual void start() = 0; + virtual void stop() = 0; + virtual auto gazeSamples() -> BinocularGazeSamples = 0; + virtual auto currentSystemTime() -> EyeTrackerSystemTime = 0; +}; + +class EyeTracking : public RunningATest::Observer { + public: + EyeTracking(EyeTracker &, MaskerPlayer &, TargetPlayer &, OutputFile &); + void notifyThatNewTestIsReady(std::string_view session) override; + void notifyThatTrialWillBegin(int trialNumber) override; + void notifyThatTargetWillPlayAt(const PlayerTimeWithDelay &) override; + void notifyThatStimulusHasEnded() override; + void notifyThatSubjectHasResponded() override; + + private: + EyeTrackerTargetPlayerSynchronization + lastEyeTrackerTargetPlayerSynchronization{}; + TargetStartTime lastTargetStartTime{}; + EyeTracker &eyeTracker; + MaskerPlayer &maskerPlayer; + TargetPlayer &targetPlayer; + OutputFile &outputFile; +}; +} + +#endif diff --git a/lib/core/include/av-speech-in-noise/core/IMaskerPlayer.hpp b/lib/core/include/av-speech-in-noise/core/IMaskerPlayer.hpp index b38adfb4..bed5ebe6 100644 --- a/lib/core/include/av-speech-in-noise/core/IMaskerPlayer.hpp +++ b/lib/core/include/av-speech-in-noise/core/IMaskerPlayer.hpp @@ -2,9 +2,12 @@ #define AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_IMASKERPLAYERHPP_ #include "Player.hpp" + #include #include + #include + #include #include #include diff --git a/lib/core/include/av-speech-in-noise/core/IModel.hpp b/lib/core/include/av-speech-in-noise/core/IModel.hpp index 591f02ab..1ac6b3ba 100644 --- a/lib/core/include/av-speech-in-noise/core/IModel.hpp +++ b/lib/core/include/av-speech-in-noise/core/IModel.hpp @@ -61,7 +61,7 @@ class Interactor { }; } -class Model { +class RunningATestFacade { public: class Observer { public: @@ -74,7 +74,7 @@ class Model { explicit RequestFailure(const std::string &s) : std::runtime_error{s} {} }; - AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(Model); + AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(RunningATestFacade); virtual void attach(Observer *) = 0; virtual void initialize(const AdaptiveTest &) = 0; virtual void initializeWithTargetReplacement( diff --git a/lib/core/include/av-speech-in-noise/core/IRecognitionTestModel.hpp b/lib/core/include/av-speech-in-noise/core/IRecognitionTestModel.hpp index 965ef4eb..5570739a 100644 --- a/lib/core/include/av-speech-in-noise/core/IRecognitionTestModel.hpp +++ b/lib/core/include/av-speech-in-noise/core/IRecognitionTestModel.hpp @@ -1,20 +1,32 @@ #ifndef AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_IRECOGNITIONTESTMODELHPP_ #define AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_IRECOGNITIONTESTMODELHPP_ -#include "TestMethod.hpp" +#include "Player.hpp" #include "IModel.hpp" +#include "TestMethod.hpp" + #include +#include namespace av_speech_in_noise { -class RecognitionTestModel { +class RunningATest { public: - AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(RecognitionTestModel); - virtual void attach(Model::Observer *) = 0; - virtual void initialize(TestMethod *, const Test &) = 0; + class Observer { + public: + AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(Observer); + virtual void notifyThatNewTestIsReady(std::string_view session) = 0; + virtual void notifyThatTrialWillBegin(int trialNumber) = 0; + virtual void notifyThatTargetWillPlayAt( + const PlayerTimeWithDelay &) = 0; + virtual void notifyThatStimulusHasEnded() = 0; + virtual void notifyThatSubjectHasResponded() = 0; + }; + AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(RunningATest); + virtual void attach(RunningATestFacade::Observer *) = 0; + virtual void initialize( + TestMethod *, const Test &, Observer * = nullptr) = 0; virtual void initializeWithSingleSpeaker(TestMethod *, const Test &) = 0; virtual void initializeWithDelayedMasker(TestMethod *, const Test &) = 0; - virtual void initializeWithEyeTracking(TestMethod *, const Test &) = 0; - virtual void initializeWithAudioRecording(TestMethod *, const Test &) = 0; virtual void playTrial(const AudioSettings &) = 0; virtual void playCalibration(const Calibration &) = 0; virtual void playLeftSpeakerCalibration(const Calibration &) = 0; diff --git a/lib/core/include/av-speech-in-noise/core/ITargetPlayer.hpp b/lib/core/include/av-speech-in-noise/core/ITargetPlayer.hpp index 15957220..d0ffddba 100644 --- a/lib/core/include/av-speech-in-noise/core/ITargetPlayer.hpp +++ b/lib/core/include/av-speech-in-noise/core/ITargetPlayer.hpp @@ -2,8 +2,10 @@ #define AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_ITARGETPLAYERHPP_ #include "Player.hpp" + #include #include + #include namespace av_speech_in_noise { diff --git a/lib/core/include/av-speech-in-noise/core/Model.hpp b/lib/core/include/av-speech-in-noise/core/Model.hpp index b63a8647..98618fdb 100644 --- a/lib/core/include/av-speech-in-noise/core/Model.hpp +++ b/lib/core/include/av-speech-in-noise/core/Model.hpp @@ -2,17 +2,17 @@ #define AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_MODELHPP_ #include "TargetPlaylist.hpp" -#include "TestMethod.hpp" -#include "IOutputFile.hpp" #include "IAdaptiveMethod.hpp" #include "IFixedLevelMethod.hpp" -#include "IRecognitionTestModel.hpp" #include "IModel.hpp" +#include "IOutputFile.hpp" +#include "IRecognitionTestModel.hpp" +#include "TestMethod.hpp" namespace av_speech_in_noise { -class ModelImpl : public Model { +class RunningATestFacadeImpl : public RunningATestFacade { public: - ModelImpl(AdaptiveMethod &, FixedLevelMethod &, + RunningATestFacadeImpl(AdaptiveMethod &, FixedLevelMethod &, TargetPlaylistReader &targetsWithReplacementReader, TargetPlaylistReader &cyclicTargetsReader, TargetPlaylist &targetsWithReplacement, @@ -20,8 +20,9 @@ class ModelImpl : public Model { FiniteTargetPlaylistWithRepeatables &everyTargetOnce, RepeatableFiniteTargetPlaylist &eachTargetNTimes, FiniteTargetPlaylistWithRepeatables &predeterminedTargets, - RecognitionTestModel &, OutputFile &); - void attach(Model::Observer *) override; + RunningATest &, OutputFile &, RunningATest::Observer &audioRecording, + RunningATest::Observer &eyeTracking); + void attach(RunningATestFacade::Observer *) override; void initialize(const AdaptiveTest &) override; void initializeWithTargetReplacement( const FixedLevelFixedTrialsTest &) override; @@ -60,6 +61,8 @@ class ModelImpl : public Model { private: void initializeTest_(const AdaptiveTest &); + RunningATest::Observer &audioRecording; + RunningATest::Observer &eyeTracking; AdaptiveMethod &adaptiveMethod; FixedLevelMethod &fixedLevelMethod; TargetPlaylistReader &targetsWithReplacementReader; @@ -69,7 +72,7 @@ class ModelImpl : public Model { FiniteTargetPlaylistWithRepeatables &everyTargetOnce; FiniteTargetPlaylistWithRepeatables &predeterminedTargets; RepeatableFiniteTargetPlaylist &eachTargetNTimes; - RecognitionTestModel &model; + RunningATest &runningATest; OutputFile &outputFile; }; } diff --git a/lib/core/include/av-speech-in-noise/core/RecognitionTestModel.hpp b/lib/core/include/av-speech-in-noise/core/RecognitionTestModel.hpp index 31088887..deec7d81 100644 --- a/lib/core/include/av-speech-in-noise/core/RecognitionTestModel.hpp +++ b/lib/core/include/av-speech-in-noise/core/RecognitionTestModel.hpp @@ -2,51 +2,38 @@ #define AV_SPEECH_IN_NOISE_LIB_CORE_INCLUDE_AVSPEECHINNOISE_CORE_RECOGNITIONTESTMODELHPP_ #include "Randomizer.hpp" -#include "IResponseEvaluator.hpp" -#include "ITargetPlayer.hpp" #include "IMaskerPlayer.hpp" -#include "IRecognitionTestModel.hpp" #include "IOutputFile.hpp" -#include "av-speech-in-noise/Model.hpp" +#include "IRecognitionTestModel.hpp" +#include "IResponseEvaluator.hpp" +#include "ITargetPlayer.hpp" + #include +#include + #include +#include namespace av_speech_in_noise { -class EyeTracker : public Writable { - public: - virtual void allocateRecordingTimeSeconds(double) = 0; - virtual void start() = 0; - virtual void stop() = 0; - virtual auto gazeSamples() -> BinocularGazeSamples = 0; - virtual auto currentSystemTime() -> EyeTrackerSystemTime = 0; -}; - class Clock { public: AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(Clock); virtual auto time() -> std::string = 0; }; -class AudioRecorder { - public: - AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS(AudioRecorder); - virtual void initialize(const LocalUrl &) = 0; - virtual void start() = 0; - virtual void stop() = 0; -}; +auto trialDuration(TargetPlayer &target, MaskerPlayer &masker) -> Duration; -class RecognitionTestModelImpl : public TargetPlayer::Observer, - public MaskerPlayer::Observer, - public RecognitionTestModel { +class RunningATestImpl : public TargetPlayer::Observer, + public MaskerPlayer::Observer, + public RunningATest { public: - RecognitionTestModelImpl(TargetPlayer &, MaskerPlayer &, AudioRecorder &, - ResponseEvaluator &, OutputFile &, Randomizer &, EyeTracker &, Clock &); - void attach(Model::Observer *) override; - void initialize(TestMethod *, const Test &) override; + RunningATestImpl(TargetPlayer &, MaskerPlayer &, ResponseEvaluator &, + OutputFile &, Randomizer &, Clock &); + void attach(RunningATestFacade::Observer *) override; + void initialize( + TestMethod *, const Test &, RunningATest::Observer *) override; void initializeWithSingleSpeaker(TestMethod *, const Test &) override; void initializeWithDelayedMasker(TestMethod *, const Test &) override; - void initializeWithEyeTracking(TestMethod *, const Test &) override; - void initializeWithAudioRecording(TestMethod *, const Test &) override; void playTrial(const AudioSettings &) override; void playCalibration(const Calibration &) override; void playLeftSpeakerCalibration(const Calibration &) override; @@ -67,30 +54,23 @@ class RecognitionTestModelImpl : public TargetPlayer::Observer, targetOnsetFringeDuration}; private: - void initialize_(TestMethod *, const Test &); + void initialize_(TestMethod *, const Test &, RunningATest::Observer *); void seekRandomMaskerPosition(); + RunningATest::Observer *observer; MaskerPlayer &maskerPlayer; TargetPlayer &targetPlayer; - AudioRecorder &audioRecorder; ResponseEvaluator &evaluator; OutputFile &outputFile; Randomizer &randomizer; - EyeTracker &eyeTracker; Clock &clock; - EyeTrackerTargetPlayerSynchronization - lastEyeTrackerTargetPlayerSynchronization{}; - TargetStartTime lastTargetStartTime{}; std::string playTrialTime_; - std::string session; - Model::Observer *listener_{}; + RunningATestFacade::Observer *listener_{}; TestMethod *testMethod{}; RealLevel maskerLevel_{}; RealLevel fullScaleLevel_{}; int trialNumber_{}; Condition condition{}; - bool eyeTracking{}; - bool audioRecordingEnabled{}; bool trialInProgress_{}; }; } diff --git a/lib/core/include/av-speech-in-noise/core/SubmittingConsonant.hpp b/lib/core/include/av-speech-in-noise/core/SubmittingConsonant.hpp index c5ecfdb1..b99099b9 100644 --- a/lib/core/include/av-speech-in-noise/core/SubmittingConsonant.hpp +++ b/lib/core/include/av-speech-in-noise/core/SubmittingConsonant.hpp @@ -11,12 +11,12 @@ namespace av_speech_in_noise::submitting_consonant { class InteractorImpl : public Interactor { public: - InteractorImpl(FixedLevelMethod &, RecognitionTestModel &, OutputFile &); + InteractorImpl(FixedLevelMethod &, RunningATest &, OutputFile &); void submit(const ConsonantResponse &r) override; private: FixedLevelMethod &method; - RecognitionTestModel &model; + RunningATest &model; OutputFile &outputFile; }; } diff --git a/lib/core/include/av-speech-in-noise/core/SubmittingFreeResponse.hpp b/lib/core/include/av-speech-in-noise/core/SubmittingFreeResponse.hpp index 23d75714..f6bfeb80 100644 --- a/lib/core/include/av-speech-in-noise/core/SubmittingFreeResponse.hpp +++ b/lib/core/include/av-speech-in-noise/core/SubmittingFreeResponse.hpp @@ -9,12 +9,12 @@ namespace av_speech_in_noise::submitting_free_response { class InteractorImpl : public Interactor { public: - InteractorImpl(FixedLevelMethod &, RecognitionTestModel &, OutputFile &); + InteractorImpl(FixedLevelMethod &, RunningATest &, OutputFile &); void submit(const FreeResponse &) override; private: FixedLevelMethod &method; - RecognitionTestModel &model; + RunningATest &model; OutputFile &outputFile; }; } diff --git a/lib/core/include/av-speech-in-noise/core/SubmittingKeywords.hpp b/lib/core/include/av-speech-in-noise/core/SubmittingKeywords.hpp index 952657eb..8ea8220c 100644 --- a/lib/core/include/av-speech-in-noise/core/SubmittingKeywords.hpp +++ b/lib/core/include/av-speech-in-noise/core/SubmittingKeywords.hpp @@ -9,12 +9,12 @@ namespace av_speech_in_noise::submitting_keywords { class InteractorImpl : public Interactor { public: - InteractorImpl(FixedLevelMethod &, RecognitionTestModel &, OutputFile &); + InteractorImpl(FixedLevelMethod &, RunningATest &, OutputFile &); void submit(const ThreeKeywordsResponse &p) override; private: FixedLevelMethod &method; - RecognitionTestModel &recognitionTestModel; + RunningATest &recognitionTestModel; OutputFile &outputFile; }; } diff --git a/lib/core/include/av-speech-in-noise/core/SubmittingNumberKeywords.hpp b/lib/core/include/av-speech-in-noise/core/SubmittingNumberKeywords.hpp index 17afde62..469a128c 100644 --- a/lib/core/include/av-speech-in-noise/core/SubmittingNumberKeywords.hpp +++ b/lib/core/include/av-speech-in-noise/core/SubmittingNumberKeywords.hpp @@ -9,8 +9,8 @@ namespace av_speech_in_noise::submitting_number_keywords { class InteractorImpl : public Interactor { public: - InteractorImpl(AdaptiveMethod &method, RecognitionTestModel &model, - OutputFile &outputFile) + InteractorImpl( + AdaptiveMethod &method, RunningATest &model, OutputFile &outputFile) : method{method}, model{model}, outputFile{outputFile} {} void submit(const CorrectKeywords &k) override { @@ -22,7 +22,7 @@ class InteractorImpl : public Interactor { private: AdaptiveMethod &method; - RecognitionTestModel &model; + RunningATest &model; OutputFile &outputFile; }; } diff --git a/lib/core/include/av-speech-in-noise/core/SubmittingPassFail.hpp b/lib/core/include/av-speech-in-noise/core/SubmittingPassFail.hpp index 8b9165e7..b811851e 100644 --- a/lib/core/include/av-speech-in-noise/core/SubmittingPassFail.hpp +++ b/lib/core/include/av-speech-in-noise/core/SubmittingPassFail.hpp @@ -9,13 +9,13 @@ namespace av_speech_in_noise::submitting_pass_fail { class InteractorImpl : public Interactor { public: - InteractorImpl(AdaptiveMethod &, RecognitionTestModel &, OutputFile &); + InteractorImpl(AdaptiveMethod &, RunningATest &, OutputFile &); void submitCorrectResponse() override; void submitIncorrectResponse() override; private: AdaptiveMethod &adaptiveMethod; - RecognitionTestModel &model; + RunningATest &model; OutputFile &outputFile; }; } diff --git a/lib/core/include/av-speech-in-noise/core/SubmittingSyllable.hpp b/lib/core/include/av-speech-in-noise/core/SubmittingSyllable.hpp index 5e95306a..9e0b0feb 100644 --- a/lib/core/include/av-speech-in-noise/core/SubmittingSyllable.hpp +++ b/lib/core/include/av-speech-in-noise/core/SubmittingSyllable.hpp @@ -65,8 +65,8 @@ static auto correctSyllable(const LocalUrl &file) -> Syllable { class InteractorImpl : public Interactor { public: - InteractorImpl(FixedLevelMethod &method, RecognitionTestModel &model, - OutputFile &outputFile) + InteractorImpl( + FixedLevelMethod &method, RunningATest &model, OutputFile &outputFile) : method{method}, model{model}, outputFile{outputFile} {} void submit(const SyllableResponse &p) override { @@ -85,7 +85,7 @@ class InteractorImpl : public Interactor { private: FixedLevelMethod &method; - RecognitionTestModel &model; + RunningATest &model; OutputFile &outputFile; }; } diff --git a/lib/core/src/AudioRecording.cpp b/lib/core/src/AudioRecording.cpp new file mode 100644 index 00000000..91ca539b --- /dev/null +++ b/lib/core/src/AudioRecording.cpp @@ -0,0 +1,26 @@ +#include "AudioRecording.hpp" + +#include + +namespace av_speech_in_noise { +AudioRecording::AudioRecording( + AudioRecorder &audioRecorder, OutputFile &outputFile) + : audioRecorder{audioRecorder}, outputFile{outputFile} {} + +void AudioRecording::notifyThatTrialWillBegin(int trialNumber) { + std::stringstream filename; + filename << trialNumber << '-' << session << ".wav"; + audioRecorder.initialize( + LocalUrl{outputFile.parentPath() / filename.str()}); +} + +void AudioRecording::notifyThatTargetWillPlayAt(const PlayerTimeWithDelay &) {} + +void AudioRecording::notifyThatStimulusHasEnded() { audioRecorder.start(); } + +void AudioRecording::notifyThatSubjectHasResponded() { audioRecorder.stop(); } + +void AudioRecording::notifyThatNewTestIsReady(std::string_view session) { + this->session = session; +} +} diff --git a/lib/core/src/EyeTracking.cpp b/lib/core/src/EyeTracking.cpp new file mode 100644 index 00000000..aef3449b --- /dev/null +++ b/lib/core/src/EyeTracking.cpp @@ -0,0 +1,53 @@ +#include "EyeTracking.hpp" + +#include "RecognitionTestModel.hpp" + +namespace av_speech_in_noise { +static auto nanoseconds(Delay x) -> std::uintmax_t { + return gsl::narrow_cast(x.seconds * 1e9); +} + +static auto nanoseconds(MaskerPlayer &player, const PlayerTime &t) + -> std::uintmax_t { + return player.nanoseconds(t); +} + +static auto nanoseconds(MaskerPlayer &player, const PlayerTimeWithDelay &t) + -> std::uintmax_t { + return nanoseconds(player, t.playerTime) + nanoseconds(t.delay); +} + +EyeTracking::EyeTracking(EyeTracker &eyeTracker, MaskerPlayer &maskerPlayer, + TargetPlayer &targetPlayer, OutputFile &outputFile) + : eyeTracker{eyeTracker}, maskerPlayer{maskerPlayer}, + targetPlayer{targetPlayer}, outputFile{outputFile} {} + +void EyeTracking::notifyThatTrialWillBegin(int trialNumber) { + eyeTracker.allocateRecordingTimeSeconds( + Duration{trialDuration(targetPlayer, maskerPlayer)}.seconds); + eyeTracker.start(); +} + +void EyeTracking::notifyThatTargetWillPlayAt( + const PlayerTimeWithDelay &timeToPlayWithDelay) { + lastTargetStartTime.nanoseconds = + nanoseconds(maskerPlayer, timeToPlayWithDelay); + + lastEyeTrackerTargetPlayerSynchronization.eyeTrackerSystemTime = + eyeTracker.currentSystemTime(); + lastEyeTrackerTargetPlayerSynchronization.targetPlayerSystemTime = + TargetPlayerSystemTime{ + nanoseconds(maskerPlayer, maskerPlayer.currentSystemTime())}; +} + +void EyeTracking::notifyThatStimulusHasEnded() { eyeTracker.stop(); } + +void EyeTracking::notifyThatSubjectHasResponded() { + outputFile.write(lastTargetStartTime); + outputFile.write(lastEyeTrackerTargetPlayerSynchronization); + outputFile.write(eyeTracker.gazeSamples()); + outputFile.save(); +} + +void EyeTracking::notifyThatNewTestIsReady(std::string_view session) {} +} diff --git a/lib/core/src/Model.cpp b/lib/core/src/Model.cpp index 385fc96e..d936310b 100644 --- a/lib/core/src/Model.cpp +++ b/lib/core/src/Model.cpp @@ -1,7 +1,8 @@ #include "Model.hpp" +#include "IRecognitionTestModel.hpp" namespace av_speech_in_noise { -ModelImpl::ModelImpl(AdaptiveMethod &adaptiveMethod, +RunningATestFacadeImpl::RunningATestFacadeImpl(AdaptiveMethod &adaptiveMethod, FixedLevelMethod &fixedLevelMethod, TargetPlaylistReader &targetsWithReplacementReader, TargetPlaylistReader &cyclicTargetsReader, @@ -10,19 +11,21 @@ ModelImpl::ModelImpl(AdaptiveMethod &adaptiveMethod, FiniteTargetPlaylistWithRepeatables &everyTargetOnce, RepeatableFiniteTargetPlaylist &eachTargetNTimes, FiniteTargetPlaylistWithRepeatables &predeterminedTargets, - RecognitionTestModel &model, OutputFile &outputFile) - : adaptiveMethod{adaptiveMethod}, fixedLevelMethod{fixedLevelMethod}, + RunningATest &model, OutputFile &outputFile, + RunningATest::Observer &audioRecording, RunningATest::Observer &eyeTracking) + : audioRecording{audioRecording}, eyeTracking{eyeTracking}, + adaptiveMethod{adaptiveMethod}, fixedLevelMethod{fixedLevelMethod}, targetsWithReplacementReader{targetsWithReplacementReader}, cyclicTargetsReader{cyclicTargetsReader}, targetsWithReplacement{targetsWithReplacement}, silentIntervalTargets{silentIntervalTargets}, everyTargetOnce{everyTargetOnce}, predeterminedTargets{predeterminedTargets}, - eachTargetNTimes{eachTargetNTimes}, model{model}, outputFile{outputFile} { -} + eachTargetNTimes{eachTargetNTimes}, runningATest{model}, + outputFile{outputFile} {} static void initialize( - RecognitionTestModel &model, TestMethod &method, const Test &test) { + RunningATest &model, TestMethod &method, const Test &test) { model.initialize(&method, test); } @@ -47,170 +50,181 @@ static void initialize(AdaptiveMethod &method, const AdaptiveTest &test, method.initialize(test, &reader); } -static void initializeWithSingleSpeaker(RecognitionTestModel &model, - AdaptiveMethod &method, const AdaptiveTest &test) { +static void initializeWithSingleSpeaker( + RunningATest &model, AdaptiveMethod &method, const AdaptiveTest &test) { model.initializeWithSingleSpeaker(&method, test); } static void initializeWithDelayedMasker( - RecognitionTestModel &model, TestMethod &method, const Test &test) { + RunningATest &model, TestMethod &method, const Test &test) { model.initializeWithDelayedMasker(&method, test); } -static void initializeWithEyeTracking( - RecognitionTestModel &model, TestMethod &method, const Test &test) { - model.initializeWithEyeTracking(&method, test); -} - -static void initializeWithAudioRecording( - RecognitionTestModel &model, TestMethod &method, const Test &test) { - model.initializeWithAudioRecording(&method, test); +static void initialize(RunningATest &model, TestMethod &method, + const Test &test, RunningATest::Observer *observer) { + model.initialize(&method, test, observer); } -void ModelImpl::initializeWithTargetReplacement( +void RunningATestFacadeImpl::initializeWithTargetReplacement( const FixedLevelFixedTrialsTest &test) { av_speech_in_noise::initialize( fixedLevelMethod, test, targetsWithReplacement); - av_speech_in_noise::initialize(model, fixedLevelMethod, test); + av_speech_in_noise::initialize(runningATest, fixedLevelMethod, test); } -void ModelImpl::initialize(const AdaptiveTest &test) { +void RunningATestFacadeImpl::initialize(const AdaptiveTest &test) { av_speech_in_noise::initialize( adaptiveMethod, test, targetsWithReplacementReader); - av_speech_in_noise::initialize(model, adaptiveMethod, test); + av_speech_in_noise::initialize(runningATest, adaptiveMethod, test); } -void ModelImpl::initializeWithSilentIntervalTargets( +void RunningATestFacadeImpl::initializeWithSilentIntervalTargets( const FixedLevelTest &test) { av_speech_in_noise::initialize( fixedLevelMethod, test, silentIntervalTargets); - av_speech_in_noise::initialize(model, fixedLevelMethod, test); + av_speech_in_noise::initialize(runningATest, fixedLevelMethod, test); } -void ModelImpl::initializeWithAllTargets(const FixedLevelTest &test) { +void RunningATestFacadeImpl::initializeWithAllTargets( + const FixedLevelTest &test) { av_speech_in_noise::initialize(fixedLevelMethod, test, everyTargetOnce); - av_speech_in_noise::initialize(model, fixedLevelMethod, test); + av_speech_in_noise::initialize(runningATest, fixedLevelMethod, test); } -void ModelImpl::initialize(const FixedLevelTestWithEachTargetNTimes &test) { +void RunningATestFacadeImpl::initialize( + const FixedLevelTestWithEachTargetNTimes &test) { eachTargetNTimes.setRepeats(test.timesEachTargetIsPlayed - 1); av_speech_in_noise::initialize(fixedLevelMethod, test, eachTargetNTimes); - av_speech_in_noise::initialize(model, fixedLevelMethod, test); + av_speech_in_noise::initialize(runningATest, fixedLevelMethod, test); } -void ModelImpl::initializeWithAllTargetsAndEyeTracking( +void RunningATestFacadeImpl::initializeWithAllTargetsAndEyeTracking( const FixedLevelTest &test) { av_speech_in_noise::initialize(fixedLevelMethod, test, everyTargetOnce); - av_speech_in_noise::initializeWithEyeTracking( - model, fixedLevelMethod, test); + av_speech_in_noise::initialize( + runningATest, fixedLevelMethod, test, &eyeTracking); } -void ModelImpl::initializeWithAllTargetsAndAudioRecording( +void RunningATestFacadeImpl::initializeWithAllTargetsAndAudioRecording( const FixedLevelTest &test) { av_speech_in_noise::initialize(fixedLevelMethod, test, everyTargetOnce); - av_speech_in_noise::initializeWithAudioRecording( - model, fixedLevelMethod, test); + av_speech_in_noise::initialize( + runningATest, fixedLevelMethod, test, &audioRecording); } -void ModelImpl::initializeWithPredeterminedTargetsAndAudioRecording( - const FixedLevelTest &test) { +void RunningATestFacadeImpl:: + initializeWithPredeterminedTargetsAndAudioRecording( + const FixedLevelTest &test) { av_speech_in_noise::initialize( fixedLevelMethod, test, predeterminedTargets); - av_speech_in_noise::initializeWithAudioRecording( - model, fixedLevelMethod, test); + av_speech_in_noise::initialize( + runningATest, fixedLevelMethod, test, &audioRecording); } -void ModelImpl::initializeWithSingleSpeaker(const AdaptiveTest &test) { +void RunningATestFacadeImpl::initializeWithSingleSpeaker( + const AdaptiveTest &test) { av_speech_in_noise::initialize( adaptiveMethod, test, targetsWithReplacementReader); av_speech_in_noise::initializeWithSingleSpeaker( - model, adaptiveMethod, test); + runningATest, adaptiveMethod, test); } -void ModelImpl::initializeWithDelayedMasker(const AdaptiveTest &test) { +void RunningATestFacadeImpl::initializeWithDelayedMasker( + const AdaptiveTest &test) { av_speech_in_noise::initialize( adaptiveMethod, test, targetsWithReplacementReader); av_speech_in_noise::initializeWithDelayedMasker( - model, adaptiveMethod, test); + runningATest, adaptiveMethod, test); } -void ModelImpl::initializeWithTargetReplacementAndEyeTracking( +void RunningATestFacadeImpl::initializeWithTargetReplacementAndEyeTracking( const FixedLevelFixedTrialsTest &test) { av_speech_in_noise::initialize( fixedLevelMethod, test, targetsWithReplacement); - av_speech_in_noise::initializeWithEyeTracking( - model, fixedLevelMethod, test); + av_speech_in_noise::initialize( + runningATest, fixedLevelMethod, test, &eyeTracking); } -void ModelImpl::initializeWithSilentIntervalTargetsAndEyeTracking( +void RunningATestFacadeImpl::initializeWithSilentIntervalTargetsAndEyeTracking( const FixedLevelTest &test) { av_speech_in_noise::initialize( fixedLevelMethod, test, silentIntervalTargets); - av_speech_in_noise::initializeWithEyeTracking( - model, fixedLevelMethod, test); + av_speech_in_noise::initialize( + runningATest, fixedLevelMethod, test, &eyeTracking); } -void ModelImpl::initializeWithEyeTracking(const AdaptiveTest &test) { +void RunningATestFacadeImpl::initializeWithEyeTracking( + const AdaptiveTest &test) { av_speech_in_noise::initialize( adaptiveMethod, test, targetsWithReplacementReader); - av_speech_in_noise::initializeWithEyeTracking(model, adaptiveMethod, test); + av_speech_in_noise::initialize( + runningATest, adaptiveMethod, test, &eyeTracking); } -void ModelImpl::initializeWithCyclicTargets(const AdaptiveTest &test) { +void RunningATestFacadeImpl::initializeWithCyclicTargets( + const AdaptiveTest &test) { av_speech_in_noise::initialize(adaptiveMethod, test, cyclicTargetsReader); - av_speech_in_noise::initialize(model, adaptiveMethod, test); + av_speech_in_noise::initialize(runningATest, adaptiveMethod, test); } -void ModelImpl::initializeWithCyclicTargetsAndEyeTracking( +void RunningATestFacadeImpl::initializeWithCyclicTargetsAndEyeTracking( const AdaptiveTest &test) { av_speech_in_noise::initialize(adaptiveMethod, test, cyclicTargetsReader); - av_speech_in_noise::initializeWithEyeTracking(model, adaptiveMethod, test); + av_speech_in_noise::initialize( + runningATest, adaptiveMethod, test, &eyeTracking); } -void ModelImpl::restartAdaptiveTestWhilePreservingTargets() { +void RunningATestFacadeImpl::restartAdaptiveTestWhilePreservingTargets() { adaptiveMethod.resetTracks(); - model.prepareNextTrialIfNeeded(); + runningATest.prepareNextTrialIfNeeded(); } -void ModelImpl::playTrial(const AudioSettings &settings) { - model.playTrial(settings); +void RunningATestFacadeImpl::playTrial(const AudioSettings &settings) { + runningATest.playTrial(settings); } -void ModelImpl::submit(const coordinate_response_measure::Response &response) { - model.submit(response); +void RunningATestFacadeImpl::submit( + const coordinate_response_measure::Response &response) { + runningATest.submit(response); } -void ModelImpl::playCalibration(const Calibration &p) { - model.playCalibration(p); +void RunningATestFacadeImpl::playCalibration(const Calibration &p) { + runningATest.playCalibration(p); } -void ModelImpl::playLeftSpeakerCalibration(const Calibration &p) { - model.playLeftSpeakerCalibration(p); +void RunningATestFacadeImpl::playLeftSpeakerCalibration(const Calibration &p) { + runningATest.playLeftSpeakerCalibration(p); } -void ModelImpl::playRightSpeakerCalibration(const Calibration &p) { - model.playRightSpeakerCalibration(p); +void RunningATestFacadeImpl::playRightSpeakerCalibration(const Calibration &p) { + runningATest.playRightSpeakerCalibration(p); } -auto ModelImpl::testComplete() -> bool { return model.testComplete(); } +auto RunningATestFacadeImpl::testComplete() -> bool { + return runningATest.testComplete(); +} -auto ModelImpl::audioDevices() -> std::vector { - return model.audioDevices(); +auto RunningATestFacadeImpl::audioDevices() -> std::vector { + return runningATest.audioDevices(); } -auto ModelImpl::adaptiveTestResults() -> AdaptiveTestResults { +auto RunningATestFacadeImpl::adaptiveTestResults() -> AdaptiveTestResults { return adaptiveMethod.testResults(); } -void ModelImpl::attach(Model::Observer *e) { model.attach(e); } +void RunningATestFacadeImpl::attach(RunningATestFacade::Observer *e) { + runningATest.attach(e); +} -auto ModelImpl::trialNumber() -> int { return model.trialNumber(); } +auto RunningATestFacadeImpl::trialNumber() -> int { + return runningATest.trialNumber(); +} -auto ModelImpl::targetFileName() -> std::string { - return model.targetFileName(); +auto RunningATestFacadeImpl::targetFileName() -> std::string { + return runningATest.targetFileName(); } -auto ModelImpl::keywordsTestResults() -> KeywordsTestResults { +auto RunningATestFacadeImpl::keywordsTestResults() -> KeywordsTestResults { return fixedLevelMethod.keywordsTestResults(); } } diff --git a/lib/core/src/RecognitionTestModel.cpp b/lib/core/src/RecognitionTestModel.cpp index 324a101d..beb51e20 100644 --- a/lib/core/src/RecognitionTestModel.cpp +++ b/lib/core/src/RecognitionTestModel.cpp @@ -1,10 +1,11 @@ #include "RecognitionTestModel.hpp" -#include "IOutputFile.hpp" -#include "av-speech-in-noise/Model.hpp" + #include + #include #include #include +#include namespace av_speech_in_noise { namespace { @@ -18,10 +19,20 @@ class NullTestMethod : public TestMethod { void writeTestingParameters(OutputFile &) override {} void writeTestResult(OutputFile &) override {} }; + +class NullObserver : public RunningATest::Observer { + void notifyThatNewTestIsReady(std::string_view session) override {} + void notifyThatTrialWillBegin(int trialNumber) override {} + void notifyThatTargetWillPlayAt(const PlayerTimeWithDelay &) override {} + void notifyThatStimulusHasEnded() override {} + void notifyThatSubjectHasResponded() override {} +}; } static NullTestMethod nullTestMethod; +static NullObserver nullObserver; + static void useAllChannels(MaskerPlayer &player) { player.useAllChannels(); } static void clearChannelDelays(MaskerPlayer &player) { @@ -40,18 +51,14 @@ static constexpr auto operator+(const Duration &a, const Duration &b) } static auto steadyLevelDuration(TargetPlayer &player) -> Duration { - return player.duration() + - RecognitionTestModelImpl::targetOnsetFringeDuration + - RecognitionTestModelImpl::targetOffsetFringeDuration; + return player.duration() + RunningATestImpl::targetOnsetFringeDuration + + RunningATestImpl::targetOffsetFringeDuration; } -static auto trialDuration(TargetPlayer &target, MaskerPlayer &masker) - -> Duration { +auto trialDuration(TargetPlayer &target, MaskerPlayer &masker) -> Duration { return totalrampDuration(masker) + steadyLevelDuration(target); } -static void turnOff(bool &b) { b = false; } - static void useFirstChannelOnly(TargetPlayer &player) { player.useFirstChannelOnly(); } @@ -76,14 +83,14 @@ static void throwRequestFailureOnInvalidAudioDevice( try { f(device); } catch (const InvalidAudioDevice &) { - throw Model::RequestFailure{ + throw RunningATestFacade::RequestFailure{ "'" + device + "' is not a valid audio device."}; } } static void throwRequestFailureIfTrialInProgress(bool f) { if (f) - throw Model::RequestFailure{"Trial in progress."}; + throw RunningATestFacade::RequestFailure{"Trial in progress."}; } static void apply(TargetPlayer &player, LevelAmplification x) { @@ -122,7 +129,7 @@ static void tryOpening(OutputFile &file, const TestIdentity &p) { try { file.openNewFile(p); } catch (const OutputFile::OpenFailure &) { - throw Model::RequestFailure{"Unable to open output file."}; + throw RunningATestFacade::RequestFailure{"Unable to open output file."}; } } @@ -161,24 +168,10 @@ static void throwRequestFailureOnInvalidAudioFile( try { f(s); } catch (const InvalidAudioFile &) { - throw Model::RequestFailure{"unable to read " + s.path}; + throw RunningATestFacade::RequestFailure{"unable to read " + s.path}; } } -static auto nanoseconds(Delay x) -> std::uintmax_t { - return gsl::narrow_cast(x.seconds * 1e9); -} - -static auto nanoseconds(MaskerPlayer &player, const PlayerTime &t) - -> std::uintmax_t { - return player.nanoseconds(t); -} - -static auto nanoseconds(MaskerPlayer &player, const PlayerTimeWithDelay &t) - -> std::uintmax_t { - return nanoseconds(player, t.playerTime) + nanoseconds(t.delay); -} - static auto offsetDuration( MaskerPlayer &player, const AudioSampleTimeWithOffset &t) -> Duration { return Duration{t.sampleOffset / player.sampleRateHz()}; @@ -249,20 +242,9 @@ static void preparePlayersForNextTrial(TestMethod *testMethod, static void prepareNextTrialIfNeeded(TestMethod *testMethod, int &trialNumber_, OutputFile &outputFile, Randomizer &randomizer, TargetPlayer &targetPlayer, - MaskerPlayer &maskerPlayer, EyeTracker &eyeTracker, RealLevel maskerLevel, - RealLevel fullScaleLevel, bool eyeTracking, - TargetStartTime lastTargetStartTime, - EyeTrackerTargetPlayerSynchronization - lastEyeTrackerTargetPlayerSynchronization, - bool audioRecordingEnabled, AudioRecorder &audioRecorder) { - if (audioRecordingEnabled) - audioRecorder.stop(); - if (eyeTracking) { - outputFile.write(lastTargetStartTime); - outputFile.write(lastEyeTrackerTargetPlayerSynchronization); - outputFile.write(eyeTracker.gazeSamples()); - save(outputFile); - } + MaskerPlayer &maskerPlayer, RunningATest::Observer *observer, + RealLevel maskerLevel, RealLevel fullScaleLevel) { + observer->notifyThatSubjectHasResponded(); if (!testMethod->complete()) { ++trialNumber_; preparePlayersForNextTrial(testMethod, randomizer, targetPlayer, @@ -276,44 +258,36 @@ static void prepareNextTrialIfNeeded(TestMethod *testMethod, int &trialNumber_, static void saveOutputFileAndPrepareNextTrialAfter( const std::function &f, TestMethod *testMethod, int &trialNumber_, OutputFile &outputFile, Randomizer &randomizer, TargetPlayer &targetPlayer, - MaskerPlayer &maskerPlayer, EyeTracker &eyeTracker, RealLevel maskerLevel, - RealLevel fullScaleLevel, bool eyeTracking, - TargetStartTime lastTargetStartTime, - EyeTrackerTargetPlayerSynchronization - lastEyeTrackerTargetPlayerSynchronization, - bool audioRecordingEnabled, AudioRecorder &audioRecorder) { + MaskerPlayer &maskerPlayer, RunningATest::Observer *observer, + RealLevel maskerLevel, RealLevel fullScaleLevel) { f(); save(outputFile); prepareNextTrialIfNeeded(testMethod, trialNumber_, outputFile, randomizer, - targetPlayer, maskerPlayer, eyeTracker, maskerLevel, fullScaleLevel, - eyeTracking, lastTargetStartTime, - lastEyeTrackerTargetPlayerSynchronization, audioRecordingEnabled, - audioRecorder); -} - -RecognitionTestModelImpl::RecognitionTestModelImpl(TargetPlayer &targetPlayer, - MaskerPlayer &maskerPlayer, AudioRecorder &audioRecorder, - ResponseEvaluator &evaluator, OutputFile &outputFile, - Randomizer &randomizer, EyeTracker &eyeTracker, Clock &clock) - : maskerPlayer{maskerPlayer}, targetPlayer{targetPlayer}, - audioRecorder{audioRecorder}, evaluator{evaluator}, - outputFile{outputFile}, randomizer{randomizer}, - eyeTracker{eyeTracker}, clock{clock}, testMethod{&nullTestMethod} { + targetPlayer, maskerPlayer, observer, maskerLevel, fullScaleLevel); +} + +RunningATestImpl::RunningATestImpl(TargetPlayer &targetPlayer, + MaskerPlayer &maskerPlayer, ResponseEvaluator &evaluator, + OutputFile &outputFile, Randomizer &randomizer, Clock &clock) + : observer{&nullObserver}, maskerPlayer{maskerPlayer}, + targetPlayer{targetPlayer}, evaluator{evaluator}, outputFile{outputFile}, + randomizer{randomizer}, clock{clock}, testMethod{&nullTestMethod} { targetPlayer.attach(this); maskerPlayer.attach(this); } -void RecognitionTestModelImpl::attach(Model::Observer *listener) { +void RunningATestImpl::attach(RunningATestFacade::Observer *listener) { listener_ = listener; } -void RecognitionTestModelImpl::initialize( - TestMethod *testMethod_, const Test &test) { - initialize_(testMethod_, test); +void RunningATestImpl::initialize(TestMethod *testMethod_, const Test &test, + RunningATest::Observer *observer_) { + initialize_( + testMethod_, test, observer_ == nullptr ? &nullObserver : observer_); } -void RecognitionTestModelImpl::initialize_( - TestMethod *testMethod_, const Test &test) { +void RunningATestImpl::initialize_(TestMethod *testMethod_, const Test &test, + RunningATest::Observer *observer_) { throwRequestFailureIfTrialInProgress(trialInProgress_); if (testMethod_->complete()) @@ -329,7 +303,6 @@ void RecognitionTestModelImpl::initialize_( fullScaleLevel_ = test.fullScaleLevel; maskerLevel_ = test.maskerLevel; condition = test.condition; - session = test.identity.session; hide(targetPlayer); maskerPlayer.apply( @@ -342,37 +315,25 @@ void RecognitionTestModelImpl::initialize_( useAllChannels(targetPlayer); useAllChannels(maskerPlayer); clearChannelDelays(maskerPlayer); - turnOff(eyeTracking); - audioRecordingEnabled = false; + observer = observer_; + observer->notifyThatNewTestIsReady(test.identity.session); } -void RecognitionTestModelImpl::initializeWithSingleSpeaker( +void RunningATestImpl::initializeWithSingleSpeaker( TestMethod *testMethod_, const Test &test) { - initialize_(testMethod_, test); + initialize_(testMethod_, test, &nullObserver); useFirstChannelOnly(targetPlayer); maskerPlayer.useFirstChannelOnly(); } -void RecognitionTestModelImpl::initializeWithDelayedMasker( +void RunningATestImpl::initializeWithDelayedMasker( TestMethod *testMethod_, const Test &test) { - initialize_(testMethod_, test); + initialize_(testMethod_, test, &nullObserver); useFirstChannelOnly(targetPlayer); maskerPlayer.setChannelDelaySeconds(0, maskerChannelDelay.seconds); } -void RecognitionTestModelImpl::initializeWithEyeTracking( - TestMethod *method, const Test &test) { - initialize_(method, test); - eyeTracking = true; -} - -void RecognitionTestModelImpl::initializeWithAudioRecording( - TestMethod *method, const Test &test) { - initialize_(method, test); - audioRecordingEnabled = true; -} - -void RecognitionTestModelImpl::playTrial(const AudioSettings &settings) { +void RunningATestImpl::playTrial(const AudioSettings &settings) { throwRequestFailureIfTrialInProgress(trialInProgress_); throwRequestFailureOnInvalidAudioDevice( @@ -382,66 +343,35 @@ void RecognitionTestModelImpl::playTrial(const AudioSettings &settings) { settings.audioDevice); playTrialTime_ = clock.time(); - if (audioRecordingEnabled) { - std::stringstream stream; - stream << trialNumber_ << '-' << session << ".wav"; - audioRecorder.initialize( - LocalUrl{outputFile.parentPath() / stream.str()}); - } - if (eyeTracking) { - eyeTracker.allocateRecordingTimeSeconds( - Duration{trialDuration(targetPlayer, maskerPlayer)}.seconds); - eyeTracker.start(); - } + observer->notifyThatTrialWillBegin(trialNumber_); if (condition == Condition::audioVisual) show(targetPlayer); targetPlayer.preRoll(); trialInProgress_ = true; } -void RecognitionTestModelImpl::notifyThatPreRollHasCompleted() { +void RunningATestImpl::notifyThatPreRollHasCompleted() { maskerPlayer.fadeIn(); } -void RecognitionTestModelImpl::fadeInComplete( - const AudioSampleTimeWithOffset &t) { - if (eyeTracking) { - PlayerTimeWithDelay timeToPlayWithDelay{}; - timeToPlayWithDelay.playerTime = t.playerTime; - timeToPlayWithDelay.delay = Delay{Duration{ - offsetDuration(maskerPlayer, t) + targetOnsetFringeDuration} - .seconds}; - targetPlayer.playAt(timeToPlayWithDelay); - - lastTargetStartTime.nanoseconds = - nanoseconds(maskerPlayer, timeToPlayWithDelay); - - lastEyeTrackerTargetPlayerSynchronization.eyeTrackerSystemTime = - eyeTracker.currentSystemTime(); - lastEyeTrackerTargetPlayerSynchronization.targetPlayerSystemTime = - TargetPlayerSystemTime{ - nanoseconds(maskerPlayer, maskerPlayer.currentSystemTime())}; - } else { - PlayerTimeWithDelay timeToPlayWithDelay{}; - timeToPlayWithDelay.playerTime = t.playerTime; - timeToPlayWithDelay.delay = Delay{Duration{ - offsetDuration(maskerPlayer, t) + targetOnsetFringeDuration} - .seconds}; - targetPlayer.playAt(timeToPlayWithDelay); - } +void RunningATestImpl::fadeInComplete(const AudioSampleTimeWithOffset &t) { + PlayerTimeWithDelay timeToPlayWithDelay{}; + timeToPlayWithDelay.playerTime = t.playerTime; + timeToPlayWithDelay.delay = Delay{ + Duration{offsetDuration(maskerPlayer, t) + targetOnsetFringeDuration} + .seconds}; + targetPlayer.playAt(timeToPlayWithDelay); + observer->notifyThatTargetWillPlayAt(timeToPlayWithDelay); } -void RecognitionTestModelImpl::fadeOutComplete() { +void RunningATestImpl::fadeOutComplete() { hide(targetPlayer); - if (eyeTracking) - eyeTracker.stop(); + observer->notifyThatStimulusHasEnded(); listener_->trialComplete(); trialInProgress_ = false; - if (audioRecordingEnabled) - audioRecorder.start(); } -void RecognitionTestModelImpl::submit( +void RunningATestImpl::submit( const coordinate_response_measure::Response &response) { saveOutputFileAndPrepareNextTrialAfter( [&]() { @@ -449,54 +379,46 @@ void RecognitionTestModelImpl::submit( testMethod->writeLastCoordinateResponse(outputFile); }, testMethod, trialNumber_, outputFile, randomizer, targetPlayer, - maskerPlayer, eyeTracker, maskerLevel_, fullScaleLevel_, eyeTracking, - lastTargetStartTime, lastEyeTrackerTargetPlayerSynchronization, - audioRecordingEnabled, audioRecorder); + maskerPlayer, observer, maskerLevel_, fullScaleLevel_); } -void RecognitionTestModelImpl::prepareNextTrialIfNeeded() { +void RunningATestImpl::prepareNextTrialIfNeeded() { av_speech_in_noise::prepareNextTrialIfNeeded(testMethod, trialNumber_, - outputFile, randomizer, targetPlayer, maskerPlayer, eyeTracker, - maskerLevel_, fullScaleLevel_, eyeTracking, lastTargetStartTime, - lastEyeTrackerTargetPlayerSynchronization, audioRecordingEnabled, - audioRecorder); + outputFile, randomizer, targetPlayer, maskerPlayer, observer, + maskerLevel_, fullScaleLevel_); } -void RecognitionTestModelImpl::playCalibration(const Calibration &calibration) { +void RunningATestImpl::playCalibration(const Calibration &calibration) { throwRequestFailureIfTrialInProgress(trialInProgress_); targetPlayer.useAllChannels(); play(targetPlayer, calibration); } -void RecognitionTestModelImpl::playLeftSpeakerCalibration( +void RunningATestImpl::playLeftSpeakerCalibration( const Calibration &calibration) { throwRequestFailureIfTrialInProgress(trialInProgress_); maskerPlayer.useFirstChannelOnly(); play(maskerPlayer, calibration); } -void RecognitionTestModelImpl::playRightSpeakerCalibration( +void RunningATestImpl::playRightSpeakerCalibration( const Calibration &calibration) { throwRequestFailureIfTrialInProgress(trialInProgress_); maskerPlayer.useSecondChannelOnly(); play(maskerPlayer, calibration); } -auto RecognitionTestModelImpl::testComplete() -> bool { - return testMethod->complete(); -} +auto RunningATestImpl::testComplete() -> bool { return testMethod->complete(); } -auto RecognitionTestModelImpl::audioDevices() -> AudioDevices { +auto RunningATestImpl::audioDevices() -> AudioDevices { return maskerPlayer.outputAudioDeviceDescriptions(); } -auto RecognitionTestModelImpl::trialNumber() -> int { return trialNumber_; } +auto RunningATestImpl::trialNumber() -> int { return trialNumber_; } -auto RecognitionTestModelImpl::targetFileName() -> std::string { +auto RunningATestImpl::targetFileName() -> std::string { return targetName(evaluator, testMethod); } -auto RecognitionTestModelImpl::playTrialTime() -> std::string { - return playTrialTime_; -} +auto RunningATestImpl::playTrialTime() -> std::string { return playTrialTime_; } } diff --git a/lib/core/src/SubmittingConsonant.cpp b/lib/core/src/SubmittingConsonant.cpp index c0cea5ee..8a1359ca 100644 --- a/lib/core/src/SubmittingConsonant.cpp +++ b/lib/core/src/SubmittingConsonant.cpp @@ -1,8 +1,8 @@ #include "SubmittingConsonant.hpp" namespace av_speech_in_noise::submitting_consonant { -InteractorImpl::InteractorImpl(FixedLevelMethod &method, - RecognitionTestModel &model, OutputFile &outputFile) +InteractorImpl::InteractorImpl( + FixedLevelMethod &method, RunningATest &model, OutputFile &outputFile) : method{method}, model{model}, outputFile{outputFile} {} void InteractorImpl::submit(const ConsonantResponse &r) { diff --git a/lib/core/src/SubmittingFreeResponse.cpp b/lib/core/src/SubmittingFreeResponse.cpp index e7e3bb4c..a6d2c10c 100644 --- a/lib/core/src/SubmittingFreeResponse.cpp +++ b/lib/core/src/SubmittingFreeResponse.cpp @@ -2,8 +2,8 @@ #include namespace av_speech_in_noise::submitting_free_response { -InteractorImpl::InteractorImpl(FixedLevelMethod &method, - RecognitionTestModel &model, OutputFile &outputFile) +InteractorImpl::InteractorImpl( + FixedLevelMethod &method, RunningATest &model, OutputFile &outputFile) : method{method}, model{model}, outputFile{outputFile} {} void InteractorImpl::submit(const FreeResponse &response) { diff --git a/lib/core/src/SubmittingKeywords.cpp b/lib/core/src/SubmittingKeywords.cpp index e97fa0c6..0f678e71 100644 --- a/lib/core/src/SubmittingKeywords.cpp +++ b/lib/core/src/SubmittingKeywords.cpp @@ -3,7 +3,7 @@ namespace av_speech_in_noise::submitting_keywords { InteractorImpl::InteractorImpl(FixedLevelMethod &method, - RecognitionTestModel &recognitionTestModel, OutputFile &outputFile) + RunningATest &recognitionTestModel, OutputFile &outputFile) : method{method}, recognitionTestModel{recognitionTestModel}, outputFile{outputFile} {} diff --git a/lib/core/src/SubmittingPassFail.cpp b/lib/core/src/SubmittingPassFail.cpp index 457e61ae..db280283 100644 --- a/lib/core/src/SubmittingPassFail.cpp +++ b/lib/core/src/SubmittingPassFail.cpp @@ -1,8 +1,8 @@ #include "SubmittingPassFail.hpp" namespace av_speech_in_noise::submitting_pass_fail { -InteractorImpl::InteractorImpl(AdaptiveMethod &adaptiveMethod, - RecognitionTestModel &model, OutputFile &outputFile) +InteractorImpl::InteractorImpl( + AdaptiveMethod &adaptiveMethod, RunningATest &model, OutputFile &outputFile) : adaptiveMethod{adaptiveMethod}, model{model}, outputFile{outputFile} {} void InteractorImpl::submitCorrectResponse() { diff --git a/lib/ui/include/av-speech-in-noise/ui/ChooseKeywords.hpp b/lib/ui/include/av-speech-in-noise/ui/ChooseKeywords.hpp index 9f418a22..80619b55 100644 --- a/lib/ui/include/av-speech-in-noise/ui/ChooseKeywords.hpp +++ b/lib/ui/include/av-speech-in-noise/ui/ChooseKeywords.hpp @@ -99,7 +99,7 @@ static auto operator==(const SentenceWithThreeKeywords &a, class PresenterImpl : public Presenter, public TaskPresenter { public: - PresenterImpl(Model &, TestView &, View &, + PresenterImpl(RunningATestFacade &, TestView &, View &, const std::vector &); void start() override; void stop() override; @@ -116,7 +116,7 @@ class PresenterImpl : public Presenter, public TaskPresenter { private: std::map sentencesWithThreeKeywordsFromExpectedFileNameSentence; - Model &model; + RunningATestFacade &model; TestView &testView; View &view; }; diff --git a/lib/ui/include/av-speech-in-noise/ui/CoordinateResponseMeasure.hpp b/lib/ui/include/av-speech-in-noise/ui/CoordinateResponseMeasure.hpp index 5947479f..a362129b 100644 --- a/lib/ui/include/av-speech-in-noise/ui/CoordinateResponseMeasure.hpp +++ b/lib/ui/include/av-speech-in-noise/ui/CoordinateResponseMeasure.hpp @@ -39,15 +39,15 @@ class CoordinateResponseMeasureController : public TaskController, public CoordinateResponseMeasureControl::Observer { public: - CoordinateResponseMeasureController( - TestController &, Model &, CoordinateResponseMeasureControl &); + CoordinateResponseMeasureController(TestController &, RunningATestFacade &, + CoordinateResponseMeasureControl &); void attach(TaskController::Observer *); void notifyThatReadyButtonHasBeenClicked() override; void notifyThatResponseButtonHasBeenClicked() override; private: TestController &testController; - Model &model; + RunningATestFacade &model; CoordinateResponseMeasureControl &control; TaskController::Observer *observer{}; }; diff --git a/lib/ui/include/av-speech-in-noise/ui/SessionController.hpp b/lib/ui/include/av-speech-in-noise/ui/SessionController.hpp index ae46991f..a1739303 100644 --- a/lib/ui/include/av-speech-in-noise/ui/SessionController.hpp +++ b/lib/ui/include/av-speech-in-noise/ui/SessionController.hpp @@ -33,7 +33,7 @@ class SessionControllerImpl : public SessionController { class SessionPresenterImpl : public SessionPresenter { public: - SessionPresenterImpl(SessionView &view, Model &model) { + SessionPresenterImpl(SessionView &view, RunningATestFacade &model) { view.populateAudioDeviceMenu(model.audioDevices()); view.populateSubjectScreenMenu(view.screens()); } diff --git a/lib/ui/include/av-speech-in-noise/ui/TestImpl.hpp b/lib/ui/include/av-speech-in-noise/ui/TestImpl.hpp index a0655207..db04315f 100644 --- a/lib/ui/include/av-speech-in-noise/ui/TestImpl.hpp +++ b/lib/ui/include/av-speech-in-noise/ui/TestImpl.hpp @@ -10,8 +10,8 @@ namespace av_speech_in_noise { class TestControllerImpl : public TestControl::Observer, public TestController { public: - TestControllerImpl(SessionController &, Model &, SessionControl &, - TestControl &, TestPresenter &); + TestControllerImpl(SessionController &, RunningATestFacade &, + SessionControl &, TestControl &, TestPresenter &); void exitTest() override; void playTrial() override; void declineContinuingTesting() override; @@ -24,7 +24,7 @@ class TestControllerImpl : public TestControl::Observer, public TestController { private: SessionController &sessionController; - Model &model; + RunningATestFacade &model; SessionControl &sessionControl; TestPresenter &presenter; }; @@ -72,10 +72,11 @@ class UninitializedTaskPresenterImpl : public UninitializedTaskPresenter { TaskPresenter *presenter{}; }; -class TestPresenterImpl : public Model::Observer, public TestPresenter { +class TestPresenterImpl : public RunningATestFacade::Observer, + public TestPresenter { public: explicit TestPresenterImpl( - Model &, TestView &, UninitializedTaskPresenter *); + RunningATestFacade &, TestView &, UninitializedTaskPresenter *); void initialize(TaskPresenter &) override; void start() override; void stop() override; @@ -88,7 +89,7 @@ class TestPresenterImpl : public Model::Observer, public TestPresenter { void completeTask() override; private: - Model &model; + RunningATestFacade &model; TestView &view; UninitializedTaskPresenter *taskPresenter; }; diff --git a/lib/ui/include/av-speech-in-noise/ui/TestSettingsInterpreter.hpp b/lib/ui/include/av-speech-in-noise/ui/TestSettingsInterpreter.hpp index b1fae176..482a7fd7 100644 --- a/lib/ui/include/av-speech-in-noise/ui/TestSettingsInterpreter.hpp +++ b/lib/ui/include/av-speech-in-noise/ui/TestSettingsInterpreter.hpp @@ -146,8 +146,8 @@ constexpr auto name(TestSetting p) -> const char * { class TestSettingsInterpreterImpl : public TestSettingsInterpreter { public: explicit TestSettingsInterpreterImpl(std::map); - void initialize(Model &, SessionController &, const std::string &, - const TestIdentity &, SNR) override; + void initialize(RunningATestFacade &, SessionController &, + const std::string &, const TestIdentity &, SNR) override; static auto meta(const std::string &) -> std::string; auto calibration(const std::string &) -> Calibration override; diff --git a/lib/ui/include/av-speech-in-noise/ui/TestSetupImpl.hpp b/lib/ui/include/av-speech-in-noise/ui/TestSetupImpl.hpp index 600cc88c..4473cc05 100644 --- a/lib/ui/include/av-speech-in-noise/ui/TestSetupImpl.hpp +++ b/lib/ui/include/av-speech-in-noise/ui/TestSetupImpl.hpp @@ -17,8 +17,8 @@ class TestSettingsInterpreter { public: AV_SPEECH_IN_NOISE_INTERFACE_SPECIAL_MEMBER_FUNCTIONS( TestSettingsInterpreter); - virtual void initialize(Model &, SessionController &, const std::string &, - const TestIdentity &, SNR) = 0; + virtual void initialize(RunningATestFacade &, SessionController &, + const std::string &, const TestIdentity &, SNR) = 0; virtual auto calibration(const std::string &) -> Calibration = 0; }; @@ -40,7 +40,7 @@ constexpr auto name(Transducer c) -> const char * { class TestSetupController : public TestSetupControl::Observer { public: TestSetupController(TestSetupControl &, SessionController &, - SessionControl &, TestSetupPresenter &, Model &, + SessionControl &, TestSetupPresenter &, RunningATestFacade &, TestSettingsInterpreter &, TextFileReader &); void notifyThatConfirmButtonHasBeenClicked() override; void notifyThatPlayCalibrationButtonHasBeenClicked() override; @@ -52,7 +52,7 @@ class TestSetupController : public TestSetupControl::Observer { SessionController &sessionController; SessionControl &sessionControl; TestSetupPresenter &presenter; - Model &model; + RunningATestFacade &model; TestSettingsInterpreter &testSettingsInterpreter; TextFileReader &textFileReader; }; diff --git a/lib/ui/src/ChooseKeywords.cpp b/lib/ui/src/ChooseKeywords.cpp index a1081942..8d08df6a 100644 --- a/lib/ui/src/ChooseKeywords.cpp +++ b/lib/ui/src/ChooseKeywords.cpp @@ -67,7 +67,8 @@ static auto transformToSentencesWithThreeKeywordsFromExpectedFileNameSentence( return map; } -PresenterImpl::PresenterImpl(Model &model, TestView &testView, View &view, +PresenterImpl::PresenterImpl(RunningATestFacade &model, TestView &testView, + View &view, const std::vector &sentencesWithThreeKeywords) : sentencesWithThreeKeywordsFromExpectedFileNameSentence{transformToSentencesWithThreeKeywordsFromExpectedFileNameSentence( sentencesWithThreeKeywords)}, @@ -83,7 +84,7 @@ void PresenterImpl::stop() { submitting_keywords::hideResponseSubmission(view); } -static auto mlstFileNameSentence(Model &model) -> std::string { +static auto mlstFileNameSentence(RunningATestFacade &model) -> std::string { std::stringstream stream{model.targetFileName()}; int ignore = 0; stream >> ignore >> std::ws; diff --git a/lib/ui/src/CoordinateResponseMeasure.cpp b/lib/ui/src/CoordinateResponseMeasure.cpp index 7cb87041..39a1fcac 100644 --- a/lib/ui/src/CoordinateResponseMeasure.cpp +++ b/lib/ui/src/CoordinateResponseMeasure.cpp @@ -22,7 +22,7 @@ static auto subjectResponse(CoordinateResponseMeasureControl &control) } CoordinateResponseMeasureController::CoordinateResponseMeasureController( - TestController &testController, Model &model, + TestController &testController, RunningATestFacade &model, CoordinateResponseMeasureControl &control) : testController{testController}, model{model}, control{control} { control.attach(this); diff --git a/lib/ui/src/TestImpl.cpp b/lib/ui/src/TestImpl.cpp index 30a81469..a3b10f86 100644 --- a/lib/ui/src/TestImpl.cpp +++ b/lib/ui/src/TestImpl.cpp @@ -9,8 +9,8 @@ static void readyNextTrial(TestPresenter &presenter) { } TestControllerImpl::TestControllerImpl(SessionController &sessionController, - Model &model, SessionControl &sessionControl, TestControl &control, - TestPresenter &presenter) + RunningATestFacade &model, SessionControl &sessionControl, + TestControl &control, TestPresenter &presenter) : sessionController{sessionController}, model{model}, sessionControl{sessionControl}, presenter{presenter} { control.attach(this); @@ -24,8 +24,8 @@ void TestControllerImpl::exitTest() { notifyThatTestIsComplete(sessionController); } -static void playTrial( - Model &model, SessionControl &control, TestPresenter &presenter) { +static void playTrial(RunningATestFacade &model, SessionControl &control, + TestPresenter &presenter) { model.playTrial(AudioSettings{control.audioDevice()}); presenter.notifyThatTrialHasStarted(); } @@ -43,21 +43,21 @@ void TestControllerImpl::acceptContinuingTesting() { readyNextTrial(presenter); } -static void ifTestCompleteElse(Model &model, const std::function &f, - const std::function &g) { +static void ifTestCompleteElse(RunningATestFacade &model, + const std::function &f, const std::function &g) { if (model.testComplete()) f(); else g(); } -static void readyNextTrialIfTestIncompleteElse( - Model &model, TestPresenter &presenter, const std::function &f) { +static void readyNextTrialIfTestIncompleteElse(RunningATestFacade &model, + TestPresenter &presenter, const std::function &f) { presenter.hideResponseSubmission(); ifTestCompleteElse(model, f, [&]() { readyNextTrial(presenter); }); } -static void notifyIfTestIsCompleteElse(Model &model, +static void notifyIfTestIsCompleteElse(RunningATestFacade &model, SessionController &controller, const std::function &f) { ifTestCompleteElse( model, [&]() { notifyThatTestIsComplete(controller); }, f); @@ -92,8 +92,8 @@ void TestControllerImpl::notifyThatUserIsReadyForNextTrial() { }); } -TestPresenterImpl::TestPresenterImpl( - Model &model, TestView &view, UninitializedTaskPresenter *taskPresenter) +TestPresenterImpl::TestPresenterImpl(RunningATestFacade &model, TestView &view, + UninitializedTaskPresenter *taskPresenter) : model{model}, view{view}, taskPresenter{taskPresenter} { model.attach(this); } diff --git a/lib/ui/src/TestSettingsInterpreter.cpp b/lib/ui/src/TestSettingsInterpreter.cpp index 4cff0cb1..e6a1e150 100644 --- a/lib/ui/src/TestSettingsInterpreter.cpp +++ b/lib/ui/src/TestSettingsInterpreter.cpp @@ -283,8 +283,9 @@ static void initializeFixedLevelTestWithEachTargetNTimes(Method method, f(test); } -static void initialize(Model &model, Method method, const std::string &contents, - const TestIdentity &identity, SNR startingSnr) { +static void initialize(RunningATestFacade &model, Method method, + const std::string &contents, const TestIdentity &identity, + SNR startingSnr) { switch (method) { case Method::adaptiveCoordinateResponseMeasureWithDelayedMasker: return av_speech_in_noise::initialize(method, contents, identity, @@ -370,7 +371,7 @@ static void initialize(Model &model, Method method, const std::string &contents, } } -void TestSettingsInterpreterImpl::initialize(Model &model, +void TestSettingsInterpreterImpl::initialize(RunningATestFacade &model, SessionController &sessionController, const std::string &contents, const TestIdentity &identity, SNR startingSnr) { const auto method{av_speech_in_noise::method(contents)}; diff --git a/lib/ui/src/TestSetupImpl.cpp b/lib/ui/src/TestSetupImpl.cpp index 7f3e6ff4..e283daf6 100644 --- a/lib/ui/src/TestSetupImpl.cpp +++ b/lib/ui/src/TestSetupImpl.cpp @@ -5,7 +5,7 @@ namespace av_speech_in_noise { TestSetupController::TestSetupController(TestSetupControl &control, SessionController &sessionController, SessionControl &sessionControl, - TestSetupPresenter &presenter, Model &model, + TestSetupPresenter &presenter, RunningATestFacade &model, TestSettingsInterpreter &testSettingsInterpreter, TextFileReader &textFileReader) : control{control}, sessionController{sessionController}, diff --git a/macos/AvFoundationPlayers.h b/macos/AvFoundationPlayers.h index 0d929e55..c89cc66d 100644 --- a/macos/AvFoundationPlayers.h +++ b/macos/AvFoundationPlayers.h @@ -2,6 +2,7 @@ #define AV_SPEECH_IN_NOISE_MACOS_MAIN_AVFOUNDATIONPLAYERS_H_ #include +#include #include #include #include diff --git a/macos/EyeTrackerStub.hpp b/macos/EyeTrackerStub.hpp index 9b13087b..db6eda60 100644 --- a/macos/EyeTrackerStub.hpp +++ b/macos/EyeTrackerStub.hpp @@ -1,7 +1,7 @@ #ifndef AV_SPEECH_IN_NOISE_MACOS_EYETRACKERSTUB_HPP_ #define AV_SPEECH_IN_NOISE_MACOS_EYETRACKERSTUB_HPP_ -#include +#include namespace av_speech_in_noise { class EyeTrackerStub : public EyeTracker { diff --git a/macos/TobiiProEyeTracker.hpp b/macos/TobiiProEyeTracker.hpp index c3feddd0..d26c9eb1 100644 --- a/macos/TobiiProEyeTracker.hpp +++ b/macos/TobiiProEyeTracker.hpp @@ -1,7 +1,7 @@ #ifndef AV_SPEECH_IN_NOISE_MACOS_TOBIIPROEYETRACKER_HPP_ #define AV_SPEECH_IN_NOISE_MACOS_TOBIIPROEYETRACKER_HPP_ -#include +#include #include #include diff --git a/macos/run.h b/macos/run.h index 906c5a02..1ce0e2f7 100644 --- a/macos/run.h +++ b/macos/run.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/macos/run.mm b/macos/run.mm index 68527e2b..534fbdd1 100644 --- a/macos/run.mm +++ b/macos/run.mm @@ -4,8 +4,10 @@ #include "AppKitView.h" #include "Foundation-utility.h" #include "AppKit-utility.h" -#include "av-speech-in-noise/playlist/PredeterminedTargetPlaylist.hpp" +#include "av-speech-in-noise/core/AudioRecording.hpp" +#include "av-speech-in-noise/core/EyeTracking.hpp" +#include #include #include #include @@ -306,9 +308,8 @@ void initializeAppAndRunEventLoop(EyeTracker &eyeTracker, static FixedLevelMethodImpl fixedLevelMethod{responseEvaluator}; static LocalTimeClock localTimeClock; static AvFoundationAudioRecorder audioRecorder; - static RecognitionTestModelImpl recognitionTestModel{targetPlayer, - maskerPlayer, audioRecorder, responseEvaluator, outputFile, randomizer, - eyeTracker, localTimeClock}; + static RunningATestImpl recognitionTestModel{targetPlayer, maskerPlayer, + responseEvaluator, outputFile, randomizer, localTimeClock}; static RandomizedTargetPlaylistWithReplacement::Factory targetsWithReplacementFactory{ &onlyIncludesTargetFileExtensions, &randomizer}; @@ -320,11 +321,14 @@ void initializeAppAndRunEventLoop(EyeTracker &eyeTracker, &cyclicTargetsFactory, &directoryReader}; static PredeterminedTargetPlaylist predeterminedTargetPlaylist{ textFileReader}; - static ModelImpl model{adaptiveMethod, fixedLevelMethod, + static AudioRecording audioRecording{audioRecorder, outputFile}; + static EyeTracking eyeTracking{ + eyeTracker, maskerPlayer, targetPlayer, outputFile}; + static RunningATestFacadeImpl model{adaptiveMethod, fixedLevelMethod, targetsWithReplacementReader, cyclicTargetsReader, targetsWithReplacement, silentIntervalTargets, everyTargetOnce, allTargetsNTimes, predeterminedTargetPlaylist, recognitionTestModel, - outputFile}; + outputFile, audioRecording, eyeTracking}; static const auto testSetupUI{testSetupUIFactory.make(nil)}; const auto consonantNSView{ [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)]}; diff --git a/test/AudioRecorderStub.hpp b/test/AudioRecorderStub.hpp new file mode 100644 index 00000000..cbf82e17 --- /dev/null +++ b/test/AudioRecorderStub.hpp @@ -0,0 +1,34 @@ +#ifndef AV_SPEECH_IN_NOISE_TEST_AUDIORECORDERSTUB_HPP_ +#define AV_SPEECH_IN_NOISE_TEST_AUDIORECORDERSTUB_HPP_ + +#include + +namespace av_speech_in_noise { +class AudioRecorderStub : public AudioRecorder { + public: + [[nodiscard]] auto started() const -> bool { return started_; } + + void start() override { started_ = true; } + + auto fileUrl() -> LocalUrl { return fileUrl_; } + + void initialize(const LocalUrl &url) override { + fileUrl_ = url; + initialized_ = true; + } + + [[nodiscard]] auto initialized() const -> bool { return initialized_; } + + [[nodiscard]] auto stopped() const -> bool { return stopped_; } + + void stop() override { stopped_ = true; } + + private: + LocalUrl fileUrl_; + bool started_{}; + bool initialized_{}; + bool stopped_{}; +}; +} + +#endif diff --git a/test/AudioRecording.cpp b/test/AudioRecording.cpp new file mode 100644 index 00000000..ae0cbd70 --- /dev/null +++ b/test/AudioRecording.cpp @@ -0,0 +1,36 @@ +#include "AudioRecorderStub.hpp" +#include "OutputFileStub.hpp" +#include "assert-utility.hpp" + +#include + +#include + +namespace av_speech_in_noise { +class AudioRecordingTests : public ::testing::Test { + protected: + AudioRecorderStub audioRecorder; + OutputFileStub outputFile; + AudioRecording audioRecording{audioRecorder, outputFile}; +}; + +#define AUDIO_RECORDING_TEST(a) TEST_F(AudioRecordingTests, a) + +AUDIO_RECORDING_TEST(initializesRecorderWhenTrialWillBegin) { + outputFile.setParentPath("/Users/user/data"); + audioRecording.notifyThatNewTestIsReady("smile"); + audioRecording.notifyThatTrialWillBegin(3); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL( + "/Users/user/data/3-smile.wav", audioRecorder.fileUrl().path); +} + +AUDIO_RECORDING_TEST(startsRecordingWhenStimulusEnds) { + audioRecording.notifyThatStimulusHasEnded(); + AV_SPEECH_IN_NOISE_EXPECT_TRUE(audioRecorder.started()); +} + +AUDIO_RECORDING_TEST(stopsRecordingWhenSubjectResponds) { + audioRecording.notifyThatSubjectHasResponded(); + AV_SPEECH_IN_NOISE_EXPECT_TRUE(audioRecorder.stopped()); +} +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0567685a..284112b0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,7 +29,9 @@ add_executable( EyeTrackerCalibrationInteractor.cpp EyeTrackerCalibrationController.cpp Subject.cpp - PredeterminedTargetPlaylist.cpp) + PredeterminedTargetPlaylist.cpp + AudioRecording.cpp + EyeTracking.cpp) target_compile_features(av-speech-in-noise-test-exe PRIVATE cxx_std_17) set_target_properties(av-speech-in-noise-test-exe PROPERTIES CXX_EXTENSIONS OFF) target_compile_options(av-speech-in-noise-test-exe diff --git a/test/EyeTrackerStub.hpp b/test/EyeTrackerStub.hpp new file mode 100644 index 00000000..d93af000 --- /dev/null +++ b/test/EyeTrackerStub.hpp @@ -0,0 +1,71 @@ +#ifndef AV_SPEECH_IN_NOISE_TEST_EYETRACKERSTUB_HPP_ +#define AV_SPEECH_IN_NOISE_TEST_EYETRACKERSTUB_HPP_ + +#include "LogString.hpp" + +#include + +#include + +namespace av_speech_in_noise { +class EyeTrackerStub : public EyeTracker { + public: + [[nodiscard]] auto recordingTimeAllocatedSeconds() const -> double { + return recordingTimeAllocatedSeconds_; + } + + [[nodiscard]] auto started() const -> bool { return started_; } + + [[nodiscard]] auto stopped() const -> bool { return stopped_; } + + [[nodiscard]] auto log() const -> const std::stringstream & { return log_; } + + void allocateRecordingTimeSeconds(double x) override { + insert(log_, "allocateRecordingTimeSeconds "); + recordingTimeAllocatedSeconds_ = x; + recordingTimeAllocated_ = true; + } + + void start() override { + insert(log_, "start "); + started_ = true; + } + + void stop() override { + insert(log_, "stop "); + stopped_ = true; + } + + [[nodiscard]] auto recordingTimeAllocated() const -> bool { + return recordingTimeAllocated_; + } + + auto gazeSamples() -> BinocularGazeSamples override { + insert(log_, "gazeSamples "); + return gazeSamples_; + } + + void setGazes(BinocularGazeSamples g) { gazeSamples_ = std::move(g); } + + auto currentSystemTime() -> EyeTrackerSystemTime override { + return currentSystemTime_; + } + + void setCurrentSystemTime(EyeTrackerSystemTime t) { + currentSystemTime_ = t; + } + + void write(std::ostream &) override {} + + private: + BinocularGazeSamples gazeSamples_; + std::stringstream log_{}; + EyeTrackerSystemTime currentSystemTime_{}; + double recordingTimeAllocatedSeconds_{}; + bool recordingTimeAllocated_{}; + bool started_{}; + bool stopped_{}; +}; +} + +#endif diff --git a/test/EyeTracking.cpp b/test/EyeTracking.cpp new file mode 100644 index 00000000..4bf5d982 --- /dev/null +++ b/test/EyeTracking.cpp @@ -0,0 +1,137 @@ +#include "OutputFileStub.hpp" +#include "EyeTrackerStub.hpp" +#include "MaskerPlayerStub.hpp" +#include "TargetPlayerStub.hpp" +#include "assert-utility.hpp" +#include "av-speech-in-noise/Model.hpp" +#include "av-speech-in-noise/core/Player.hpp" + +#include +#include + +#include + +namespace av_speech_in_noise { +constexpr auto operator==(const Point2D &a, const Point2D &b) -> bool { + return a.x == b.x && a.y == b.y; +} + +constexpr auto operator==(const Point3D &a, const Point3D &b) -> bool { + return a.x == b.x && a.y == b.y && a.z == b.z; +} + +constexpr auto operator==(const GazePosition &a, const GazePosition &b) + -> bool { + return a.relativeTrackbox == b.relativeTrackbox && + a.relativeScreen == b.relativeScreen; +} + +constexpr auto operator==(const GazeOrigin &a, const GazeOrigin &b) -> bool { + return a.relativeTrackbox == b.relativeTrackbox; +} + +constexpr auto operator==(const Gaze &a, const Gaze &b) -> bool { + return a.origin == b.origin && a.position == b.position; +} + +constexpr auto operator==( + const BinocularGazeSample &a, const BinocularGazeSample &b) -> bool { + return a.systemTime.microseconds == b.systemTime.microseconds && + a.left == b.left && a.right == b.right; +} + +class EyeTrackingTests : public ::testing::Test { + protected: + EyeTrackerStub eyeTracker; + MaskerPlayerStub maskerPlayer; + TargetPlayerStub targetPlayer; + OutputFileStub outputFile; + EyeTracking eyeTracking{eyeTracker, maskerPlayer, targetPlayer, outputFile}; +}; + +#define EYE_TRACKING_TEST(a) TEST_F(EyeTrackingTests, a) + +EYE_TRACKING_TEST( + playTrialForTestWithEyeTrackingAllocatesTrialDurationsWorthRecordingTimeForEyeTracking) { + targetPlayer.setDurationSeconds(3); + maskerPlayer.setFadeTimeSeconds(4); + eyeTracking.notifyThatTrialWillBegin(1); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL(3 + 2 * 4. + + RunningATestImpl::targetOnsetFringeDuration.seconds + + RunningATestImpl::targetOffsetFringeDuration.seconds, + eyeTracker.recordingTimeAllocatedSeconds()); +} + +EYE_TRACKING_TEST(startsTrackerWhenTrialWillBegin) { + eyeTracking.notifyThatTrialWillBegin(1); + AV_SPEECH_IN_NOISE_EXPECT_TRUE(eyeTracker.started()); +} + +EYE_TRACKING_TEST(startsTrackerAfterAllocatingRecordingTime) { + eyeTracking.notifyThatTrialWillBegin(1); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL( + std::string{"allocateRecordingTimeSeconds start "}, + string(eyeTracker.log())); +} + +EYE_TRACKING_TEST(stopsTrackerWhenStimulusHasEnded) { + eyeTracking.notifyThatStimulusHasEnded(); + AV_SPEECH_IN_NOISE_EXPECT_TRUE(eyeTracker.stopped()); +} + +EYE_TRACKING_TEST(passesTargetStartSystemTimeForConversion) { + PlayerTimeWithDelay playerTime; + playerTime.playerTime.system = 1; + eyeTracking.notifyThatTargetWillPlayAt(playerTime); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL(player_system_time_type{1}, + maskerPlayer.toNanosecondsSystemTime().at(0)); +} + +EYE_TRACKING_TEST(submittingCoordinateResponseWritesEyeGazes) { + eyeTracker.setGazes({{{1}, {{}, {{}, {2, 3}}}, {{}, {{}, {4, 5}}}}, + {{6}, {{}, {{}, {7, 8}}}, {{}, {{}, {9, 10}}}}}); + eyeTracking.notifyThatSubjectHasResponded(); + ::assertEqual({{{1}, {{}, {{}, {2, 3}}}, {{}, {{}, {4, 5}}}}, + {{6}, {{}, {{}, {7, 8}}}, {{}, {{}, {9, 10}}}}}, + outputFile.eyeGazes()); +} + +EYE_TRACKING_TEST( + submitCoordinateResponseWritesTargetStartTimeWhenEyeTracking) { + maskerPlayer.setNanosecondsFromPlayerTime(1); + PlayerTimeWithDelay playerTime; + playerTime.delay.seconds = 2; + eyeTracking.notifyThatTargetWillPlayAt(playerTime); + eyeTracking.notifyThatSubjectHasResponded(); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL( + 1 + gsl::narrow_cast(2 * 1e9), + outputFile.targetStartTime().nanoseconds); +} + +static auto eyeTrackerTargetPlayerSynchronization(OutputFileStub &file) + -> EyeTrackerTargetPlayerSynchronization { + return file.eyeTrackerTargetPlayerSynchronization(); +} + +EYE_TRACKING_TEST(submitCoordinateResponseWritesSyncTimes) { + maskerPlayer.setNanosecondsFromPlayerTime(1); + eyeTracker.setCurrentSystemTime(EyeTrackerSystemTime{2}); + eyeTracking.notifyThatTargetWillPlayAt({}); + eyeTracking.notifyThatSubjectHasResponded(); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL(std::uintmax_t{1}, + eyeTrackerTargetPlayerSynchronization(outputFile) + .targetPlayerSystemTime.nanoseconds); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL(std::int_least64_t{2}, + eyeTrackerTargetPlayerSynchronization(outputFile) + .eyeTrackerSystemTime.microseconds); +} + +EYE_TRACKING_TEST(passesCurrentMaskerTimeForNanosecondConversion) { + av_speech_in_noise::PlayerTime t{}; + t.system = 1; + maskerPlayer.setCurrentSystemTime(t); + eyeTracking.notifyThatTargetWillPlayAt({}); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL(player_system_time_type{1}, + maskerPlayer.toNanosecondsSystemTime().at(1)); +} +} diff --git a/test/MaskerPlayerStub.hpp b/test/MaskerPlayerStub.hpp index f68a81c0..ddbae183 100644 --- a/test/MaskerPlayerStub.hpp +++ b/test/MaskerPlayerStub.hpp @@ -2,7 +2,9 @@ #define TESTS_MASKERPLAYERSTUB_HPP_ #include "LogString.hpp" -#include + +#include + #include namespace av_speech_in_noise { diff --git a/test/Model.cpp b/test/Model.cpp index dd61d665..5dba1f1c 100644 --- a/test/Model.cpp +++ b/test/Model.cpp @@ -2,9 +2,9 @@ #include "ModelObserverStub.hpp" #include "TargetPlaylistStub.hpp" #include "TargetPlaylistSetReaderStub.hpp" +#include "AudioRecorderStub.hpp" #include "assert-utility.hpp" -#include #include #include #include @@ -24,6 +24,19 @@ static auto operator==(const AdaptiveTestResult &a, const AdaptiveTestResult &b) return a.targetsUrl.path == b.targetsUrl.path && a.threshold == b.threshold; } +class RunningATestObserverStub : public RunningATest::Observer { + public: + void notifyThatNewTestIsReady(std::string_view session) override {} + + void notifyThatTrialWillBegin(int trialNumber) override {} + + void notifyThatTargetWillPlayAt(const PlayerTimeWithDelay &) override {} + + void notifyThatStimulusHasEnded() override {} + + void notifyThatSubjectHasResponded() override {} +}; + namespace { class AdaptiveMethodStub : public AdaptiveMethod { public: @@ -198,7 +211,7 @@ class FixedLevelMethodStub : public FixedLevelMethod { bool submittedFreeResponse_{}; }; -class RecognitionTestModelStub : public RecognitionTestModel { +class RecognitionTestModelStub : public RunningATest { public: explicit RecognitionTestModelStub(AdaptiveMethodStub &adaptiveMethod, FixedLevelMethodStub &fixedLevelMethodStub) @@ -216,9 +229,11 @@ class RecognitionTestModelStub : public RecognitionTestModel { fixedLevelMethodStub.log("TOOLATE "); } - void initialize(TestMethod *method, const Test &test) override { + void initialize( + TestMethod *method, const Test &test, Observer *observer) override { testMethod_ = method; test_ = &test; + this->observer = observer; } void initializeWithSingleSpeaker( @@ -235,19 +250,6 @@ class RecognitionTestModelStub : public RecognitionTestModel { initializedWithDelayedMasker_ = true; } - void initializeWithEyeTracking( - TestMethod *method, const Test &test) override { - testMethod_ = method; - test_ = &test; - initializedWithEyeTracking_ = true; - } - - void initializeWithAudioRecording( - TestMethod *method, const Test &test) override { - testMethod_ = method; - test_ = &test; - } - auto trialNumber() -> int override { return trialNumber_; } void setTrialNumber(int n) { trialNumber_ = n; } @@ -268,7 +270,7 @@ class RecognitionTestModelStub : public RecognitionTestModel { return audioDevices_; } - void attach(Model::Observer *e) override { listener_ = e; } + void attach(RunningATestFacade::Observer *e) override { listener_ = e; } void playCalibration(const Calibration &c) override { calibration_ = &c; } @@ -288,10 +290,6 @@ class RecognitionTestModelStub : public RecognitionTestModel { return initializedWithDelayedMasker_; } - [[nodiscard]] auto initializedWithEyeTracking() const -> bool { - return initializedWithEyeTracking_; - } - [[nodiscard]] auto coordinateResponse() const { return coordinateResponse_; } @@ -324,13 +322,15 @@ class RecognitionTestModelStub : public RecognitionTestModel { void setPlayTrialTime(std::string s) { playTrialTime_ = std::move(s); } + const Observer *observer{}; + private: std::vector audioDevices_{}; std::string targetFileName_{}; std::string playTrialTime_; AdaptiveMethodStub &adaptiveMethod; FixedLevelMethodStub &fixedLevelMethodStub; - const Model::Observer *listener_{}; + const RunningATestFacade::Observer *listener_{}; const Calibration *calibration_{}; const Calibration *leftSpeakerCalibration_{}; const Calibration *rightSpeakerCalibration_{}; @@ -342,77 +342,82 @@ class RecognitionTestModelStub : public RecognitionTestModel { bool complete_{}; bool initializedWithSingleSpeaker_{}; bool initializedWithDelayedMasker_{}; - bool initializedWithEyeTracking_{}; bool nextTrialPreparedIfNeeded_{}; }; class InitializingTestUseCase { public: virtual ~InitializingTestUseCase() = default; - virtual void run(ModelImpl &) = 0; + virtual void run(RunningATestFacadeImpl &) = 0; virtual auto test() -> const Test & = 0; virtual auto testMethod() -> const TestMethod * = 0; }; class InitializingFixedLevelTest : public virtual InitializingTestUseCase { public: - virtual void run(ModelImpl &model, const FixedLevelTest &test) = 0; + virtual void run( + RunningATestFacadeImpl &model, const FixedLevelTest &test) = 0; }; class InitializingFixedLevelFixedTrialsTest : public virtual InitializingTestUseCase { public: - virtual void run( - ModelImpl &model, const FixedLevelFixedTrialsTest &test) = 0; + virtual void run(RunningATestFacadeImpl &model, + const FixedLevelFixedTrialsTest &test) = 0; }; class InitializingAdaptiveTest : public virtual InitializingTestUseCase { public: - virtual void run(ModelImpl &model, const AdaptiveTest &test) = 0; + virtual void run( + RunningATestFacadeImpl &model, const AdaptiveTest &test) = 0; }; -void initialize(ModelImpl &model, const AdaptiveTest &test) { +void initialize(RunningATestFacadeImpl &model, const AdaptiveTest &test) { model.initialize(test); } void initializeWithTargetReplacement( - ModelImpl &model, const FixedLevelFixedTrialsTest &test) { + RunningATestFacadeImpl &model, const FixedLevelFixedTrialsTest &test) { model.initializeWithTargetReplacement(test); } -void initializeWithSingleSpeaker(ModelImpl &model, const AdaptiveTest &test) { +void initializeWithSingleSpeaker( + RunningATestFacadeImpl &model, const AdaptiveTest &test) { model.initializeWithSingleSpeaker(test); } -void initializeWithDelayedMasker(ModelImpl &model, const AdaptiveTest &test) { +void initializeWithDelayedMasker( + RunningATestFacadeImpl &model, const AdaptiveTest &test) { model.initializeWithDelayedMasker(test); } -void initializeWithCyclicTargets(ModelImpl &model, const AdaptiveTest &test) { +void initializeWithCyclicTargets( + RunningATestFacadeImpl &model, const AdaptiveTest &test) { model.initializeWithCyclicTargets(test); } void initializeWithCyclicTargetsAndEyeTracking( - ModelImpl &model, const AdaptiveTest &test) { + RunningATestFacadeImpl &model, const AdaptiveTest &test) { model.initializeWithCyclicTargetsAndEyeTracking(test); } void initializeWithSilentIntervalTargets( - ModelImpl &model, const FixedLevelTest &test) { + RunningATestFacadeImpl &model, const FixedLevelTest &test) { model.initializeWithSilentIntervalTargets(test); } void initializeWithTargetReplacementAndEyeTracking( - ModelImpl &model, const FixedLevelFixedTrialsTest &test) { + RunningATestFacadeImpl &model, const FixedLevelFixedTrialsTest &test) { model.initializeWithTargetReplacementAndEyeTracking(test); } void initializeWithSilentIntervalTargetsAndEyeTracking( - ModelImpl &model, const FixedLevelTest &test) { + RunningATestFacadeImpl &model, const FixedLevelTest &test) { model.initializeWithSilentIntervalTargetsAndEyeTracking(test); } -void initializeWithEyeTracking(ModelImpl &model, const AdaptiveTest &test) { +void initializeWithEyeTracking( + RunningATestFacadeImpl &model, const AdaptiveTest &test) { model.initializeWithEyeTracking(test); } @@ -424,9 +429,11 @@ class InitializingDefaultAdaptiveTest : public InitializingAdaptiveTest { explicit InitializingDefaultAdaptiveTest(AdaptiveMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { initialize(model, test_); } + void run(RunningATestFacadeImpl &model) override { + initialize(model, test_); + } - void run(ModelImpl &model, const AdaptiveTest &test) override { + void run(RunningATestFacadeImpl &model, const AdaptiveTest &test) override { initialize(model, test); } @@ -444,11 +451,11 @@ class InitializingAdaptiveTestWithEyeTracking explicit InitializingAdaptiveTestWithEyeTracking(AdaptiveMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { initializeWithEyeTracking(model, test_); } - void run(ModelImpl &model, const AdaptiveTest &test) override { + void run(RunningATestFacadeImpl &model, const AdaptiveTest &test) override { initializeWithEyeTracking(model, test); } @@ -467,11 +474,11 @@ class InitializingAdaptiveTestWithSingleSpeaker AdaptiveMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { initializeWithSingleSpeaker(model, test_); } - void run(ModelImpl &model, const AdaptiveTest &test) override { + void run(RunningATestFacadeImpl &model, const AdaptiveTest &test) override { initializeWithSingleSpeaker(model, test); } @@ -490,11 +497,11 @@ class InitializingAdaptiveTestWithDelayedMasker AdaptiveMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { initializeWithDelayedMasker(model, test_); } - void run(ModelImpl &model, const AdaptiveTest &test) override { + void run(RunningATestFacadeImpl &model, const AdaptiveTest &test) override { initializeWithDelayedMasker(model, test); } @@ -513,11 +520,11 @@ class InitializingAdaptiveTestWithCyclicTargets AdaptiveMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { initializeWithCyclicTargets(model, test_); } - void run(ModelImpl &model, const AdaptiveTest &test) override { + void run(RunningATestFacadeImpl &model, const AdaptiveTest &test) override { initializeWithCyclicTargets(model, test); } @@ -536,11 +543,11 @@ class InitializingAdaptiveTestWithCyclicTargetsAndEyeTracking AdaptiveMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { initializeWithCyclicTargetsAndEyeTracking(model, test_); } - void run(ModelImpl &model, const AdaptiveTest &test) override { + void run(RunningATestFacadeImpl &model, const AdaptiveTest &test) override { initializeWithCyclicTargetsAndEyeTracking(model, test); } @@ -559,11 +566,12 @@ class InitializingFixedLevelTestWithTargetReplacement FixedLevelMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { initializeWithTargetReplacement(model, test_); } - void run(ModelImpl &model, const FixedLevelFixedTrialsTest &test) override { + void run(RunningATestFacadeImpl &model, + const FixedLevelFixedTrialsTest &test) override { initializeWithTargetReplacement(model, test); } @@ -582,11 +590,12 @@ class InitializingFixedLevelTestWithSilentIntervalTargets FixedLevelMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { initializeWithSilentIntervalTargets(model, test_); } - void run(ModelImpl &model, const FixedLevelTest &test) override { + void run( + RunningATestFacadeImpl &model, const FixedLevelTest &test) override { initializeWithSilentIntervalTargets(model, test); } @@ -605,11 +614,12 @@ class InitializingFixedLevelTestWithTargetReplacementAndEyeTracking FixedLevelMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { initializeWithTargetReplacementAndEyeTracking(model, test_); } - void run(ModelImpl &model, const FixedLevelFixedTrialsTest &test) override { + void run(RunningATestFacadeImpl &model, + const FixedLevelFixedTrialsTest &test) override { initializeWithTargetReplacementAndEyeTracking(model, test); } @@ -628,11 +638,12 @@ class InitializingFixedLevelTestWithSilentIntervalTargetsAndEyeTracking FixedLevelMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { initializeWithSilentIntervalTargetsAndEyeTracking(model, test_); } - void run(ModelImpl &model, const FixedLevelTest &test) override { + void run( + RunningATestFacadeImpl &model, const FixedLevelTest &test) override { initializeWithSilentIntervalTargetsAndEyeTracking(model, test); } @@ -651,11 +662,12 @@ class InitializingFixedLevelTestWithAllTargets FixedLevelMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { model.initializeWithAllTargets(test_); } - void run(ModelImpl &model, const FixedLevelTest &test) override { + void run( + RunningATestFacadeImpl &model, const FixedLevelTest &test) override { model.initializeWithAllTargets(test); } @@ -674,11 +686,12 @@ class InitializingFixedLevelTestWithAllTargetsAndEyeTracking FixedLevelMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { model.initializeWithAllTargetsAndEyeTracking(test_); } - void run(ModelImpl &model, const FixedLevelTest &test) override { + void run( + RunningATestFacadeImpl &model, const FixedLevelTest &test) override { model.initializeWithAllTargetsAndEyeTracking(test); } @@ -697,9 +710,12 @@ class InitializingFixedLevelTestWithEachTargetNTimes FixedLevelMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { model.initialize(test_); } + void run(RunningATestFacadeImpl &model) override { + model.initialize(test_); + } - void run(ModelImpl &model, const FixedLevelTestWithEachTargetNTimes &test) { + void run(RunningATestFacadeImpl &model, + const FixedLevelTestWithEachTargetNTimes &test) { model.initialize(test); } @@ -718,11 +734,12 @@ class InitializingFixedLevelTestWithAllTargetsAndAudioRecording FixedLevelMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { model.initializeWithAllTargetsAndAudioRecording(test_); } - void run(ModelImpl &model, const FixedLevelTest &test) override { + void run( + RunningATestFacadeImpl &model, const FixedLevelTest &test) override { model.initializeWithAllTargetsAndAudioRecording(test); } @@ -741,11 +758,12 @@ class InitializingFixedLevelTestWithPredeterminedTargetsAndAudioRecording FixedLevelMethodStub *method) : method{method} {} - void run(ModelImpl &model) override { + void run(RunningATestFacadeImpl &model) override { model.initializeWithPredeterminedTargetsAndAudioRecording(test_); } - void run(ModelImpl &model, const FixedLevelTest &test) override { + void run( + RunningATestFacadeImpl &model, const FixedLevelTest &test) override { model.initializeWithPredeterminedTargetsAndAudioRecording(test); } @@ -754,21 +772,22 @@ class InitializingFixedLevelTestWithPredeterminedTargetsAndAudioRecording auto testMethod() -> const TestMethod * override { return method; } }; -auto initializedWithEyeTracking(RecognitionTestModelStub &m) -> bool { - return m.initializedWithEyeTracking(); +auto initializedWithEyeTracking( + RecognitionTestModelStub &m, RunningATest::Observer *observer) -> bool { + return m.observer == observer; } class PlayingCalibrationUseCase { public: virtual ~PlayingCalibrationUseCase() = default; - virtual void run(Model &model, const Calibration &c) = 0; + virtual void run(RunningATestFacade &model, const Calibration &c) = 0; virtual auto calibration(RecognitionTestModelStub &model) -> const Calibration * = 0; }; class PlayingCalibration : public PlayingCalibrationUseCase { public: - void run(Model &model, const Calibration &c) override { + void run(RunningATestFacade &model, const Calibration &c) override { model.playCalibration(c); } @@ -780,7 +799,7 @@ class PlayingCalibration : public PlayingCalibrationUseCase { class PlayingLeftSpeakerCalibration : public PlayingCalibrationUseCase { public: - void run(Model &model, const Calibration &c) override { + void run(RunningATestFacade &model, const Calibration &c) override { model.playLeftSpeakerCalibration(c); } @@ -792,7 +811,7 @@ class PlayingLeftSpeakerCalibration : public PlayingCalibrationUseCase { class PlayingRightSpeakerCalibration : public PlayingCalibrationUseCase { public: - void run(Model &model, const Calibration &c) override { + void run(RunningATestFacade &model, const Calibration &c) override { model.playRightSpeakerCalibration(c); } @@ -815,10 +834,13 @@ class ModelTests : public ::testing::Test { RepeatableFiniteTargetPlaylistStub eachTargetNTimes; RecognitionTestModelStub internalModel{adaptiveMethod, fixedLevelMethod}; OutputFileStub outputFile; - ModelImpl model{adaptiveMethod, fixedLevelMethod, + RunningATestObserverStub audioRecording; + RunningATestObserverStub eyeTracking; + RunningATestFacadeImpl model{adaptiveMethod, fixedLevelMethod, targetsWithReplacementReader, cyclicTargetsReader, targetsWithReplacement, silentIntervals, everyTargetOnce, - eachTargetNTimes, predeterminedTargets, internalModel, outputFile}; + eachTargetNTimes, predeterminedTargets, internalModel, outputFile, + audioRecording, eyeTracking}; AdaptiveTest adaptiveTest; FixedLevelTest fixedLevelTest; FixedLevelTestWithEachTargetNTimes fixedLevelTestWithEachTargetNTimes; @@ -1472,33 +1494,50 @@ MODEL_TEST(initializeAdaptiveTestWithDelayedMaskerInitializesSingleSpeaker) { internalModel.initializedWithDelayedMasker()); } +MODEL_TEST( + initializeFixedLevelTestWithAllTargetsAndAudioRecordingInitializesWithAudioRecording) { + run(initializingFixedLevelTestWithAllTargetsAndAudioRecording); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL(internalModel.observer, &audioRecording); +} + +MODEL_TEST( + initializeFixedLevelTestWithPredeterminedTargetsAndAudioRecordingInitializesWithAudioRecording) { + run(initializingFixedLevelTestWithPredeterminedTargetsAndAudioRecording); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL(internalModel.observer, &audioRecording); +} + MODEL_TEST( initializeFixedLevelTestWithAllTargetsAndEyeTrackingInitializesWithEyeTracking) { run(initializingFixedLevelTestWithAllTargetsAndEyeTracking); - AV_SPEECH_IN_NOISE_EXPECT_TRUE(initializedWithEyeTracking(internalModel)); + AV_SPEECH_IN_NOISE_EXPECT_TRUE( + initializedWithEyeTracking(internalModel, &eyeTracking)); } MODEL_TEST(initializeAdaptiveTestWithEyeTrackingInitializesWithEyeTracking) { run(initializingAdaptiveTestWithEyeTracking); - AV_SPEECH_IN_NOISE_EXPECT_TRUE(initializedWithEyeTracking(internalModel)); + AV_SPEECH_IN_NOISE_EXPECT_TRUE( + initializedWithEyeTracking(internalModel, &eyeTracking)); } MODEL_TEST( initializeAdaptiveTestWithCyclicTargetsAndEyeTrackingInitializesWithEyeTracking) { run(initializingAdaptiveTestWithCyclicTargetsAndEyeTracking); - AV_SPEECH_IN_NOISE_EXPECT_TRUE(initializedWithEyeTracking(internalModel)); + AV_SPEECH_IN_NOISE_EXPECT_TRUE( + initializedWithEyeTracking(internalModel, &eyeTracking)); } MODEL_TEST( initializeFixedLevelTestWithTargetReplacementAndEyeTrackingInitializesWithEyeTracking) { run(initializingFixedLevelTestWithTargetReplacementAndEyeTracking); - AV_SPEECH_IN_NOISE_EXPECT_TRUE(initializedWithEyeTracking(internalModel)); + AV_SPEECH_IN_NOISE_EXPECT_TRUE( + initializedWithEyeTracking(internalModel, &eyeTracking)); } MODEL_TEST( initializeFixedLevelTestWithSilentIntervalTargetsAndEyeTrackingInitializesWithEyeTracking) { run(initializingFixedLevelTestWithSilentIntervalTargetsAndEyeTracking); - AV_SPEECH_IN_NOISE_EXPECT_TRUE(initializedWithEyeTracking(internalModel)); + AV_SPEECH_IN_NOISE_EXPECT_TRUE( + initializedWithEyeTracking(internalModel, &eyeTracking)); } MODEL_TEST(submitResponsePassesCoordinateResponse) { @@ -1569,7 +1608,7 @@ MODEL_TEST(subscribesToListener) { ModelObserverStub listener; model.attach(&listener); AV_SPEECH_IN_NOISE_EXPECT_EQUAL( - static_cast(&listener), + static_cast(&listener), internalModel.listener()); } } diff --git a/test/ModelObserverStub.hpp b/test/ModelObserverStub.hpp index f3c5dd2d..ff184bd3 100644 --- a/test/ModelObserverStub.hpp +++ b/test/ModelObserverStub.hpp @@ -4,7 +4,7 @@ #include namespace av_speech_in_noise { -class ModelObserverStub : public Model::Observer { +class ModelObserverStub : public RunningATestFacade::Observer { bool notified_{}; public: diff --git a/test/ModelStub.hpp b/test/ModelStub.hpp index 453fc361..47940bf2 100644 --- a/test/ModelStub.hpp +++ b/test/ModelStub.hpp @@ -7,7 +7,7 @@ #include namespace av_speech_in_noise { -class ModelStub : public Model { +class ModelStub : public RunningATestFacade { public: void setAdaptiveTestResults(AdaptiveTestResults v) { adaptiveTestResults_ = std::move(v); diff --git a/test/OutputFileStub.hpp b/test/OutputFileStub.hpp index 9521c09a..c0d4aa60 100644 --- a/test/OutputFileStub.hpp +++ b/test/OutputFileStub.hpp @@ -2,14 +2,16 @@ #define TESTS_OUTPUTFILESTUB_HPP_ #include "LogString.hpp" + #include + #include #include namespace av_speech_in_noise { class OutputFileStub : public OutputFile { public: - auto parentPath() -> std::filesystem::path { return parentPath_; } + auto parentPath() -> std::filesystem::path override { return parentPath_; } void setParentPath(std::filesystem::path path) { parentPath_ = std::move(path); diff --git a/test/RecognitionTestModel.cpp b/test/RecognitionTestModel.cpp index 0b15a2ed..7b22caeb 100644 --- a/test/RecognitionTestModel.cpp +++ b/test/RecognitionTestModel.cpp @@ -6,9 +6,11 @@ #include "ResponseEvaluatorStub.hpp" #include "TargetPlayerStub.hpp" #include "assert-utility.hpp" -#include "av-speech-in-noise/Model.hpp" + #include + #include + #include #include #include @@ -18,34 +20,6 @@ constexpr auto operator==(const EyeGaze &a, const EyeGaze &b) -> bool { return a.x == b.x && a.y == b.y; } -constexpr auto operator==(const Point2D &a, const Point2D &b) -> bool { - return a.x == b.x && a.y == b.y; -} - -constexpr auto operator==(const Point3D &a, const Point3D &b) -> bool { - return a.x == b.x && a.y == b.y && a.z == b.z; -} - -constexpr auto operator==(const GazeOrigin &a, const GazeOrigin &b) -> bool { - return a.relativeTrackbox == b.relativeTrackbox; -} - -constexpr auto operator==(const GazePosition &a, const GazePosition &b) - -> bool { - return a.relativeTrackbox == b.relativeTrackbox && - a.relativeScreen == b.relativeScreen; -} - -constexpr auto operator==(const Gaze &a, const Gaze &b) -> bool { - return a.origin == b.origin && a.position == b.position; -} - -constexpr auto operator==( - const BinocularGazeSample &a, const BinocularGazeSample &b) -> bool { - return a.systemTime.microseconds == b.systemTime.microseconds && - a.left == b.left && a.right == b.right; -} - namespace { class TestMethodStub : public TestMethod { public: @@ -100,21 +74,23 @@ class TestMethodStub : public TestMethod { class UseCase { public: virtual ~UseCase() = default; - virtual void run(RecognitionTestModelImpl &) = 0; + virtual void run(RunningATestImpl &) = 0; }; class InitializingTest : public UseCase { public: - explicit InitializingTest(TestMethod *method, const Test &test) - : test{test}, method{method} {} + explicit InitializingTest( + TestMethod *method, const Test &test, RunningATest::Observer *observer) + : test{test}, method{method}, observer{observer} {} - void run(RecognitionTestModelImpl &m) override { - m.initialize(method, test); + void run(RunningATestImpl &m) override { + m.initialize(method, test, observer); } private: const Test &test{}; TestMethod *method; + RunningATest::Observer *observer; }; class InitializingTestWithSingleSpeaker : public UseCase { @@ -122,7 +98,7 @@ class InitializingTestWithSingleSpeaker : public UseCase { explicit InitializingTestWithSingleSpeaker(TestMethod *method) : method{method} {} - void run(RecognitionTestModelImpl &m) override { + void run(RunningATestImpl &m) override { m.initializeWithSingleSpeaker(method, {}); } @@ -135,7 +111,7 @@ class InitializingTestWithDelayedMasker : public UseCase { explicit InitializingTestWithDelayedMasker(TestMethod *method) : method{method} {} - void run(RecognitionTestModelImpl &m) override { + void run(RunningATestImpl &m) override { m.initializeWithDelayedMasker(method, {}); } @@ -143,33 +119,6 @@ class InitializingTestWithDelayedMasker : public UseCase { TestMethod *method; }; -class InitializingTestWithEyeTracking : public UseCase { - TestMethod *method; - const Test &test; - - public: - explicit InitializingTestWithEyeTracking( - TestMethod *method, const Test &test) - : method{method}, test{test} {} - - void run(RecognitionTestModelImpl &model) override { - model.initializeWithEyeTracking(method, test); - } -}; - -class InitializingTestWithAudioRecording : public UseCase { - TestMethod *method; - const Test &test; - - public: - InitializingTestWithAudioRecording(TestMethod *method, const Test &test) - : method{method}, test{test} {} - - void run(RecognitionTestModelImpl &model) override { - model.initializeWithAudioRecording(method, test); - } -}; - class AudioDeviceUseCase : public virtual UseCase { public: virtual void setAudioDevice(std::string) = 0; @@ -192,7 +141,7 @@ class PlayingCalibration : public AudioDeviceUseCase, calibration.audioDevice = std::move(s); } - void run(RecognitionTestModelImpl &model) override { + void run(RunningATestImpl &model) override { model.playCalibration(calibration); } @@ -218,7 +167,7 @@ class PlayingLeftSpeakerCalibration : public AudioDeviceUseCase, calibration.audioDevice = std::move(s); } - void run(RecognitionTestModelImpl &model) override { + void run(RunningATestImpl &model) override { model.playLeftSpeakerCalibration(calibration); } @@ -244,7 +193,7 @@ class PlayingRightSpeakerCalibration : public AudioDeviceUseCase, calibration.audioDevice = std::move(s); } - void run(RecognitionTestModelImpl &model) override { + void run(RunningATestImpl &model) override { model.playRightSpeakerCalibration(calibration); } @@ -265,7 +214,7 @@ class PlayingTrial : public AudioDeviceUseCase { trial.audioDevice = std::move(s); } - void run(RecognitionTestModelImpl &m) override { m.playTrial(trial); } + void run(RunningATestImpl &m) override { m.playTrial(trial); } private: AudioSettings trial; @@ -273,77 +222,16 @@ class PlayingTrial : public AudioDeviceUseCase { class PreparingNextTrialIfNeeded : public UseCase { public: - void run(RecognitionTestModelImpl &m) override { - m.prepareNextTrialIfNeeded(); - } + void run(RunningATestImpl &m) override { m.prepareNextTrialIfNeeded(); } }; class SubmittingCoordinateResponse : public UseCase { public: - void run(RecognitionTestModelImpl &m) override { + void run(RunningATestImpl &m) override { m.submit(coordinate_response_measure::Response{}); } }; -class EyeTrackerStub : public EyeTracker { - public: - auto recordingTimeAllocatedSeconds() const -> double { - return recordingTimeAllocatedSeconds_; - } - - auto started() const -> bool { return started_; } - - auto stopped() const -> bool { return stopped_; } - - auto log() const -> const std::stringstream & { return log_; } - - void allocateRecordingTimeSeconds(double x) override { - insert(log_, "allocateRecordingTimeSeconds "); - recordingTimeAllocatedSeconds_ = x; - recordingTimeAllocated_ = true; - } - - void start() override { - insert(log_, "start "); - started_ = true; - } - - void stop() override { - insert(log_, "stop "); - stopped_ = true; - } - - auto recordingTimeAllocated() const -> bool { - return recordingTimeAllocated_; - } - - auto gazeSamples() -> BinocularGazeSamples override { - insert(log_, "gazeSamples "); - return gazeSamples_; - } - - void setGazes(BinocularGazeSamples g) { gazeSamples_ = std::move(g); } - - auto currentSystemTime() -> EyeTrackerSystemTime override { - return currentSystemTime_; - } - - void setCurrentSystemTime(EyeTrackerSystemTime t) { - currentSystemTime_ = t; - } - - void write(std::ostream &) override {} - - private: - BinocularGazeSamples gazeSamples_; - std::stringstream log_{}; - EyeTrackerSystemTime currentSystemTime_{}; - double recordingTimeAllocatedSeconds_{}; - bool recordingTimeAllocated_{}; - bool started_{}; - bool stopped_{}; -}; - class ClockStub : public Clock { public: [[nodiscard]] auto timeQueried() const -> bool { return timeQueried_; } @@ -358,30 +246,34 @@ class ClockStub : public Clock { bool timeQueried_{}; }; -class AudioRecorderStub : public AudioRecorder { +class RunningATestObserverStub : public RunningATest::Observer { public: - [[nodiscard]] auto started() const -> bool { return started_; } - - void start() override { started_ = true; } - - auto fileUrl() -> LocalUrl { return fileUrl_; } + void notifyThatNewTestIsReady(std::string_view session) override { + this->session = session; + } - void initialize(const LocalUrl &url) override { - fileUrl_ = url; - initialized_ = true; + void notifyThatTrialWillBegin(int trialNumber) override { + this->trialNumber = trialNumber; } - [[nodiscard]] auto initialized() const -> bool { return initialized_; } + void notifyThatTargetWillPlayAt( + const PlayerTimeWithDelay &playerTimeWithDelay) override { + this->playerTimeWithDelay = playerTimeWithDelay; + } - [[nodiscard]] auto stopped() const -> bool { return stopped_; } + void notifyThatStimulusHasEnded() override { + notifiedThatStimulusHasEnded = true; + } - void stop() override { stopped_ = true; } + void notifyThatSubjectHasResponded() override { + notifiedThatSubjectHasResponded = true; + } - private: - LocalUrl fileUrl_; - bool started_{}; - bool initialized_{}; - bool stopped_{}; + PlayerTimeWithDelay playerTimeWithDelay; + std::string session; + int trialNumber{}; + bool notifiedThatStimulusHasEnded{}; + bool notifiedThatSubjectHasResponded{}; }; void setMaskerLevel_dB_SPL(Test &test, int x) { test.maskerLevel.dB_SPL = x; } @@ -390,7 +282,7 @@ void setCurrentTarget(TestMethodStub &m, std::string s) { m.setCurrentTarget(std::move(s)); } -auto targetFileName(RecognitionTestModelImpl &m) -> std::string { +auto targetFileName(RunningATestImpl &m) -> std::string { return m.targetFileName(); } @@ -428,9 +320,7 @@ auto log(OutputFileStub &file) -> const std::stringstream & { return file.log(); } -void run(UseCase &useCase, RecognitionTestModelImpl &model) { - useCase.run(model); -} +void run(UseCase &useCase, RunningATestImpl &model) { useCase.run(model); } auto fadedIn(MaskerPlayerStub &maskerPlayer) -> bool { return maskerPlayer.fadeInCalled(); @@ -482,7 +372,7 @@ void assertLevelEquals_dB(TargetPlayerStub &player, double x) { AV_SPEECH_IN_NOISE_EXPECT_EQUAL(x, player.level_dB()); } -auto testComplete(RecognitionTestModelImpl &model) -> bool { +auto testComplete(RunningATestImpl &model) -> bool { return model.testComplete(); } @@ -506,24 +396,20 @@ void assertChannelDelaysCleared(MaskerPlayerStub &player) { AV_SPEECH_IN_NOISE_EXPECT_TRUE(player.channelDelaysCleared()); } -auto targetPlayerObserver(const RecognitionTestModelImpl &model) +auto targetPlayerObserver(const RunningATestImpl &model) -> const TargetPlayer::Observer * { return &model; } -auto maskerPlayerObserver(const RecognitionTestModelImpl &model) +auto maskerPlayerObserver(const RunningATestImpl &model) -> const MaskerPlayer::Observer * { return &model; } -void setEyeGazes(EyeTrackerStub &eyeTracker, BinocularGazeSamples g) { - eyeTracker.setGazes(std::move(g)); -} - -void runIgnoringFailure(UseCase &useCase, RecognitionTestModelImpl &model) { +void runIgnoringFailure(UseCase &useCase, RunningATestImpl &model) { try { run(useCase, model); - } catch (const Model::RequestFailure &) { + } catch (const RunningATestFacade::RequestFailure &) { } } @@ -535,27 +421,10 @@ void setFadeTimeSeconds(MaskerPlayerStub &player, double x) { player.setFadeTimeSeconds(x); } -auto started(EyeTrackerStub &eyeTracker) -> bool { - return eyeTracker.started(); -} - -auto stopped(EyeTrackerStub &eyeTracker) -> bool { - return eyeTracker.stopped(); -} - void setSystemTime(AudioSampleTimeWithOffset &time, player_system_time_type s) { time.playerTime.system = s; } -void setNanosecondsFromPlayerTime(MaskerPlayerStub &player, std::uintmax_t t) { - player.setNanosecondsFromPlayerTime(t); -} - -void setCurrentSystemTimeMicroseconds( - EyeTrackerStub &eyeTracker, std::int_least64_t t) { - eyeTracker.setCurrentSystemTime({t}); -} - void setSampleOffset(AudioSampleTimeWithOffset &time, gsl::index n) { time.sampleOffset = n; } @@ -574,14 +443,12 @@ class RecognitionTestModelTests : public ::testing::Test { ModelObserverStub listener; TargetPlayerStub targetPlayer; MaskerPlayerStub maskerPlayer; - AudioRecorderStub audioRecorder; ResponseEvaluatorStub evaluator; OutputFileStub outputFile; RandomizerStub randomizer; - EyeTrackerStub eyeTracker; ClockStub clock; - RecognitionTestModelImpl model{targetPlayer, maskerPlayer, audioRecorder, - evaluator, outputFile, randomizer, eyeTracker, clock}; + RunningATestImpl model{ + targetPlayer, maskerPlayer, evaluator, outputFile, randomizer, clock}; TestMethodStub testMethod; Calibration calibration{}; PlayingCalibration playingCalibration{calibration, targetPlayer}; @@ -590,15 +457,12 @@ class RecognitionTestModelTests : public ::testing::Test { PlayingRightSpeakerCalibration playingRightSpeakerCalibration{ calibration, maskerPlayer}; av_speech_in_noise::Test test{}; - InitializingTest initializingTest{&testMethod, test}; + RunningATestObserverStub observer; + InitializingTest initializingTest{&testMethod, test, &observer}; InitializingTestWithSingleSpeaker initializingTestWithSingleSpeaker{ &testMethod}; InitializingTestWithDelayedMasker initializingTestWithDelayedMasker{ &testMethod}; - InitializingTestWithEyeTracking initializingTestWithEyeTracking{ - &testMethod, test}; - InitializingTestWithAudioRecording initializingTestWithAudioRecording{ - &testMethod, test}; PlayingTrial playingTrial; SubmittingCoordinateResponse submittingCoordinateResponse; FreeResponse freeResponse{}; @@ -646,8 +510,8 @@ class RecognitionTestModelTests : public ::testing::Test { run(useCase, model); AV_SPEECH_IN_NOISE_EXPECT_EQUAL(0., randomizer.lowerFloatBound()); ::assertEqual(10. - 2 - 1 - 2 - - RecognitionTestModelImpl::targetOnsetFringeDuration.seconds - - RecognitionTestModelImpl::targetOffsetFringeDuration.seconds, + RunningATestImpl::targetOnsetFringeDuration.seconds - + RunningATestImpl::targetOffsetFringeDuration.seconds, randomizer.upperFloatBound(), 1e-15); } @@ -667,7 +531,7 @@ class RecognitionTestModelTests : public ::testing::Test { try { run(useCase, model); FAIL() << "Expected Model::RequestFailure"; - } catch (const Model::RequestFailure &e) { + } catch (const RunningATestFacade::RequestFailure &e) { AV_SPEECH_IN_NOISE_EXPECT_EQUAL(what, e.what()); } } @@ -761,31 +625,6 @@ class RecognitionTestModelTests : public ::testing::Test { AV_SPEECH_IN_NOISE_EXPECT_EQUAL( std::string{"a"}, maskerPlayer.filePath()); } - - void assertAllocatesTrialDurationForEyeTracking( - UseCase &initializing, UseCase &useCase) { - run(initializing, model); - setDurationSeconds(targetPlayer, 3); - setFadeTimeSeconds(maskerPlayer, 4); - run(useCase, model); - AV_SPEECH_IN_NOISE_EXPECT_EQUAL(3 + 2 * 4. + - RecognitionTestModelImpl::targetOnsetFringeDuration.seconds + - RecognitionTestModelImpl::targetOffsetFringeDuration.seconds, - eyeTracker.recordingTimeAllocatedSeconds()); - } - - void assertPlayTrialDoesNotAllocateRecordingTime(UseCase &useCase) { - run(useCase, model); - run(playingTrial, model); - AV_SPEECH_IN_NOISE_EXPECT_FALSE(eyeTracker.recordingTimeAllocated()); - } - - void - assertPlayTrialDoesNotAllocateRecordingTimeForEyeTrackingAfterTestWithEyeTracking( - UseCase &useCase) { - run(initializingTestWithEyeTracking, model); - assertPlayTrialDoesNotAllocateRecordingTime(useCase); - } }; #define RECOGNITION_TEST_MODEL_TEST(a) TEST_F(RecognitionTestModelTests, a) @@ -819,18 +658,6 @@ RECOGNITION_TEST_MODEL_TEST( initializingTestWithDelayedMasker); } -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithEyeTrackingClosesOutputFile_Opens_AndWritesTestInOrder) { - assertClosesOutputFileOpensAndWritesTestInOrder( - initializingTestWithEyeTracking); -} - -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithAudioRecordingClosesOutputFile_Opens_AndWritesTestInOrder) { - assertClosesOutputFileOpensAndWritesTestInOrder( - initializingTestWithAudioRecording); -} - RECOGNITION_TEST_MODEL_TEST(initializeTestUsesAllTargetPlayerChannels) { run(initializingTest, model); AV_SPEECH_IN_NOISE_EXPECT_TRUE(targetPlayer.usingAllChannels()); @@ -846,36 +673,6 @@ RECOGNITION_TEST_MODEL_TEST(initializeTestUsesAllMaskerPlayerChannels) { assertUsingAllChannels(maskerPlayer); } -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithEyeTrackingUsesAllMaskerPlayerChannels) { - assertUsesAllMaskerPlayerChannels(initializingTestWithEyeTracking); -} - -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithEyeTrackingUsesAllTargetPlayerChannels) { - assertUsesAllTargetPlayerChannels(initializingTestWithEyeTracking); -} - -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithEyeTrackingClearsAllMaskerPlayerChannelDelays) { - assertMaskerPlayerChannelDelaysCleared(initializingTestWithEyeTracking); -} - -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithAudioRecordingUsesAllMaskerPlayerChannels) { - assertUsesAllMaskerPlayerChannels(initializingTestWithAudioRecording); -} - -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithAudioRecordingUsesAllTargetPlayerChannels) { - assertUsesAllTargetPlayerChannels(initializingTestWithAudioRecording); -} - -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithAudioRecordingClearsAllMaskerPlayerChannelDelays) { - assertMaskerPlayerChannelDelaysCleared(initializingTestWithAudioRecording); -} - RECOGNITION_TEST_MODEL_TEST(initializeTestClearsAllMaskerPlayerChannelDelays) { run(initializingTest, model); assertChannelDelaysCleared(maskerPlayer); @@ -929,7 +726,7 @@ RECOGNITION_TEST_MODEL_TEST( AV_SPEECH_IN_NOISE_EXPECT_EQUAL( gsl::index{0}, maskerPlayer.channelDelayed()); AV_SPEECH_IN_NOISE_EXPECT_EQUAL( - RecognitionTestModelImpl::maskerChannelDelay.seconds, + RunningATestImpl::maskerChannelDelay.seconds, maskerPlayer.channelDelaySeconds()); } @@ -938,115 +735,22 @@ RECOGNITION_TEST_MODEL_TEST( assertPassesTestIdentityToOutputFile(initializingTest); } -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithEyeTrackingOpensNewOutputFilePassingTestInformation) { - assertPassesTestIdentityToOutputFile(initializingTestWithEyeTracking); -} - -RECOGNITION_TEST_MODEL_TEST( - playTrialForTestWithEyeTrackingAllocatesTrialDurationsWorthRecordingTimeForEyeTracking) { - assertAllocatesTrialDurationForEyeTracking( - initializingTestWithEyeTracking, playingTrial); -} - -RECOGNITION_TEST_MODEL_TEST(playTrialForTestWithEyeTrackingStartsEyeTracking) { - run(initializingTestWithEyeTracking, model); - run(playingTrial, model); - AV_SPEECH_IN_NOISE_EXPECT_TRUE(started(eyeTracker)); -} - -RECOGNITION_TEST_MODEL_TEST(playTrialForDefaultTestDoesNotStartEyeTracking) { - run(initializingTest, model); - run(playingTrial, model); - AV_SPEECH_IN_NOISE_EXPECT_FALSE(started(eyeTracker)); -} - -RECOGNITION_TEST_MODEL_TEST( - playTrialForTestWithAudioRecordingInitializesRecorder) { +RECOGNITION_TEST_MODEL_TEST(initializeTestNotifiesObserverOfNewTest) { test.identity.session = "smile"; - outputFile.setParentPath("/Users/user/data"); - run(initializingTestWithAudioRecording, model); - run(playingTrial, model); - AV_SPEECH_IN_NOISE_EXPECT_EQUAL( - "/Users/user/data/1-smile.wav", audioRecorder.fileUrl().path); -} - -RECOGNITION_TEST_MODEL_TEST(playTrialForDefaultTestDoesNotInitializeRecorder) { run(initializingTest, model); - run(playingTrial, model); - AV_SPEECH_IN_NOISE_EXPECT_FALSE(audioRecorder.initialized()); -} - -RECOGNITION_TEST_MODEL_TEST( - playTrialForTestWithEyeTrackingStartsEyeTrackingAfterAllocatingRecordingTime) { - run(initializingTestWithEyeTracking, model); - run(playingTrial, model); - AV_SPEECH_IN_NOISE_EXPECT_EQUAL( - std::string{"allocateRecordingTimeSeconds start "}, - string(eyeTracker.log())); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL("smile", observer.session); } -RECOGNITION_TEST_MODEL_TEST( - fadeOutCompleteForTestWithEyeTrackingStopsEyeTracking) { - run(initializingTestWithEyeTracking, model); - fadeOutComplete(maskerPlayer); - AV_SPEECH_IN_NOISE_EXPECT_TRUE(stopped(eyeTracker)); -} - -RECOGNITION_TEST_MODEL_TEST( - fadeOutCompleteForDefaultTestDoesNotStopEyeTracking) { +RECOGNITION_TEST_MODEL_TEST(playTrialNotifiesObserverOfTrialAboutToBegin) { run(initializingTest, model); - fadeOutComplete(maskerPlayer); - AV_SPEECH_IN_NOISE_EXPECT_FALSE(stopped(eyeTracker)); -} - -RECOGNITION_TEST_MODEL_TEST( - fadeOutCompleteForTestWithAudioRecordingStartsRecording) { - run(initializingTestWithAudioRecording, model); - fadeOutComplete(maskerPlayer); - AV_SPEECH_IN_NOISE_EXPECT_TRUE(audioRecorder.started()); + run(playingTrial, model); + AV_SPEECH_IN_NOISE_EXPECT_EQUAL(1, observer.trialNumber); } -RECOGNITION_TEST_MODEL_TEST( - fadeOutCompleteForDefaultTestDoesNotStartRecording) { +RECOGNITION_TEST_MODEL_TEST(fadeOutCompleteNotifiesThatStimulusHasEnded) { run(initializingTest, model); fadeOutComplete(maskerPlayer); - AV_SPEECH_IN_NOISE_EXPECT_FALSE(audioRecorder.started()); -} - -RECOGNITION_TEST_MODEL_TEST( - playTrialForDefaultTestDoesNotAllocateRecordingTimeForEyeTracking) { - assertPlayTrialDoesNotAllocateRecordingTime(initializingTest); -} - -RECOGNITION_TEST_MODEL_TEST( - playTrialForTestWithDelayedMaskerDoesNotAllocateRecordingTimeForEyeTracking) { - assertPlayTrialDoesNotAllocateRecordingTime( - initializingTestWithDelayedMasker); -} - -RECOGNITION_TEST_MODEL_TEST( - playTrialForTestWithSingleSpeakerDoesNotAllocateRecordingTimeForEyeTracking) { - assertPlayTrialDoesNotAllocateRecordingTime( - initializingTestWithSingleSpeaker); -} - -RECOGNITION_TEST_MODEL_TEST( - playTrialForDefaultTestFollowingTestWithEyeTrackingDoesNotAllocateRecordingTimeForEyeTracking) { - assertPlayTrialDoesNotAllocateRecordingTimeForEyeTrackingAfterTestWithEyeTracking( - initializingTest); -} - -RECOGNITION_TEST_MODEL_TEST( - playTrialForTestWithSingleSpeakerFollowingTestWithEyeTrackingDoesNotAllocateRecordingTimeForEyeTracking) { - assertPlayTrialDoesNotAllocateRecordingTimeForEyeTrackingAfterTestWithEyeTracking( - initializingTestWithSingleSpeaker); -} - -RECOGNITION_TEST_MODEL_TEST( - playTrialForTestWithDelayedMaskerFollowingTestWithEyeTrackingDoesNotAllocateRecordingTimeForEyeTracking) { - assertPlayTrialDoesNotAllocateRecordingTimeForEyeTrackingAfterTestWithEyeTracking( - initializingTestWithDelayedMasker); + AV_SPEECH_IN_NOISE_EXPECT_TRUE(observer.notifiedThatStimulusHasEnded); } RECOGNITION_TEST_MODEL_TEST(playTrialCapturesTimeStampForEventualReporting) { @@ -1109,8 +813,8 @@ RECOGNITION_TEST_MODEL_TEST(playRightSpeakerCalibrationPlaysMasker) { assertPlayed(maskerPlayer); } -RECOGNITION_TEST_MODEL_TEST(fadeInCompletePlaysTargetAtWhenEyeTracking) { - run(initializingTestWithEyeTracking, model); +RECOGNITION_TEST_MODEL_TEST(fadeInCompletePlaysTargetAt) { + run(initializingTest, model); setSystemTime(fadeInCompleteTime, 1); setSampleOffset(fadeInCompleteTime, 2); setSampleRateHz(maskerPlayer, 3); @@ -1118,66 +822,22 @@ RECOGNITION_TEST_MODEL_TEST(fadeInCompletePlaysTargetAtWhenEyeTracking) { AV_SPEECH_IN_NOISE_EXPECT_EQUAL(player_system_time_type{1}, targetPlayer.timePlayedAt().playerTime.system); AV_SPEECH_IN_NOISE_EXPECT_EQUAL( - 2 / 3. + RecognitionTestModelImpl::targetOnsetFringeDuration.seconds, + 2 / 3. + RunningATestImpl::targetOnsetFringeDuration.seconds, targetPlayer.timePlayedAt().delay.seconds); } -RECOGNITION_TEST_MODEL_TEST(fadeInCompletePlaysTargetAtWhenNotEyeTracking) { +RECOGNITION_TEST_MODEL_TEST( + fadeInCompleteNotifiesObserverThatTargetWillPlayAt) { run(initializingTest, model); setSystemTime(fadeInCompleteTime, 1); setSampleOffset(fadeInCompleteTime, 2); setSampleRateHz(maskerPlayer, 3); fadeInComplete(maskerPlayer, fadeInCompleteTime); AV_SPEECH_IN_NOISE_EXPECT_EQUAL(player_system_time_type{1}, - targetPlayer.timePlayedAt().playerTime.system); + observer.playerTimeWithDelay.playerTime.system); AV_SPEECH_IN_NOISE_EXPECT_EQUAL( - 2 / 3. + RecognitionTestModelImpl::targetOnsetFringeDuration.seconds, - targetPlayer.timePlayedAt().delay.seconds); -} - -RECOGNITION_TEST_MODEL_TEST( - fadeInCompletePassesTargetStartSystemTimeForConversionWhenEyeTracking) { - run(initializingTestWithEyeTracking, model); - setSystemTime(fadeInCompleteTime, 1); - fadeInComplete(maskerPlayer, fadeInCompleteTime); - AV_SPEECH_IN_NOISE_EXPECT_EQUAL(player_system_time_type{1}, - maskerPlayer.toNanosecondsSystemTime().at(0)); -} - -RECOGNITION_TEST_MODEL_TEST(fadeOutCompleteStopsEyeTracker) { - run(initializingTestWithEyeTracking, model); - fadeOutComplete(maskerPlayer); - AV_SPEECH_IN_NOISE_EXPECT_EQUAL( - std::string{"stop "}, string(eyeTracker.log())); -} - -RECOGNITION_TEST_MODEL_TEST(submittingCoordinateResponseWritesEyeGazes) { - run(initializingTestWithEyeTracking, model); - setEyeGazes(eyeTracker, - {{{1}, {{}, {{}, {2, 3}}}, {{}, {{}, {4, 5}}}}, - {{6}, {{}, {{}, {7, 8}}}, {{}, {{}, {9, 10}}}}}); - run(submittingCoordinateResponse, model); - ::assertEqual({{{1}, {{}, {{}, {2, 3}}}, {{}, {{}, {4, 5}}}}, - {{6}, {{}, {{}, {7, 8}}}, {{}, {{}, {9, 10}}}}}, - outputFile.eyeGazes()); -} - -RECOGNITION_TEST_MODEL_TEST( - submitCoordinateResponseWritesTargetStartTimeWhenEyeTracking) { - run(initializingTestWithEyeTracking, model); - setNanosecondsFromPlayerTime(maskerPlayer, 1); - setSampleOffset(fadeInCompleteTime, 2); - setSampleRateHz(maskerPlayer, 3); - fadeInComplete(maskerPlayer, fadeInCompleteTime); - fadeOutComplete(maskerPlayer); - run(submittingCoordinateResponse, model); - AV_SPEECH_IN_NOISE_EXPECT_EQUAL(1 + - gsl::narrow_cast( - (2 / 3. + - RecognitionTestModelImpl::targetOnsetFringeDuration - .seconds) * - 1e9), - outputFile.targetStartTime().nanoseconds); + 2 / 3. + RunningATestImpl::targetOnsetFringeDuration.seconds, + observer.playerTimeWithDelay.delay.seconds); } auto eyeTrackerTargetPlayerSynchronization(OutputFileStub &file) @@ -1185,49 +845,15 @@ auto eyeTrackerTargetPlayerSynchronization(OutputFileStub &file) return file.eyeTrackerTargetPlayerSynchronization(); } -RECOGNITION_TEST_MODEL_TEST(submitCoordinateResponseWritesSyncTimes) { - run(initializingTestWithEyeTracking, model); - setNanosecondsFromPlayerTime(maskerPlayer, 1); - setCurrentSystemTimeMicroseconds(eyeTracker, 2); - fadeInComplete(maskerPlayer, fadeInCompleteTime); - fadeOutComplete(maskerPlayer); - run(submittingCoordinateResponse, model); - AV_SPEECH_IN_NOISE_EXPECT_EQUAL(std::uintmax_t{1}, - eyeTrackerTargetPlayerSynchronization(outputFile) - .targetPlayerSystemTime.nanoseconds); - AV_SPEECH_IN_NOISE_EXPECT_EQUAL(std::int_least64_t{2}, - eyeTrackerTargetPlayerSynchronization(outputFile) - .eyeTrackerSystemTime.microseconds); -} - -RECOGNITION_TEST_MODEL_TEST(passesCurrentMaskerTimeForNanosecondConversion) { - run(initializingTestWithEyeTracking, model); - av_speech_in_noise::PlayerTime t{}; - t.system = 1; - maskerPlayer.setCurrentSystemTime(t); - fadeInComplete(maskerPlayer, fadeInCompleteTime); - AV_SPEECH_IN_NOISE_EXPECT_EQUAL(player_system_time_type{1}, - maskerPlayer.toNanosecondsSystemTime().at(1)); -} - RECOGNITION_TEST_MODEL_TEST( initializeDefaultTestPassesNextTargetToTargetPlayer) { assertPassesNextTargetToPlayer(initializingTest); } -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithEyeTrackingPassesNextTargetToTargetPlayer) { - assertPassesNextTargetToPlayer(initializingTestWithEyeTracking); -} - RECOGNITION_TEST_MODEL_TEST(initializeDefaultTestResetsTrialNumber) { assertYieldsTrialNumber(initializingTest, 1); } -RECOGNITION_TEST_MODEL_TEST(initializeTestWithEyeTrackingResetsTrialNumber) { - assertYieldsTrialNumber(initializingTestWithEyeTracking, 1); -} - RECOGNITION_TEST_MODEL_TEST(returnsTargetFileName) { evaluator.setFileName("a"); AV_SPEECH_IN_NOISE_EXPECT_EQUAL(std::string{"a"}, targetFileName(model)); @@ -1293,11 +919,6 @@ RECOGNITION_TEST_MODEL_TEST( assertPassesMaskerFilePathToMaskerPlayer(initializingTest); } -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithEyeTrackingPassesMaskerFilePathToMaskerPlayer) { - assertPassesMaskerFilePathToMaskerPlayer(initializingTestWithEyeTracking); -} - RECOGNITION_TEST_MODEL_TEST(initializeTestPassesMaskerFilePathToMaskerPlayer) { setMaskerFilePath(test, "a"); run(initializingTest, model); @@ -1308,8 +929,8 @@ RECOGNITION_TEST_MODEL_TEST( initializeDefaultTestSetsMaskerSteadyLevelDuration) { setDurationSeconds(targetPlayer, 1); run(initializingTest, model); - ::assertEqual(RecognitionTestModelImpl::targetOnsetFringeDuration.seconds + - RecognitionTestModelImpl::targetOffsetFringeDuration.seconds + 1, + ::assertEqual(RunningATestImpl::targetOnsetFringeDuration.seconds + + RunningATestImpl::targetOffsetFringeDuration.seconds + 1, maskerPlayer.steadyLevelDuration().seconds, 1e-15); } @@ -1318,12 +939,6 @@ RECOGNITION_TEST_MODEL_TEST( assertSeeksToRandomMaskerPositionWithinTrialDuration(initializingTest); } -RECOGNITION_TEST_MODEL_TEST( - initializeTestWithEyeTrackingSeeksToRandomMaskerPositionWithinTrialDuration) { - assertSeeksToRandomMaskerPositionWithinTrialDuration( - initializingTestWithEyeTracking); -} - RECOGNITION_TEST_MODEL_TEST( submitCoordinateResponseSeeksToRandomMaskerPositionWithinTrialDuration) { assertSeeksToRandomMaskerPositionWithinTrialDuration( @@ -1376,7 +991,7 @@ RECOGNITION_TEST_MODEL_TEST(preparingNextTrialIfNeededSetsTargetPlayerLevel) { } void assertLevelSet(Calibration &calibration, PlayerLevelUseCase &useCase, - RecognitionTestModelImpl &model) { + RunningATestImpl &model) { calibration.level.dB_SPL = 1; calibration.fullScaleLevel.dB_SPL = 2; useCase.set(DigitalLevel{3}); @@ -1436,17 +1051,10 @@ RECOGNITION_TEST_MODEL_TEST( } RECOGNITION_TEST_MODEL_TEST( - preparingNextTrialStopsAudioRecordingForAudioRecordingEnabledTest) { - run(initializingTestWithAudioRecording, model); - model.prepareNextTrialIfNeeded(); - AV_SPEECH_IN_NOISE_EXPECT_TRUE(audioRecorder.stopped()); -} - -RECOGNITION_TEST_MODEL_TEST( - preparingNextTrialDoesNotStopAudioRecordingForDefaultTest) { + preparingNextTrialStopsNotifiesThatSubjectHasResponded) { run(initializingTest, model); model.prepareNextTrialIfNeeded(); - AV_SPEECH_IN_NOISE_EXPECT_FALSE(audioRecorder.stopped()); + AV_SPEECH_IN_NOISE_EXPECT_TRUE(observer.notifiedThatSubjectHasResponded); } RECOGNITION_TEST_MODEL_TEST( diff --git a/test/TargetPlayerStub.hpp b/test/TargetPlayerStub.hpp index cc4bed60..03383a54 100644 --- a/test/TargetPlayerStub.hpp +++ b/test/TargetPlayerStub.hpp @@ -2,7 +2,9 @@ #define TESTS_TARGETPLAYERSTUB_HPP_ #include "LogString.hpp" -#include + +#include + #include #include diff --git a/test/TestSettingsInterpreter.cpp b/test/TestSettingsInterpreter.cpp index da6f69fe..0312c26d 100644 --- a/test/TestSettingsInterpreter.cpp +++ b/test/TestSettingsInterpreter.cpp @@ -82,9 +82,10 @@ auto fixedLevelTestWithEachTargetNTimes(ModelStub &m) return m.fixedLevelTestWithEachTargetNTimes(); } -void initialize(TestSettingsInterpreterImpl &interpreter, Model &model, - SessionController &sessionController, const std::vector &v, - int startingSnr = {}, const TestIdentity &identity = {}) { +void initialize(TestSettingsInterpreterImpl &interpreter, + RunningATestFacade &model, SessionController &sessionController, + const std::vector &v, int startingSnr = {}, + const TestIdentity &identity = {}) { interpreter.initialize( model, sessionController, concatenate(v), identity, SNR{startingSnr}); } @@ -140,8 +141,8 @@ void assertPassesSimpleFixedLevelSettings( fixedLevelTest(model).fullScaleLevel.dB_SPL); } -void initialize(TestSettingsInterpreterImpl &interpreter, Model &model, - SessionController &sessionController, Method m, +void initialize(TestSettingsInterpreterImpl &interpreter, + RunningATestFacade &model, SessionController &sessionController, Method m, const TestIdentity &identity = {}, int startingSnr = {}) { initialize(interpreter, model, sessionController, {entryWithNewline(TestSetting::method, m)}, startingSnr, identity); diff --git a/test/TestSetup.cpp b/test/TestSetup.cpp index 4d3415fe..7059d275 100644 --- a/test/TestSetup.cpp +++ b/test/TestSetup.cpp @@ -109,8 +109,8 @@ class TestSettingsInterpreterStub : public TestSettingsInterpreter { return calibration_; } - void initialize(Model &m, SessionController &sc, const std::string &t, - const TestIdentity &id, SNR snr) override { + void initialize(RunningATestFacade &m, SessionController &sc, + const std::string &t, const TestIdentity &id, SNR snr) override { startingSnr_ = snr.dB; sessionController_ = ≻ text_ = t; @@ -329,7 +329,7 @@ class TestSetupPresenterTests : public ::testing::Test { TestSetupPresenterImpl presenter{view, sessionView}; }; -class RequestFailingModel : public Model { +class RequestFailingModel : public RunningATestFacade { std::string errorMessage{}; public: