diff --git a/.datalad/.gitattributes b/.datalad/.gitattributes new file mode 100644 index 00000000..b5408201 --- /dev/null +++ b/.datalad/.gitattributes @@ -0,0 +1,4 @@ + +config annex.largefiles=nothing +metadata/aggregate* annex.largefiles=nothing +metadata/objects/** annex.largefiles=(anything) \ No newline at end of file diff --git a/.datalad/config b/.datalad/config new file mode 100644 index 00000000..119c0751 --- /dev/null +++ b/.datalad/config @@ -0,0 +1,2 @@ +[datalad "dataset"] + id = 2b3f09cc-05d0-4364-a694-73e1af4079e6 diff --git a/.env.tpl b/.env.tpl new file mode 100644 index 00000000..324526cf --- /dev/null +++ b/.env.tpl @@ -0,0 +1,3 @@ +export PUPIL_PATH=../pupil +#export PUPIL_PATH=/home/basile/data/src/pupil +export GI_TYPELIB_PATH=/usr/local/lib/x86_64-linux-gnu/girepository-1.0/ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..448d70f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +* annex.backend=MD5E +**/.git* annex.largefiles=nothing +*.jpg annex.largefiles=anything +*.png annex.largefiles=anything +*.svg annex.largefiles=anything +*.txt annex.largefiles=nothing +*.tsv annex.largefiles=nothing +*.csv annex.largefiles=nothing +*.py annex.largefiles=nothing diff --git a/.gitignore b/.gitignore index 894a44cc..71513654 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,4 @@ venv.bak/ # mypy .mypy_cache/ +data/things/memory_designs/* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..04be9982 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "data/things/images"] + path = data/things/images + url = git@github.com:courtois-neuromod/things.stimuli.git + datalad-id = 5504289b-0ac5-4f7c-a6cd-86af1f5c2135 +[submodule "data/videogames/shinobi"] + path = data/videogames/shinobi + url = git@github.com:courtois-neuromod/shinobi.stimuli.git + datalad-id = 74cb8a6e-664b-11eb-a4f2-1a44c1d5432b +[submodule "data/videogames/mario"] + path = data/videogames/mario + url = git@github.com:courtois-neuromod/mario.stimuli.git + datalad-id = 23782599-795f-46e5-aefe-5ecd578e968d diff --git a/README.md b/README.md index 307571f8..17969a99 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,13 @@ mkdir output ## how to launch a session -`python3 main.py --subject test --ses videoshorttest --eyetracking --fmri` +`python3 main.py --subject test --session video003 --tasks videoshorttest --eyetracking --fmri -o /path/to/dataset/` - --subject: can be whatever, will be used to save data in a bids-like structure -- --session: must match the name of a session script in `src/ses-.py`, which contains the tasks to be ran on that session +- --session: a session identifier that will be used to save the data in the BIDS +- --tasks: must match the name of a session script in `src/ses-.py`, which contains the tasks to be ran on that session - --eyetracking: turn on eyetracking, start pupil software and recording of eye +- -o : specifies the path to the root of the dataset where to output the data (in sourcedata or BIDS ) - --fmri: will wait for TTL (can be emulated with character `5` on the keyboard) to start the tasks that are labeled as fmri dependent. When not using that flag, tasks will run back to back. It will also append a video loop at the beginning of the session in order for the participant to have sound and visual stimuli to test the setup (then skip to start the session). diff --git a/bug_game.txt b/bug_game.txt deleted file mode 100644 index 54673b17..00000000 --- a/bug_game.txt +++ /dev/null @@ -1,123 +0,0 @@ -labopb@criugm0279:~/git/task_stimuli$ python3 main.py --subject 01 --ses videogame_test --fmri -pygame 1.9.4 -Hello from the pygame community. https://www.pygame.org/contribute.html -Namespace(eyetracking=False, fmri=True, meg=False, session='videogame_test', subject='01') -[983.46666667 768. ] -435.61 -READY -Next task: : setup_video -16333.3571 WARNING Monitor specification not found. Creating a temporary one... -16333.6624 WARNING The size of the gamma ramp was reported as 0. This can mean that gamma settings have no effect. Proceeding with a default gamma ramp size. -16333.6629 WARNING The size of the gamma ramp was reported as 0. This can mean that gamma settings have no effect. Proceeding with a default gamma ramp size. -skip -READY -Next task: : Pause -skip -READY -Next task: : ShinobiIIIReturnOfTheNinjaMaster-test -Next task: : ShinobiIIIReturnOfTheNinjaMaster-test -Traceback (most recent call last): - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/event.py", line 367, in dispatch_event - if getattr(self, event_type)(*args): -TypeError: 'NoneType' object is not callable - -During handling of the above exception, another exception occurred: - -Traceback (most recent call last): - File "/usr/lib/python3.6/inspect.py", line 1124, in getfullargspec - sigcls=Signature) - File "/usr/lib/python3.6/inspect.py", line 2191, in _signature_from_callable - raise TypeError('{!r} is not a callable object'.format(obj)) -TypeError: None is not a callable object - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "main.py", line 24, in - cli.main_loop(tasks, parsed.subject, parsed.session, parsed.eyetracking, parsed.fmri, parsed.meg) - File "/home/labopb/git/task_stimuli/src/shared/cli.py", line 135, in main_loop - exp_win.flip() - File "/home/labopb/.local/lib/python3.6/site-packages/psychopy/visual/window.py", line 779, in flip - self.backend.swapBuffers(flipThisFrame) - File "/home/labopb/.local/lib/python3.6/site-packages/psychopy/visual/backends/pygletbackend.py", line 259, in swapBuffers - self.winHandle.dispatch_events() - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/xlib/__init__.py", line 881, in dispatch_events - self.dispatch_platform_event(e) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/xlib/__init__.py", line 917, in dispatch_platform_event - event_handler(e) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/xlib/__init__.py", line 1108, in _event_key - return self._event_key_view(ev) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/xlib/__init__.py", line 1101, in _event_key_view - self.dispatch_event('on_key_release', symbol, modifiers) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/__init__.py", line 1232, in dispatch_event - if EventDispatcher.dispatch_event(self, *args) != False: - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/event.py", line 371, in dispatch_event - event_type, args, getattr(self, event_type)) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/event.py", line 392, in _raise_dispatch_exception - inspect.getargspec(handler) - File "/usr/lib/python3.6/inspect.py", line 1078, in getargspec - getfullargspec(func) - File "/usr/lib/python3.6/inspect.py", line 1130, in getfullargspec - raise TypeError('unsupported callable') from ex -TypeError: unsupported callable -labopb@criugm0279:~/git/task_stimuli$ python3 main.py --subject 01 --ses videogame_test --fmri -pygame 1.9.4 -Hello from the pygame community. https://www.pygame.org/contribute.html -Namespace(eyetracking=False, fmri=True, meg=False, session='videogame_test', subject='01') -[983.46666667 768. ] -435.61 -READY -Next task: : setup_video -17292.8077 WARNING Monitor specification not found. Creating a temporary one... -17293.1032 WARNING The size of the gamma ramp was reported as 0. This can mean that gamma settings have no effect. Proceeding with a default gamma ramp size. -17293.1036 WARNING The size of the gamma ramp was reported as 0. This can mean that gamma settings have no effect. Proceeding with a default gamma ramp size. -skip -READY -Next task: : Pause -skip -READY -Next task: : ShinobiIIIReturnOfTheNinjaMaster-test -Traceback (most recent call last): - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/event.py", line 367, in dispatch_event - if getattr(self, event_type)(*args): -TypeError: 'NoneType' object is not callable - -During handling of the above exception, another exception occurred: - -Traceback (most recent call last): - File "/usr/lib/python3.6/inspect.py", line 1124, in getfullargspec - sigcls=Signature) - File "/usr/lib/python3.6/inspect.py", line 2191, in _signature_from_callable - raise TypeError('{!r} is not a callable object'.format(obj)) -TypeError: None is not a callable object - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "main.py", line 24, in - cli.main_loop(tasks, parsed.subject, parsed.session, parsed.eyetracking, parsed.fmri, parsed.meg) - File "/home/labopb/git/task_stimuli/src/shared/cli.py", line 135, in main_loop - exp_win.flip() - File "/home/labopb/.local/lib/python3.6/site-packages/psychopy/visual/window.py", line 779, in flip - self.backend.swapBuffers(flipThisFrame) - File "/home/labopb/.local/lib/python3.6/site-packages/psychopy/visual/backends/pygletbackend.py", line 259, in swapBuffers - self.winHandle.dispatch_events() - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/xlib/__init__.py", line 881, in dispatch_events - self.dispatch_platform_event(e) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/xlib/__init__.py", line 917, in dispatch_platform_event - event_handler(e) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/xlib/__init__.py", line 1108, in _event_key - return self._event_key_view(ev) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/xlib/__init__.py", line 1101, in _event_key_view - self.dispatch_event('on_key_release', symbol, modifiers) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/window/__init__.py", line 1232, in dispatch_event - if EventDispatcher.dispatch_event(self, *args) != False: - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/event.py", line 371, in dispatch_event - event_type, args, getattr(self, event_type)) - File "/home/labopb/.local/lib/python3.6/site-packages/pyglet/event.py", line 392, in _raise_dispatch_exception - inspect.getargspec(handler) - File "/usr/lib/python3.6/inspect.py", line 1078, in getargspec - getfullargspec(func) - File "/usr/lib/python3.6/inspect.py", line 1130, in getfullargspec - raise TypeError('unsupported callable') from ex -TypeError: unsupported callable diff --git a/data/language/harrypotter/task-harry_run-1_events.tsv b/data/language/harrypotter/task-harry_run-1_events.tsv new file mode 100644 index 00000000..cbba7e5c --- /dev/null +++ b/data/language/harrypotter/task-harry_run-1_events.tsv @@ -0,0 +1,1305 @@ +word format onset duration ++ 0 10 +Harry 10.0 0.5 +had 10.5 0.5 +never 11.0 0.5 +believed 11.5 0.5 +he 12.0 0.5 +would 12.5 0.5 +meet 13.0 0.5 +a 13.5 0.5 +boy 14.0 0.5 +he 14.5 0.5 +hated 15.0 0.5 +more 15.5 0.5 +than 16.0 0.5 +Dudley, 16.5 0.5 +but 17.0 0.5 +that 17.5 0.5 +was 18.0 0.5 +before 18.5 0.5 +he 19.0 0.5 +met 19.5 0.5 +Draco 20.0 0.5 +Malfoy. 20.5 0.5 +Still, 21.0 0.5 +first-year 21.5 0.5 +Gryffindors 22.0 0.5 +only 22.5 0.5 +had 23.0 0.5 +Potions 23.5 0.5 +with 24.0 0.5 +the 24.5 0.5 +Slytherins, 25.0 0.5 +so 25.5 0.5 +they 26.0 0.5 +didn't 26.5 0.5 +have 27.0 0.5 +to 27.5 0.5 +put 28.0 0.5 +up 28.5 0.5 +with 29.0 0.5 +Malfoy 29.5 0.5 +much. 30.0 0.5 +Or 30.5 0.5 +at 31.0 0.5 +least, 31.5 0.5 +they 32.0 0.5 +didn't 32.5 0.5 +until 33.0 0.5 +they 33.5 0.5 +spotted 34.0 0.5 +a 34.5 0.5 +notice 35.0 0.5 +pinned 35.5 0.5 +up 36.0 0.5 +in 36.5 0.5 +the 37.0 0.5 +Gryffindor 37.5 0.5 +common 38.0 0.5 +room 38.5 0.5 +that 39.0 0.5 +made 39.5 0.5 +them 40.0 0.5 +all 40.5 0.5 +groan. 41.0 0.5 +Flying 41.5 0.5 +lessons 42.0 0.5 +would 42.5 0.5 +be 43.0 0.5 +starting 43.5 0.5 +on 44.0 0.5 +Thursday 44.5 0.5 +— 45.0 0.5 +and 45.5 0.5 +Gryffindor 46.0 0.5 +and 46.5 0.5 +Slytherin 47.0 0.5 +would 47.5 0.5 +be 48.0 0.5 +learning 48.5 0.5 +together. 49.0 0.5 ++ 49.5 0.5 +"""Typical,""" 50.0 0.5 +said 50.5 0.5 +Harry 51.0 0.5 +darkly. 51.5 0.5 +"""Just" 52.0 0.5 +what 52.5 0.5 +I 53.0 0.5 +always 53.5 0.5 +wanted. 54.0 0.5 +To 54.5 0.5 +make 55.0 0.5 +a 55.5 0.5 +fool 56.0 0.5 +of 56.5 0.5 +myself 57.0 0.5 +on 57.5 0.5 +a 58.0 0.5 +broomstick 58.5 0.5 +in 59.0 0.5 +front 59.5 0.5 +of 60.0 0.5 +"Malfoy.""" 60.5 0.5 ++ 61.0 0.5 +He 61.5 0.5 +had 62.0 0.5 +been 62.5 0.5 +looking 63.0 0.5 +forward 63.5 0.5 +to 64.0 0.5 +learning 64.5 0.5 +to 65.0 0.5 +fly 65.5 0.5 +more 66.0 0.5 +than 66.5 0.5 +anything 67.0 0.5 +else. 67.5 0.5 +"""You" 68.0 0.5 +don't 68.5 0.5 +know 69.0 0.5 +that 69.5 0.5 +you'll 70.0 0.5 +make 70.5 0.5 +a 71.0 0.5 +fool 71.5 0.5 +of 72.0 0.5 +"yourself,""" 72.5 0.5 +said 73.0 0.5 +Ron 73.5 0.5 +reasonably. 74.0 0.5 +"""Anyway," 74.5 0.5 +I 75.0 0.5 +know 75.5 0.5 +Malfoy's 76.0 0.5 +always 76.5 0.5 +going 77.0 0.5 +on 77.5 0.5 +about 78.0 0.5 +how 78.5 0.5 +good 79.0 0.5 +he 79.5 0.5 +is 80.0 0.5 +at 80.5 0.5 +Quidditch, 81.0 0.5 +but 81.5 0.5 +I 82.0 0.5 +bet 82.5 0.5 +that's 83.0 0.5 +all 83.5 0.5 +"talk.""" 84.0 0.5 ++ 84.5 0.5 +Malfoy 85.0 0.5 +certainly 85.5 0.5 +did 86.0 0.5 +talk 86.5 0.5 +about 87.0 0.5 +flying 87.5 0.5 +a 88.0 0.5 +lot. 88.5 0.5 +He 89.0 0.5 +complained 89.5 0.5 +loudly 90.0 0.5 +about 90.5 0.5 +first 91.0 0.5 +years 91.5 0.5 +never 92.0 0.5 +getting 92.5 0.5 +on 93.0 0.5 +the 93.5 0.5 +House 94.0 0.5 +Quidditch 94.5 0.5 +teams 95.0 0.5 +and 95.5 0.5 +told 96.0 0.5 +long, 96.5 0.5 +boastful 97.0 0.5 +stories 97.5 0.5 +that 98.0 0.5 +always 98.5 0.5 +seemed 99.0 0.5 +to 99.5 0.5 +end 100.0 0.5 +with 100.5 0.5 +him 101.0 0.5 +narrowly 101.5 0.5 +escaping 102.0 0.5 +Muggles 102.5 0.5 +in 103.0 0.5 +helicopters. 103.5 0.5 +He 104.0 0.5 +wasn't 104.5 0.5 +the 105.0 0.5 +only 105.5 0.5 +one, 106.0 0.5 +though: 106.5 0.5 +the 107.0 0.5 +way 107.5 0.5 +Seamus 108.0 0.5 +Finnigan 108.5 0.5 +told 109.0 0.5 +it, 109.5 0.5 +he'd 110.0 0.5 +spent 110.5 0.5 +most 111.0 0.5 +of 111.5 0.5 +his 112.0 0.5 +childhood 112.5 0.5 +zooming 113.0 0.5 +around 113.5 0.5 +the 114.0 0.5 +countryside 114.5 0.5 +on 115.0 0.5 +his 115.5 0.5 +broomstick. 116.0 0.5 +Even 116.5 0.5 +Ron 117.0 0.5 +would 117.5 0.5 +tell 118.0 0.5 +anyone 118.5 0.5 +who'd 119.0 0.5 +listen 119.5 0.5 +about 120.0 0.5 +the 120.5 0.5 +time 121.0 0.5 +he'd 121.5 0.5 +almost 122.0 0.5 +hit 122.5 0.5 +a 123.0 0.5 +hang 123.5 0.5 +glider 124.0 0.5 +on 124.5 0.5 +Charlie's 125.0 0.5 +old 125.5 0.5 +broom. 126.0 0.5 +Everyone 126.5 0.5 +from 127.0 0.5 +wizarding 127.5 0.5 +families 128.0 0.5 +talked 128.5 0.5 +about 129.0 0.5 +Quidditch 129.5 0.5 +constantly. 130.0 0.5 +Ron 130.5 0.5 +had 131.0 0.5 +already 131.5 0.5 +had 132.0 0.5 +a 132.5 0.5 +big 133.0 0.5 +argument 133.5 0.5 +with 134.0 0.5 +Dean 134.5 0.5 +Thomas, 135.0 0.5 +who 135.5 0.5 +shared 136.0 0.5 +their 136.5 0.5 +dormitory, 137.0 0.5 +about 137.5 0.5 +soccer. 138.0 0.5 +Ron 138.5 0.5 +couldn't 139.0 0.5 +see 139.5 0.5 +what 140.0 0.5 +was 140.5 0.5 +exciting 141.0 0.5 +about 141.5 0.5 +a 142.0 0.5 +game 142.5 0.5 +with 143.0 0.5 +only 143.5 0.5 +one 144.0 0.5 +ball 144.5 0.5 +where 145.0 0.5 +no 145.5 0.5 +one 146.0 0.5 +was 146.5 0.5 +allowed 147.0 0.5 +to 147.5 0.5 +fly. 148.0 0.5 +Harry 148.5 0.5 +had 149.0 0.5 +caught 149.5 0.5 +Ron 150.0 0.5 +prodding 150.5 0.5 +Dean's 151.0 0.5 +poster 151.5 0.5 +of 152.0 0.5 +West 152.5 0.5 +Ham 153.0 0.5 +soccer 153.5 0.5 +team, 154.0 0.5 +trying 154.5 0.5 +to 155.0 0.5 +make 155.5 0.5 +the 156.0 0.5 +players 156.5 0.5 +move. 157.0 0.5 ++ 157.5 0.5 +Neville 158.0 0.5 +had 158.5 0.5 +never 159.0 0.5 +been 159.5 0.5 +on 160.0 0.5 +a 160.5 0.5 +broomstick 161.0 0.5 +in 161.5 0.5 +his 162.0 0.5 +life, 162.5 0.5 +because 163.0 0.5 +his 163.5 0.5 +grandmother 164.0 0.5 +had 164.5 0.5 +never 165.0 0.5 +let 165.5 0.5 +him 166.0 0.5 +near 166.5 0.5 +one. 167.0 0.5 +Privately, 167.5 0.5 +Harry 168.0 0.5 +felt 168.5 0.5 +she'd 169.0 0.5 +had 169.5 0.5 +good 170.0 0.5 +reason, 170.5 0.5 +because 171.0 0.5 +Neville 171.5 0.5 +managed 172.0 0.5 +to 172.5 0.5 +have 173.0 0.5 +an 173.5 0.5 +extraordinary 174.0 0.5 +number 174.5 0.5 +of 175.0 0.5 +accidents 175.5 0.5 +even 176.0 0.5 +with 176.5 0.5 +both 177.0 0.5 +feet 177.5 0.5 +on 178.0 0.5 +the 178.5 0.5 +ground. 179.0 0.5 ++ 179.5 0.5 +Hermione 180.0 0.5 +Granger 180.5 0.5 +was 181.0 0.5 +almost 181.5 0.5 +as 182.0 0.5 +nervous 182.5 0.5 +about 183.0 0.5 +flying 183.5 0.5 +as 184.0 0.5 +Neville 184.5 0.5 +was. 185.0 0.5 +This 185.5 0.5 +was 186.0 0.5 +something 186.5 0.5 +you 187.0 0.5 +couldn't 187.5 0.5 +learn 188.0 0.5 +by 188.5 0.5 +heart 189.0 0.5 +out 189.5 0.5 +of 190.0 0.5 +a 190.5 0.5 +book 191.0 0.5 +— 191.5 0.5 +not 192.0 0.5 +that 192.5 0.5 +she 193.0 0.5 +hadn't 193.5 0.5 +tried. 194.0 0.5 +At 194.5 0.5 +breakfast 195.0 0.5 +on 195.5 0.5 +Thursday 196.0 0.5 +she 196.5 0.5 +bored 197.0 0.5 +them 197.5 0.5 +all 198.0 0.5 +stupid 198.5 0.5 +with 199.0 0.5 +flying 199.5 0.5 +tips 200.0 0.5 +she'd 200.5 0.5 +gotten 201.0 0.5 +out 201.5 0.5 +of 202.0 0.5 +a 202.5 0.5 +library 203.0 0.5 +book 203.5 0.5 +called 204.0 0.5 +Quidditch italic 204.5 0.5 +Through italic 205.0 0.5 +the italic 205.5 0.5 +Ages. italic 206.0 0.5 +Neville 206.5 0.5 +was 207.0 0.5 +hanging 207.5 0.5 +on 208.0 0.5 +to 208.5 0.5 +her 209.0 0.5 +every 209.5 0.5 +word, 210.0 0.5 +desperate 210.5 0.5 +for 211.0 0.5 +anything 211.5 0.5 +that 212.0 0.5 +might 212.5 0.5 +help 213.0 0.5 +him 213.5 0.5 +hang 214.0 0.5 +on 214.5 0.5 +to 215.0 0.5 +his 215.5 0.5 +broomstick 216.0 0.5 +later, 216.5 0.5 +but 217.0 0.5 +everybody 217.5 0.5 +else 218.0 0.5 +was 218.5 0.5 +very 219.0 0.5 +pleased 219.5 0.5 +when 220.0 0.5 +Hermione's 220.5 0.5 +lecture 221.0 0.5 +was 221.5 0.5 +interrupted 222.0 0.5 +by 222.5 0.5 +the 223.0 0.5 +arrival 223.5 0.5 +of 224.0 0.5 +the 224.5 0.5 +mail. 225.0 0.5 ++ 225.5 0.5 +Harry 226.0 0.5 +hadn't 226.5 0.5 +had 227.0 0.5 +a 227.5 0.5 +single 228.0 0.5 +letter 228.5 0.5 +since 229.0 0.5 +Hagrid's 229.5 0.5 +note, 230.0 0.5 +something 230.5 0.5 +that 231.0 0.5 +Malfoy 231.5 0.5 +had 232.0 0.5 +been 232.5 0.5 +quick 233.0 0.5 +to 233.5 0.5 +notice, 234.0 0.5 +of 234.5 0.5 +course. 235.0 0.5 +Malfoy's 235.5 0.5 +eagle 236.0 0.5 +owl 236.5 0.5 +was 237.0 0.5 +always 237.5 0.5 +bringing 238.0 0.5 +him 238.5 0.5 +packages 239.0 0.5 +of 239.5 0.5 +sweets 240.0 0.5 +from 240.5 0.5 +home, 241.0 0.5 +which 241.5 0.5 +he 242.0 0.5 +opened 242.5 0.5 +gloatingly 243.0 0.5 +at 243.5 0.5 +the 244.0 0.5 +Slytherin 244.5 0.5 +table. 245.0 0.5 ++ 245.5 0.5 +A 246.0 0.5 +barn 246.5 0.5 +owl 247.0 0.5 +brought 247.5 0.5 +Neville 248.0 0.5 +a 248.5 0.5 +small 249.0 0.5 +package 249.5 0.5 +from 250.0 0.5 +his 250.5 0.5 +grandmother. 251.0 0.5 +He 251.5 0.5 +opened 252.0 0.5 +it 252.5 0.5 +excitedly 253.0 0.5 +and 253.5 0.5 +showed 254.0 0.5 +them 254.5 0.5 +a 255.0 0.5 +glass 255.5 0.5 +ball 256.0 0.5 +the 256.5 0.5 +size 257.0 0.5 +of 257.5 0.5 +a 258.0 0.5 +large 258.5 0.5 +marble, 259.0 0.5 +which 259.5 0.5 +seemed 260.0 0.5 +to 260.5 0.5 +be 261.0 0.5 +full 261.5 0.5 +of 262.0 0.5 +white 262.5 0.5 +smoke. 263.0 0.5 ++ 263.5 0.5 +"""It's" 264.0 0.5 +a 264.5 0.5 +"Remembrall!""" 265.0 0.5 +he 265.5 0.5 +explained. 266.0 0.5 +"""Gran" 266.5 0.5 +knows 267.0 0.5 +I 267.5 0.5 +forget 268.0 0.5 +things 268.5 0.5 +— 269.0 0.5 +this 269.5 0.5 +tells 270.0 0.5 +you 270.5 0.5 +if 271.0 0.5 +there's 271.5 0.5 +something 272.0 0.5 +you've 272.5 0.5 +forgotten 273.0 0.5 +to 273.5 0.5 +do. 274.0 0.5 +Look, 274.5 0.5 +you 275.0 0.5 +hold 275.5 0.5 +it 276.0 0.5 +tight 276.5 0.5 +like 277.0 0.5 +this 277.5 0.5 +and 278.0 0.5 +if 278.5 0.5 +it 279.0 0.5 +turns 279.5 0.5 +red 280.0 0.5 +— 280.5 0.5 +oh 281.0 0.5 +"...""" 281.5 0.5 +His 282.0 0.5 +face 282.5 0.5 +fell, 283.0 0.5 +because 283.5 0.5 +the 284.0 0.5 +Remembrall 284.5 0.5 +had 285.0 0.5 +suddenly 285.5 0.5 +glowed 286.0 0.5 +scarlet, 286.5 0.5 +"""..." 287.0 0.5 +you've 287.5 0.5 +forgotten 288.0 0.5 +something 288.5 0.5 +"...""" 289.0 0.5 ++ 289.5 0.5 +Neville 290.0 0.5 +was 290.5 0.5 +trying 291.0 0.5 +to 291.5 0.5 +remember 292.0 0.5 +what 292.5 0.5 +he'd 293.0 0.5 +forgotten 293.5 0.5 +when 294.0 0.5 +Draco 294.5 0.5 +Malfoy, 295.0 0.5 +who 295.5 0.5 +was 296.0 0.5 +passing 296.5 0.5 +the 297.0 0.5 +Gryffindor 297.5 0.5 +table, 298.0 0.5 +snatched 298.5 0.5 +the 299.0 0.5 +Remembrall 299.5 0.5 +out 300.0 0.5 +of 300.5 0.5 +his 301.0 0.5 +hand. 301.5 0.5 ++ 302.0 0.5 +Harry 302.5 0.5 +and 303.0 0.5 +Ron 303.5 0.5 +jumped 304.0 0.5 +to 304.5 0.5 +their 305.0 0.5 +feet. 305.5 0.5 +They 306.0 0.5 +were 306.5 0.5 +half 307.0 0.5 +hoping 307.5 0.5 +for 308.0 0.5 +a 308.5 0.5 +reason 309.0 0.5 +to 309.5 0.5 +fight 310.0 0.5 +Malfoy, 310.5 0.5 +but 311.0 0.5 +Professor 311.5 0.5 +McGonagall, 312.0 0.5 +who 312.5 0.5 +could 313.0 0.5 +spot 313.5 0.5 +trouble 314.0 0.5 +quicker 314.5 0.5 +than 315.0 0.5 +any 315.5 0.5 +teacher 316.0 0.5 +in 316.5 0.5 +the 317.0 0.5 +school, 317.5 0.5 +was 318.0 0.5 +there 318.5 0.5 +in 319.0 0.5 +a 319.5 0.5 +flash. 320.0 0.5 ++ 320.5 0.5 +"""What's" 321.0 0.5 +going 321.5 0.5 +"on?""" 322.0 0.5 ++ 322.5 0.5 +"""Malfoy's" 323.0 0.5 +got 323.5 0.5 +my 324.0 0.5 +Remembrall, 324.5 0.5 +"Professor.""" 325.0 0.5 ++ 325.5 0.5 +Scowling, 326.0 0.5 +Malfoy 326.5 0.5 +quickly 327.0 0.5 +dropped 327.5 0.5 +the 328.0 0.5 +Remembrall 328.5 0.5 +back 329.0 0.5 +on 329.5 0.5 +the 330.0 0.5 +table. 330.5 0.5 ++ 331.0 0.5 +"""Just" 331.5 0.5 +"looking,""" 332.0 0.5 +he 332.5 0.5 +said, 333.0 0.5 +and 333.5 0.5 +he 334.0 0.5 +sloped 334.5 0.5 +away 335.0 0.5 +with 335.5 0.5 +Crabbe 336.0 0.5 +and 336.5 0.5 +Goyle 337.0 0.5 +behind 337.5 0.5 +him. 338.0 0.5 ++ 338.5 0.5 ++ 339.0 0.5 +At 339.5 0.5 +three-thirty 340.0 0.5 +that 340.5 0.5 +afternoon, 341.0 0.5 +Harry, 341.5 0.5 +Ron, 342.0 0.5 +and 342.5 0.5 +the 343.0 0.5 +other 343.5 0.5 +Gryffindors 344.0 0.5 +hurried 344.5 0.5 +down 345.0 0.5 +the 345.5 0.5 +front 346.0 0.5 +steps 346.5 0.5 +onto 347.0 0.5 +the 347.5 0.5 +grounds 348.0 0.5 +for 348.5 0.5 +their 349.0 0.5 +first 349.5 0.5 +flying 350.0 0.5 +lesson. 350.5 0.5 +It 351.0 0.5 +was 351.5 0.5 +a 352.0 0.5 +clear, 352.5 0.5 +breezy 353.0 0.5 +day, 353.5 0.5 +and 354.0 0.5 +the 354.5 0.5 +grass 355.0 0.5 +rippled 355.5 0.5 +under 356.0 0.5 +their 356.5 0.5 +feet 357.0 0.5 +as 357.5 0.5 +they 358.0 0.5 +marched 358.5 0.5 +down 359.0 0.5 +the 359.5 0.5 +sloping 360.0 0.5 +lawns 360.5 0.5 +toward 361.0 0.5 +a 361.5 0.5 +smooth, 362.0 0.5 +flat 362.5 0.5 +lawn 363.0 0.5 +on 363.5 0.5 +the 364.0 0.5 +opposite 364.5 0.5 +side 365.0 0.5 +of 365.5 0.5 +the 366.0 0.5 +grounds 366.5 0.5 +to 367.0 0.5 +the 367.5 0.5 +forbidden 368.0 0.5 +forest, 368.5 0.5 +whose 369.0 0.5 +trees 369.5 0.5 +were 370.0 0.5 +swaying 370.5 0.5 +darkly 371.0 0.5 +in 371.5 0.5 +the 372.0 0.5 +distance. 372.5 0.5 ++ 373.0 0.5 +The 373.5 0.5 +Slytherins 374.0 0.5 +were 374.5 0.5 +already 375.0 0.5 +there, 375.5 0.5 +and 376.0 0.5 +so 376.5 0.5 +were 377.0 0.5 +twenty 377.5 0.5 +broomsticks 378.0 0.5 +lying 378.5 0.5 +in 379.0 0.5 +neat 379.5 0.5 +lines 380.0 0.5 +on 380.5 0.5 +the 381.0 0.5 +ground. 381.5 0.5 +Harry 382.0 0.5 +had 382.5 0.5 +heard 383.0 0.5 +Fred 383.5 0.5 +and 384.0 0.5 +George 384.5 0.5 +Weasley 385.0 0.5 +complain 385.5 0.5 +about 386.0 0.5 +the 386.5 0.5 +school 387.0 0.5 +brooms, 387.5 0.5 +saying 388.0 0.5 +that 388.5 0.5 +some 389.0 0.5 +of 389.5 0.5 +them 390.0 0.5 +started 390.5 0.5 +to 391.0 0.5 +vibrate 391.5 0.5 +if 392.0 0.5 +you 392.5 0.5 +flew 393.0 0.5 +too 393.5 0.5 +high, 394.0 0.5 +or 394.5 0.5 +always 395.0 0.5 +flew 395.5 0.5 +slightly 396.0 0.5 +to 396.5 0.5 +the 397.0 0.5 +left. 397.5 0.5 ++ 398.0 0.5 +Their 398.5 0.5 +teacher, 399.0 0.5 +Madam 399.5 0.5 +Hooch, 400.0 0.5 +arrived. 400.5 0.5 +She 401.0 0.5 +had 401.5 0.5 +short, 402.0 0.5 +gray 402.5 0.5 +hair, 403.0 0.5 +and 403.5 0.5 +yellow 404.0 0.5 +eyes 404.5 0.5 +like 405.0 0.5 +a 405.5 0.5 +hawk. 406.0 0.5 ++ 406.5 0.5 +"""Well," 407.0 0.5 +what 407.5 0.5 +are 408.0 0.5 +you 408.5 0.5 +all 409.0 0.5 +waiting 409.5 0.5 +"for?""" 410.0 0.5 +she 410.5 0.5 +barked. 411.0 0.5 +"""Everyone" 411.5 0.5 +stand 412.0 0.5 +by 412.5 0.5 +a 413.0 0.5 +broomstick. 413.5 0.5 +Come 414.0 0.5 +on, 414.5 0.5 +hurry 415.0 0.5 +"up.""" 415.5 0.5 ++ 416.0 0.5 +Harry 416.5 0.5 +glanced 417.0 0.5 +down 417.5 0.5 +at 418.0 0.5 +his 418.5 0.5 +broom. 419.0 0.5 +It 419.5 0.5 +was 420.0 0.5 +old 420.5 0.5 +and 421.0 0.5 +some 421.5 0.5 +of 422.0 0.5 +the 422.5 0.5 +twigs 423.0 0.5 +stuck 423.5 0.5 +out 424.0 0.5 +at 424.5 0.5 +odd 425.0 0.5 +angles. 425.5 0.5 ++ 426.0 0.5 +"""Stick" 426.5 0.5 +out 427.0 0.5 +your 427.5 0.5 +right 428.0 0.5 +hand 428.5 0.5 +over 429.0 0.5 +your 429.5 0.5 +"broom,""" 430.0 0.5 +called 430.5 0.5 +Madam 431.0 0.5 +Hooch 431.5 0.5 +at 432.0 0.5 +the 432.5 0.5 +front, 433.0 0.5 +"""and" 433.5 0.5 +say 434.0 0.5 +"'Up!'""" 434.5 0.5 ++ 435.0 0.5 +"""UP!""" 435.5 0.5 +everyone 436.0 0.5 +shouted. 436.5 0.5 ++ 437.0 0.5 +Harry's 437.5 0.5 +broom 438.0 0.5 +jumped 438.5 0.5 +into 439.0 0.5 +his 439.5 0.5 +hand 440.0 0.5 +at 440.5 0.5 +once, 441.0 0.5 +but 441.5 0.5 +it 442.0 0.5 +was 442.5 0.5 +one 443.0 0.5 +of 443.5 0.5 +the 444.0 0.5 +few 444.5 0.5 +that 445.0 0.5 +did. 445.5 0.5 +Hermione 446.0 0.5 +Granger's 446.5 0.5 +had 447.0 0.5 +simply 447.5 0.5 +rolled 448.0 0.5 +over 448.5 0.5 +on 449.0 0.5 +the 449.5 0.5 +ground, 450.0 0.5 +and 450.5 0.5 +Neville's 451.0 0.5 +hadn't 451.5 0.5 +moved 452.0 0.5 +at 452.5 0.5 +all. 453.0 0.5 +Perhaps 453.5 0.5 +brooms, 454.0 0.5 +like 454.5 0.5 +horses, 455.0 0.5 +could 455.5 0.5 +tell 456.0 0.5 +when 456.5 0.5 +you 457.0 0.5 +were 457.5 0.5 +afraid, 458.0 0.5 +thought 458.5 0.5 +Harry; 459.0 0.5 +there 459.5 0.5 +was 460.0 0.5 +a 460.5 0.5 +quaver 461.0 0.5 +in 461.5 0.5 +Neville's 462.0 0.5 +voice 462.5 0.5 +that 463.0 0.5 +said 463.5 0.5 +only 464.0 0.5 +too 464.5 0.5 +clearly 465.0 0.5 +that 465.5 0.5 +he 466.0 0.5 +wanted 466.5 0.5 +to 467.0 0.5 +keep 467.5 0.5 +his 468.0 0.5 +feet 468.5 0.5 +on 469.0 0.5 +the 469.5 0.5 +ground. 470.0 0.5 ++ 470.5 0.5 +Madam 471.0 0.5 +Hooch 471.5 0.5 +then 472.0 0.5 +showed 472.5 0.5 +them 473.0 0.5 +how 473.5 0.5 +to 474.0 0.5 +mount 474.5 0.5 +their 475.0 0.5 +brooms 475.5 0.5 +without 476.0 0.5 +sliding 476.5 0.5 +off 477.0 0.5 +the 477.5 0.5 +end, 478.0 0.5 +and 478.5 0.5 +walked 479.0 0.5 +up 479.5 0.5 +and 480.0 0.5 +down 480.5 0.5 +the 481.0 0.5 +rows 481.5 0.5 +correcting 482.0 0.5 +their 482.5 0.5 +grips. 483.0 0.5 +Harry 483.5 0.5 +and 484.0 0.5 +Ron 484.5 0.5 +were 485.0 0.5 +delighted 485.5 0.5 +when 486.0 0.5 +she 486.5 0.5 +told 487.0 0.5 +Malfoy 487.5 0.5 +he'd 488.0 0.5 +been 488.5 0.5 +doing 489.0 0.5 +it 489.5 0.5 +wrong 490.0 0.5 +for 490.5 0.5 +years. 491.0 0.5 ++ 491.5 0.5 +"""Now," 492.0 0.5 +when 492.5 0.5 +I 493.0 0.5 +blow 493.5 0.5 +my 494.0 0.5 +whistle, 494.5 0.5 +you 495.0 0.5 +kick 495.5 0.5 +off 496.0 0.5 +from 496.5 0.5 +the 497.0 0.5 +ground, 497.5 0.5 +"hard,""" 498.0 0.5 +said 498.5 0.5 +Madam 499.0 0.5 +Hooch. 499.5 0.5 +"""Keep" 500.0 0.5 +your 500.5 0.5 +brooms 501.0 0.5 +steady, 501.5 0.5 +rise 502.0 0.5 +a 502.5 0.5 +few 503.0 0.5 +feet, 503.5 0.5 +and 504.0 0.5 +then 504.5 0.5 +come 505.0 0.5 +straight 505.5 0.5 +back 506.0 0.5 +down 506.5 0.5 +by 507.0 0.5 +leaning 507.5 0.5 +forward 508.0 0.5 +slightly. 508.5 0.5 +On 509.0 0.5 +my 509.5 0.5 +whistle 510.0 0.5 +— 510.5 0.5 +three 511.0 0.5 +— 511.5 0.5 +two 512.0 0.5 +"—""" 512.5 0.5 ++ 513.0 0.5 +But 513.5 0.5 +Neville, 514.0 0.5 +nervous 514.5 0.5 +and 515.0 0.5 +jumpy 515.5 0.5 +and 516.0 0.5 +frightened 516.5 0.5 +of 517.0 0.5 +being 517.5 0.5 +left 518.0 0.5 +on 518.5 0.5 +the 519.0 0.5 +ground, 519.5 0.5 +pushed 520.0 0.5 +off 520.5 0.5 +hard 521.0 0.5 +before 521.5 0.5 +the 522.0 0.5 +whistle 522.5 0.5 +had 523.0 0.5 +touched 523.5 0.5 +Madam 524.0 0.5 +Hooch's 524.5 0.5 +lips. 525.0 0.5 ++ 525.5 0.5 +"""Come" 526.0 0.5 +back, 526.5 0.5 +"boy!""" 527.0 0.5 +she 527.5 0.5 +shouted, 528.0 0.5 +but 528.5 0.5 +Neville 529.0 0.5 +was 529.5 0.5 +rising 530.0 0.5 +straight 530.5 0.5 +up 531.0 0.5 +like 531.5 0.5 +a 532.0 0.5 +cork 532.5 0.5 +shot 533.0 0.5 +out 533.5 0.5 +of 534.0 0.5 +a 534.5 0.5 +bottle 535.0 0.5 +— 535.5 0.5 +twelve 536.0 0.5 +feet 536.5 0.5 +— 537.0 0.5 +twenty 537.5 0.5 +feet. 538.0 0.5 +Harry 538.5 0.5 +saw 539.0 0.5 +his 539.5 0.5 +scared 540.0 0.5 +white 540.5 0.5 +face 541.0 0.5 +look 541.5 0.5 +down 542.0 0.5 +at 542.5 0.5 +the 543.0 0.5 +ground 543.5 0.5 +falling 544.0 0.5 +away, 544.5 0.5 +saw 545.0 0.5 +him 545.5 0.5 +gasp, 546.0 0.5 +slip 546.5 0.5 +sideways 547.0 0.5 +off 547.5 0.5 +the 548.0 0.5 +broom 548.5 0.5 +and 549.0 0.5 +— 549.5 0.5 ++ 550.0 0.5 +WHAM 550.5 0.5 +— 551.0 0.5 +a 551.5 0.5 +thud 552.0 0.5 +and 552.5 0.5 +a 553.0 0.5 +nasty 553.5 0.5 +crack 554.0 0.5 +and 554.5 0.5 +Neville 555.0 0.5 +lay 555.5 0.5 +facedown 556.0 0.5 +on 556.5 0.5 +the 557.0 0.5 +grass 557.5 0.5 +in 558.0 0.5 +a 558.5 0.5 +heap. 559.0 0.5 +His 559.5 0.5 +broomstick 560.0 0.5 +was 560.5 0.5 +still 561.0 0.5 +rising 561.5 0.5 +higher 562.0 0.5 +and 562.5 0.5 +higher, 563.0 0.5 +and 563.5 0.5 +started 564.0 0.5 +to 564.5 0.5 +drift 565.0 0.5 +lazily 565.5 0.5 +toward 566.0 0.5 +the 566.5 0.5 +forbidden 567.0 0.5 +forest 567.5 0.5 +and 568.0 0.5 +out 568.5 0.5 +of 569.0 0.5 +sight. 569.5 0.5 ++ 570.0 0.5 +Madam 570.5 0.5 +Hooch 571.0 0.5 +was 571.5 0.5 +bending 572.0 0.5 +over 572.5 0.5 +Neville, 573.0 0.5 +her 573.5 0.5 +face 574.0 0.5 +as 574.5 0.5 +white 575.0 0.5 +as 575.5 0.5 +his. 576.0 0.5 +"""Broken" 576.5 0.5 +"wrist,""" 577.0 0.5 +Harry 577.5 0.5 +heard 578.0 0.5 +her 578.5 0.5 +mutter. 579.0 0.5 +"""Come" 579.5 0.5 +on, 580.0 0.5 +boy 580.5 0.5 +— 581.0 0.5 +it's 581.5 0.5 +all 582.0 0.5 +right, 582.5 0.5 +up 583.0 0.5 +you 583.5 0.5 +"get.""" 584.0 0.5 ++ 584.5 0.5 +She 585.0 0.5 +turned 585.5 0.5 +to 586.0 0.5 +the 586.5 0.5 +rest 587.0 0.5 +of 587.5 0.5 +the 588.0 0.5 +class. 588.5 0.5 +"""None" 589.0 0.5 +of 589.5 0.5 +you 590.0 0.5 +is 590.5 0.5 +to 591.0 0.5 +move 591.5 0.5 +while 592.0 0.5 +I 592.5 0.5 +take 593.0 0.5 +this 593.5 0.5 +boy 594.0 0.5 +to 594.5 0.5 +the 595.0 0.5 +hospital 595.5 0.5 +wing! 596.0 0.5 +You 596.5 0.5 +leave 597.0 0.5 +those 597.5 0.5 +brooms 598.0 0.5 +where 598.5 0.5 +they 599.0 0.5 +are 599.5 0.5 +or 600.0 0.5 +you'll 600.5 0.5 +be 601.0 0.5 +out 601.5 0.5 +of 602.0 0.5 +Hogwarts 602.5 0.5 +before 603.0 0.5 +you 603.5 0.5 +can 604.0 0.5 +say 604.5 0.5 +'Quidditch.' 605.0 0.5 +Come 605.5 0.5 +on, 606.0 0.5 +"dear.""" 606.5 0.5 ++ 607.0 0.5 +Neville, 607.5 0.5 +his 608.0 0.5 +face 608.5 0.5 +tear-streaked, 609.0 0.5 +clutching 609.5 0.5 +his 610.0 0.5 +wrist, 610.5 0.5 +hobbled 611.0 0.5 +off 611.5 0.5 +with 612.0 0.5 +Madam 612.5 0.5 +Hooch, 613.0 0.5 +who 613.5 0.5 +had 614.0 0.5 +her 614.5 0.5 +arm 615.0 0.5 +around 615.5 0.5 +him. 616.0 0.5 ++ 616.5 0.5 +No 617.0 0.5 +sooner 617.5 0.5 +were 618.0 0.5 +they 618.5 0.5 +out 619.0 0.5 +of 619.5 0.5 +earshot 620.0 0.5 +than 620.5 0.5 +Malfoy 621.0 0.5 +burst 621.5 0.5 +into 622.0 0.5 +laughter. 622.5 0.5 ++ 623.0 0.5 +"""Did" 623.5 0.5 +you 624.0 0.5 +see 624.5 0.5 +his 625.0 0.5 +face, 625.5 0.5 +the 626.0 0.5 +great 626.5 0.5 +"lump?""" 627.0 0.5 ++ 627.5 0.5 +The 628.0 0.5 +other 628.5 0.5 +Slytherins 629.0 0.5 +joined 629.5 0.5 +in. 630.0 0.5 ++ 630.5 0.5 +"""Shut" 631.0 0.5 +up, 631.5 0.5 +"Malfoy,""" 632.0 0.5 +snapped 632.5 0.5 +Parvati 633.0 0.5 +Patil. 633.5 0.5 ++ 634.0 0.5 +"""Ooh," 634.5 0.5 +sticking 635.0 0.5 +up 635.5 0.5 +for 636.0 0.5 +"Longbottom?""" 636.5 0.5 +said 637.0 0.5 +Pansy 637.5 0.5 +Parkinson, 638.0 0.5 +a 638.5 0.5 +hard-faced 639.0 0.5 +Slytherin 639.5 0.5 +girl. 640.0 0.5 +"""Never" 640.5 0.5 +thought 641.0 0.5 +you'd italic 641.5 0.5 +like 642.0 0.5 +fat 642.5 0.5 +little 643.0 0.5 +crybabies, 643.5 0.5 +"Parvati.""" 644.0 0.5 ++ 644.5 0.5 +"""Look!""" 645.0 0.5 +said 645.5 0.5 +Malfoy, 646.0 0.5 +darting 646.5 0.5 +forward 647.0 0.5 +and 647.5 0.5 +snatching 648.0 0.5 +something 648.5 0.5 +out 649.0 0.5 +of 649.5 0.5 +the 650.0 0.5 +grass. 650.5 0.5 +"""It's" 651.0 0.5 +that 651.5 0.5 +stupid 652.0 0.5 +thing 652.5 0.5 +Longbottom's 653.0 0.5 +gran 653.5 0.5 +sent 654.0 0.5 +"him.""" 654.5 0.5 ++ 655.0 0.5 +The 655.5 0.5 +Remembrall 656.0 0.5 +glittered 656.5 0.5 +in 657.0 0.5 +the 657.5 0.5 +sun 658.0 0.5 +as 658.5 0.5 +he 659.0 0.5 +held 659.5 0.5 +it 660.0 0.5 +up. 660.5 0.5 ++ 661.0 16 diff --git a/data/language/harrypotter/task-harry_run-2_events.tsv b/data/language/harrypotter/task-harry_run-2_events.tsv new file mode 100644 index 00000000..3f74fee1 --- /dev/null +++ b/data/language/harrypotter/task-harry_run-2_events.tsv @@ -0,0 +1,1353 @@ +word format onset duration ++ 0 10 +"""Give" 10.0 0.5 +that 10.5 0.5 +here, 11.0 0.5 +"Malfoy,""" 11.5 0.5 +said 12.0 0.5 +Harry 12.5 0.5 +quietly. 13.0 0.5 +Everyone 13.5 0.5 +stopped 14.0 0.5 +talking 14.5 0.5 +to 15.0 0.5 +watch. 15.5 0.5 ++ 16.0 0.5 +Malfoy 16.5 0.5 +smiled 17.0 0.5 +nastily. 17.5 0.5 +"""I" 18.0 0.5 +think 18.5 0.5 +I'll 19.0 0.5 +leave 19.5 0.5 +it 20.0 0.5 +somewhere 20.5 0.5 +for 21.0 0.5 +Longbottom 21.5 0.5 +to 22.0 0.5 +find 22.5 0.5 +— 23.0 0.5 +how 23.5 0.5 +about 24.0 0.5 +— 24.5 0.5 +up 25.0 0.5 +a 25.5 0.5 +"tree?""" 26.0 0.5 ++ 26.5 0.5 +"""Give" 27.0 0.5 +it 27.5 0.5 +"here!""" italic 28.0 0.5 +Harry 28.5 0.5 +yelled, 29.0 0.5 +but 29.5 0.5 +Malfoy 30.0 0.5 +had 30.5 0.5 +leapt 31.0 0.5 +onto 31.5 0.5 +his 32.0 0.5 +broomstick 32.5 0.5 +and 33.0 0.5 +taken 33.5 0.5 +off. 34.0 0.5 +He 34.5 0.5 +hadn't 35.0 0.5 +been 35.5 0.5 +lying, 36.0 0.5 +he 36.5 0.5 +could italic 37.0 0.5 +fly 37.5 0.5 +well. 38.0 0.5 +Hovering 38.5 0.5 +level 39.0 0.5 +with 39.5 0.5 +the 40.0 0.5 +topmost 40.5 0.5 +branches 41.0 0.5 +of 41.5 0.5 +an 42.0 0.5 +oak 42.5 0.5 +he 43.0 0.5 +called, 43.5 0.5 +"""Come" 44.0 0.5 +and 44.5 0.5 +get 45.0 0.5 +it, 45.5 0.5 +"Potter!""" 46.0 0.5 ++ 46.5 0.5 +Harry 47.0 0.5 +grabbed 47.5 0.5 +his 48.0 0.5 +broom. 48.5 0.5 ++ 49.0 0.5 +"""No!""" italic 49.5 0.5 +shouted 50.0 0.5 +Hermione 50.5 0.5 +Granger. 51.0 0.5 +"""Madam" 51.5 0.5 +Hooch 52.0 0.5 +told 52.5 0.5 +us 53.0 0.5 +not 53.5 0.5 +to 54.0 0.5 +move 54.5 0.5 +— 55.0 0.5 +you'll 55.5 0.5 +get 56.0 0.5 +us 56.5 0.5 +all 57.0 0.5 +into 57.5 0.5 +"trouble.""" 58.0 0.5 ++ 58.5 0.5 +Harry 59.0 0.5 +ignored 59.5 0.5 +her. 60.0 0.5 +Blood 60.5 0.5 +was 61.0 0.5 +pounding 61.5 0.5 +in 62.0 0.5 +his 62.5 0.5 +ears. 63.0 0.5 +He 63.5 0.5 +mounted 64.0 0.5 +the 64.5 0.5 +broom 65.0 0.5 +and 65.5 0.5 +kicked 66.0 0.5 +hard 66.5 0.5 +against 67.0 0.5 +the 67.5 0.5 +ground 68.0 0.5 +and 68.5 0.5 +up, 69.0 0.5 +up 69.5 0.5 +he 70.0 0.5 +soared; 70.5 0.5 +air 71.0 0.5 +rushed 71.5 0.5 +through 72.0 0.5 +his 72.5 0.5 +hair, 73.0 0.5 +and 73.5 0.5 +his 74.0 0.5 +robes 74.5 0.5 +whipped 75.0 0.5 +out 75.5 0.5 +behind 76.0 0.5 +him 76.5 0.5 +— 77.0 0.5 +and 77.5 0.5 +in 78.0 0.5 +a 78.5 0.5 +rush 79.0 0.5 +of 79.5 0.5 +fierce 80.0 0.5 +joy 80.5 0.5 +he 81.0 0.5 +realized 81.5 0.5 +he'd 82.0 0.5 +found 82.5 0.5 +something 83.0 0.5 +he 83.5 0.5 +could 84.0 0.5 +do 84.5 0.5 +without 85.0 0.5 +being 85.5 0.5 +taught 86.0 0.5 +— 86.5 0.5 +this 87.0 0.5 +was 87.5 0.5 +easy, 88.0 0.5 +this 88.5 0.5 +was 89.0 0.5 +wonderful. italic 89.5 0.5 +He 90.0 0.5 +pulled 90.5 0.5 +his 91.0 0.5 +broomstick 91.5 0.5 +up 92.0 0.5 +a 92.5 0.5 +little 93.0 0.5 +to 93.5 0.5 +take 94.0 0.5 +it 94.5 0.5 +even 95.0 0.5 +higher, 95.5 0.5 +and 96.0 0.5 +heard 96.5 0.5 +screams 97.0 0.5 +and 97.5 0.5 +gasps 98.0 0.5 +of 98.5 0.5 +girls 99.0 0.5 +back 99.5 0.5 +on 100.0 0.5 +the 100.5 0.5 +ground 101.0 0.5 +and 101.5 0.5 +an 102.0 0.5 +admiring 102.5 0.5 +whoop 103.0 0.5 +from 103.5 0.5 +Ron. 104.0 0.5 ++ 104.5 0.5 +He 105.0 0.5 +turned 105.5 0.5 +his 106.0 0.5 +broomstick 106.5 0.5 +sharply 107.0 0.5 +to 107.5 0.5 +face 108.0 0.5 +Malfoy 108.5 0.5 +in 109.0 0.5 +midair. 109.5 0.5 ++ 110.0 0.5 +Malfoy 110.5 0.5 +looked 111.0 0.5 +stunned. 111.5 0.5 +"""Give" 112.0 0.5 +it 112.5 0.5 +"here,""" 113.0 0.5 +Harry 113.5 0.5 +called, 114.0 0.5 +"""or" 114.5 0.5 +I'll 115.0 0.5 +knock 115.5 0.5 +you 116.0 0.5 +off 116.5 0.5 +that 117.0 0.5 +"broom!""" 117.5 0.5 ++ 118.0 0.5 +"""Oh," 118.5 0.5 +"yeah?""" 119.0 0.5 +said 119.5 0.5 +Malfoy, 120.0 0.5 +trying 120.5 0.5 +to 121.0 0.5 +sneer, 121.5 0.5 +but 122.0 0.5 +looking 122.5 0.5 +worried. 123.0 0.5 ++ 123.5 0.5 +Harry 124.0 0.5 +knew, 124.5 0.5 +somehow, 125.0 0.5 +what 125.5 0.5 +to 126.0 0.5 +do. 126.5 0.5 +He 127.0 0.5 +leaned 127.5 0.5 +forward 128.0 0.5 +and 128.5 0.5 +grasped 129.0 0.5 +the 129.5 0.5 +broom 130.0 0.5 +tightly 130.5 0.5 +in 131.0 0.5 +both 131.5 0.5 +hands, 132.0 0.5 +and 132.5 0.5 +it 133.0 0.5 +shot 133.5 0.5 +toward 134.0 0.5 +Malfoy 134.5 0.5 +like 135.0 0.5 +a 135.5 0.5 +javelin. 136.0 0.5 +Malfoy 136.5 0.5 +only 137.0 0.5 +just 137.5 0.5 +got 138.0 0.5 +out 138.5 0.5 +of 139.0 0.5 +the 139.5 0.5 +way 140.0 0.5 +in 140.5 0.5 +time; 141.0 0.5 +Harry 141.5 0.5 +made 142.0 0.5 +a 142.5 0.5 +sharp 143.0 0.5 +about-face 143.5 0.5 +and 144.0 0.5 +held 144.5 0.5 +the 145.0 0.5 +broom 145.5 0.5 +steady. 146.0 0.5 +A 146.5 0.5 +few 147.0 0.5 +people 147.5 0.5 +below 148.0 0.5 +were 148.5 0.5 +clapping. 149.0 0.5 ++ 149.5 0.5 +"""No" 150.0 0.5 +Crabbe 150.5 0.5 +and 151.0 0.5 +Goyle 151.5 0.5 +up 152.0 0.5 +here 152.5 0.5 +to 153.0 0.5 +save 153.5 0.5 +your 154.0 0.5 +neck, 154.5 0.5 +"Malfoy,""" 155.0 0.5 +Harry 155.5 0.5 +called. 156.0 0.5 ++ 156.5 0.5 +The 157.0 0.5 +same 157.5 0.5 +thought 158.0 0.5 +seemed 158.5 0.5 +to 159.0 0.5 +have 159.5 0.5 +struck 160.0 0.5 +Malfoy. 160.5 0.5 ++ 161.0 0.5 +"""Catch" 161.5 0.5 +it 162.0 0.5 +if 162.5 0.5 +you 163.0 0.5 +can, 163.5 0.5 +"then!""" 164.0 0.5 +he 164.5 0.5 +shouted, 165.0 0.5 +and 165.5 0.5 +he 166.0 0.5 +threw 166.5 0.5 +the 167.0 0.5 +glass 167.5 0.5 +ball 168.0 0.5 +high 168.5 0.5 +into 169.0 0.5 +the 169.5 0.5 +air 170.0 0.5 +and 170.5 0.5 +streaked 171.0 0.5 +back 171.5 0.5 +toward 172.0 0.5 +the 172.5 0.5 +ground. 173.0 0.5 ++ 173.5 0.5 +Harry 174.0 0.5 +saw, 174.5 0.5 +as 175.0 0.5 +though 175.5 0.5 +in 176.0 0.5 +slow 176.5 0.5 +motion, 177.0 0.5 +the 177.5 0.5 +ball 178.0 0.5 +rise 178.5 0.5 +up 179.0 0.5 +in 179.5 0.5 +the 180.0 0.5 +air 180.5 0.5 +and 181.0 0.5 +then 181.5 0.5 +start 182.0 0.5 +to 182.5 0.5 +fall. 183.0 0.5 +He 183.5 0.5 +leaned 184.0 0.5 +forward 184.5 0.5 +and 185.0 0.5 +pointed 185.5 0.5 +his 186.0 0.5 +broom 186.5 0.5 +handle 187.0 0.5 +down 187.5 0.5 +— 188.0 0.5 +next 188.5 0.5 +second 189.0 0.5 +he 189.5 0.5 +was 190.0 0.5 +gathering 190.5 0.5 +speed 191.0 0.5 +in 191.5 0.5 +a 192.0 0.5 +steep 192.5 0.5 +dive, 193.0 0.5 +racing 193.5 0.5 +the 194.0 0.5 +ball 194.5 0.5 +— 195.0 0.5 +wind 195.5 0.5 +whistled 196.0 0.5 +in 196.5 0.5 +his 197.0 0.5 +ears, 197.5 0.5 +mingled 198.0 0.5 +with 198.5 0.5 +the 199.0 0.5 +screams 199.5 0.5 +of 200.0 0.5 +people 200.5 0.5 +watching 201.0 0.5 +— 201.5 0.5 +he 202.0 0.5 +stretched 202.5 0.5 +out 203.0 0.5 +his 203.5 0.5 +hand 204.0 0.5 +— 204.5 0.5 +a 205.0 0.5 +foot 205.5 0.5 +from 206.0 0.5 +the 206.5 0.5 +ground 207.0 0.5 +he 207.5 0.5 +caught 208.0 0.5 +it, 208.5 0.5 +just 209.0 0.5 +in 209.5 0.5 +time 210.0 0.5 +to 210.5 0.5 +pull 211.0 0.5 +his 211.5 0.5 +broom 212.0 0.5 +straight, 212.5 0.5 +and 213.0 0.5 +he 213.5 0.5 +toppled 214.0 0.5 +gently 214.5 0.5 +onto 215.0 0.5 +the 215.5 0.5 +grass 216.0 0.5 +with 216.5 0.5 +the 217.0 0.5 +Remembrall 217.5 0.5 +clutched 218.0 0.5 +safely 218.5 0.5 +in 219.0 0.5 +his 219.5 0.5 +fist. 220.0 0.5 ++ 220.5 0.5 +"""HARRY" 221.0 0.5 +"POTTER!""" 221.5 0.5 ++ 222.0 0.5 +His 222.5 0.5 +heart 223.0 0.5 +sank 223.5 0.5 +faster 224.0 0.5 +than 224.5 0.5 +he'd 225.0 0.5 +just 225.5 0.5 +dived. 226.0 0.5 +Professor 226.5 0.5 +McGonagall 227.0 0.5 +was 227.5 0.5 +running 228.0 0.5 +toward 228.5 0.5 +them. 229.0 0.5 +He 229.5 0.5 +got 230.0 0.5 +to 230.5 0.5 +his 231.0 0.5 +feet, 231.5 0.5 +trembling. 232.0 0.5 ++ 232.5 0.5 +"""Never" italic 233.0 0.5 +— 233.5 0.5 +in 234.0 0.5 +all 234.5 0.5 +my 235.0 0.5 +time 235.5 0.5 +at 236.0 0.5 +Hogwarts 236.5 0.5 +"—""" 237.0 0.5 +Professor 237.5 0.5 +McGonagall 238.0 0.5 +was 238.5 0.5 +almost 239.0 0.5 +speechless 239.5 0.5 +with 240.0 0.5 +shock, 240.5 0.5 +and 241.0 0.5 +her 241.5 0.5 +glasses 242.0 0.5 +flashed 242.5 0.5 +furiously, 243.0 0.5 +"""—" 243.5 0.5 +how 244.0 0.5 +dare italic 244.5 0.5 +you 245.0 0.5 +— 245.5 0.5 +might 246.0 0.5 +have 246.5 0.5 +broken 247.0 0.5 +your 247.5 0.5 +neck 248.0 0.5 +"—""" 248.5 0.5 ++ 249.0 0.5 +"""It" 249.5 0.5 +wasn't 250.0 0.5 +his 250.5 0.5 +fault, 251.0 0.5 +Professor 251.5 0.5 +"—""" 252.0 0.5 ++ 252.5 0.5 +"""Be" 253.0 0.5 +quiet, 253.5 0.5 +Miss 254.0 0.5 +Patil 254.5 0.5 +"—""" 255.0 0.5 ++ 255.5 0.5 +"""But" 256.0 0.5 +Malfoy 256.5 0.5 +"—""" 257.0 0.5 +"""That's" 257.5 0.5 +enough, 258.0 0.5 +Mr. 258.5 0.5 +Weasley. 259.0 0.5 +Potter, 259.5 0.5 +follow 260.0 0.5 +me, 260.5 0.5 +"now.""" 261.0 0.5 ++ 261.5 0.5 +Harry 262.0 0.5 +caught 262.5 0.5 +sight 263.0 0.5 +of 263.5 0.5 +Malfoy, 264.0 0.5 +Crabbe, 264.5 0.5 +and 265.0 0.5 +Goyle's 265.5 0.5 +triumphant 266.0 0.5 +faces 266.5 0.5 +as 267.0 0.5 +he 267.5 0.5 +left, 268.0 0.5 +walking 268.5 0.5 +numbly 269.0 0.5 +in 269.5 0.5 +Professor 270.0 0.5 +McGonagall's 270.5 0.5 +wake 271.0 0.5 +as 271.5 0.5 +she 272.0 0.5 +strode 272.5 0.5 +toward 273.0 0.5 +the 273.5 0.5 +castle. 274.0 0.5 +He 274.5 0.5 +was 275.0 0.5 +going 275.5 0.5 +to 276.0 0.5 +be 276.5 0.5 +expelled, 277.0 0.5 +he 277.5 0.5 +just 278.0 0.5 +knew 278.5 0.5 +it. 279.0 0.5 +He 279.5 0.5 +wanted 280.0 0.5 +to 280.5 0.5 +say 281.0 0.5 +something 281.5 0.5 +to 282.0 0.5 +defend 282.5 0.5 +himself, 283.0 0.5 +but 283.5 0.5 +there 284.0 0.5 +seemed 284.5 0.5 +to 285.0 0.5 +be 285.5 0.5 +something 286.0 0.5 +wrong 286.5 0.5 +with 287.0 0.5 +his 287.5 0.5 +voice. 288.0 0.5 +Professor 288.5 0.5 +McGonagall 289.0 0.5 +was 289.5 0.5 +sweeping 290.0 0.5 +along 290.5 0.5 +without 291.0 0.5 +even 291.5 0.5 +looking 292.0 0.5 +at 292.5 0.5 +him; 293.0 0.5 +he 293.5 0.5 +had 294.0 0.5 +to 294.5 0.5 +jog 295.0 0.5 +to 295.5 0.5 +keep 296.0 0.5 +up. 296.5 0.5 +Now 297.0 0.5 +he'd 297.5 0.5 +done 298.0 0.5 +it. 298.5 0.5 +He 299.0 0.5 +hadn't 299.5 0.5 +even 300.0 0.5 +lasted 300.5 0.5 +two 301.0 0.5 +weeks. 301.5 0.5 +He'd 302.0 0.5 +be 302.5 0.5 +packing 303.0 0.5 +his 303.5 0.5 +bags 304.0 0.5 +in 304.5 0.5 +ten 305.0 0.5 +minutes. 305.5 0.5 +What 306.0 0.5 +would 306.5 0.5 +the 307.0 0.5 +Dursleys 307.5 0.5 +say 308.0 0.5 +when 308.5 0.5 +he 309.0 0.5 +turned 309.5 0.5 +up 310.0 0.5 +on 310.5 0.5 +the 311.0 0.5 +doorstep? 311.5 0.5 ++ 312.0 0.5 +Up 312.5 0.5 +the 313.0 0.5 +front 313.5 0.5 +steps, 314.0 0.5 +up 314.5 0.5 +the 315.0 0.5 +marble 315.5 0.5 +staircase 316.0 0.5 +inside, 316.5 0.5 +and 317.0 0.5 +still 317.5 0.5 +Professor 318.0 0.5 +McGonagall 318.5 0.5 +didn't 319.0 0.5 +say 319.5 0.5 +a 320.0 0.5 +word 320.5 0.5 +to 321.0 0.5 +him. 321.5 0.5 +She 322.0 0.5 +wrenched 322.5 0.5 +open 323.0 0.5 +doors 323.5 0.5 +and 324.0 0.5 +marched 324.5 0.5 +along 325.0 0.5 +corridors 325.5 0.5 +with 326.0 0.5 +Harry 326.5 0.5 +trotting 327.0 0.5 +miserably 327.5 0.5 +behind 328.0 0.5 +her. 328.5 0.5 +Maybe 329.0 0.5 +she 329.5 0.5 +was 330.0 0.5 +taking 330.5 0.5 +him 331.0 0.5 +to 331.5 0.5 +Dumbledore. 332.0 0.5 +He 332.5 0.5 +thought 333.0 0.5 +of 333.5 0.5 +Hagrid, 334.0 0.5 +expelled 334.5 0.5 +but 335.0 0.5 +allowed 335.5 0.5 +to 336.0 0.5 +stay 336.5 0.5 +on 337.0 0.5 +as 337.5 0.5 +gamekeeper. 338.0 0.5 +Perhaps 338.5 0.5 +he 339.0 0.5 +could 339.5 0.5 +be 340.0 0.5 +Hagrid's 340.5 0.5 +assistant. 341.0 0.5 +His 341.5 0.5 +stomach 342.0 0.5 +twisted 342.5 0.5 +as 343.0 0.5 +he 343.5 0.5 +imagined 344.0 0.5 +it, 344.5 0.5 +watching 345.0 0.5 +Ron 345.5 0.5 +and 346.0 0.5 +the 346.5 0.5 +others 347.0 0.5 +becoming 347.5 0.5 +wizards 348.0 0.5 +while 348.5 0.5 +he 349.0 0.5 +stumped 349.5 0.5 +around 350.0 0.5 +the 350.5 0.5 +grounds 351.0 0.5 +carrying 351.5 0.5 +Hagrid's 352.0 0.5 +bag. 352.5 0.5 ++ 353.0 0.5 +Professor 353.5 0.5 +McGonagall 354.0 0.5 +stopped 354.5 0.5 +outside 355.0 0.5 +a 355.5 0.5 +classroom. 356.0 0.5 +She 356.5 0.5 +opened 357.0 0.5 +the 357.5 0.5 +door 358.0 0.5 +and 358.5 0.5 +poked 359.0 0.5 +her 359.5 0.5 +head 360.0 0.5 +inside. 360.5 0.5 ++ 361.0 0.5 +"""Excuse" 361.5 0.5 +me, 362.0 0.5 +Professor 362.5 0.5 +Flitwick, 363.0 0.5 +could 363.5 0.5 +I 364.0 0.5 +borrow 364.5 0.5 +Wood 365.0 0.5 +for 365.5 0.5 +a 366.0 0.5 +"moment?""" 366.5 0.5 ++ 367.0 0.5 +Wood? 367.5 0.5 +thought 368.0 0.5 +Harry, 368.5 0.5 +bewildered; 369.0 0.5 +was 369.5 0.5 +Wood 370.0 0.5 +a 370.5 0.5 +cane 371.0 0.5 +she 371.5 0.5 +was 372.0 0.5 +going 372.5 0.5 +to 373.0 0.5 +use 373.5 0.5 +on 374.0 0.5 +him? 374.5 0.5 +But 375.0 0.5 +Wood 375.5 0.5 +turned 376.0 0.5 +out 376.5 0.5 +to 377.0 0.5 +be 377.5 0.5 +a 378.0 0.5 +person, 378.5 0.5 +a 379.0 0.5 +burly 379.5 0.5 +fifth-year 380.0 0.5 +boy 380.5 0.5 +who 381.0 0.5 +came 381.5 0.5 +out 382.0 0.5 +of 382.5 0.5 +Flitwick's 383.0 0.5 +class 383.5 0.5 +looking 384.0 0.5 +confused. 384.5 0.5 ++ 385.0 0.5 +"""Follow" 385.5 0.5 +me, 386.0 0.5 +you 386.5 0.5 +"two,""" 387.0 0.5 +said 387.5 0.5 +Professor 388.0 0.5 +McGonagall, 388.5 0.5 +and 389.0 0.5 +they 389.5 0.5 +marched 390.0 0.5 +on 390.5 0.5 +up 391.0 0.5 +the 391.5 0.5 +corridor, 392.0 0.5 +Wood 392.5 0.5 +looking 393.0 0.5 +curiously 393.5 0.5 +at 394.0 0.5 +Harry. 394.5 0.5 ++ 395.0 0.5 +"""In" 395.5 0.5 +"here.""" 396.0 0.5 ++ 396.5 0.5 +Professor 397.0 0.5 +McGonagall 397.5 0.5 +pointed 398.0 0.5 +them 398.5 0.5 +into 399.0 0.5 +a 399.5 0.5 +classroom 400.0 0.5 +that 400.5 0.5 +was 401.0 0.5 +empty 401.5 0.5 +except 402.0 0.5 +for 402.5 0.5 +Peeves, 403.0 0.5 +who 403.5 0.5 +was 404.0 0.5 +busy 404.5 0.5 +writing 405.0 0.5 +rude 405.5 0.5 +words 406.0 0.5 +on 406.5 0.5 +the 407.0 0.5 +blackboard. 407.5 0.5 ++ 408.0 0.5 +"""Out," 408.5 0.5 +"Peeves!""" 409.0 0.5 +she 409.5 0.5 +barked. 410.0 0.5 +Peeves 410.5 0.5 +threw 411.0 0.5 +the 411.5 0.5 +chalk 412.0 0.5 +into 412.5 0.5 +a 413.0 0.5 +bin, 413.5 0.5 +which 414.0 0.5 +clanged 414.5 0.5 +loudly, 415.0 0.5 +and 415.5 0.5 +he 416.0 0.5 +swooped 416.5 0.5 +out 417.0 0.5 +cursing. 417.5 0.5 +Professor 418.0 0.5 +McGonagall 418.5 0.5 +slammed 419.0 0.5 +the 419.5 0.5 +door 420.0 0.5 +behind 420.5 0.5 +him 421.0 0.5 +and 421.5 0.5 +turned 422.0 0.5 +to 422.5 0.5 +face 423.0 0.5 +the 423.5 0.5 +two 424.0 0.5 +boys. 424.5 0.5 ++ 425.0 0.5 +"""Potter," 425.5 0.5 +this 426.0 0.5 +is 426.5 0.5 +Oliver 427.0 0.5 +Wood. 427.5 0.5 + Wood 428.0 0.5 +— 428.5 0.5 +I've 429.0 0.5 +found 429.5 0.5 +you 430.0 0.5 +a 430.5 0.5 +"Seeker.""" 431.0 0.5 ++ 431.5 0.5 +Wood's 432.0 0.5 +expression 432.5 0.5 +changed 433.0 0.5 +from 433.5 0.5 +puzzlement 434.0 0.5 +to 434.5 0.5 +delight. 435.0 0.5 ++ 435.5 0.5 +"""Are" 436.0 0.5 +you 436.5 0.5 +serious, 437.0 0.5 +"Professor?""" 437.5 0.5 ++ 438.0 0.5 +"""Absolutely,""" 438.5 0.5 +said 439.0 0.5 +Professor 439.5 0.5 +McGonagall 440.0 0.5 +crisply. 440.5 0.5 +"""The" 441.0 0.5 +boy's 441.5 0.5 +a 442.0 0.5 +natural. 442.5 0.5 +I've 443.0 0.5 +never 443.5 0.5 +seen 444.0 0.5 +anything 444.5 0.5 +like 445.0 0.5 +it. 445.5 0.5 +Was 446.0 0.5 +that 446.5 0.5 +your 447.0 0.5 +first 447.5 0.5 +time 448.0 0.5 +on 448.5 0.5 +a 449.0 0.5 +broomstick, 449.5 0.5 +"Potter?""" 450.0 0.5 ++ 450.5 0.5 +Harry 451.0 0.5 +nodded 451.5 0.5 +silently. 452.0 0.5 +He 452.5 0.5 +didn't 453.0 0.5 +have 453.5 0.5 +a 454.0 0.5 +clue 454.5 0.5 +what 455.0 0.5 +was 455.5 0.5 +going 456.0 0.5 +on, 456.5 0.5 +but 457.0 0.5 +he 457.5 0.5 +didn't 458.0 0.5 +seem 458.5 0.5 +to 459.0 0.5 +be 459.5 0.5 +being 460.0 0.5 +expelled, 460.5 0.5 +and 461.0 0.5 +some 461.5 0.5 +of 462.0 0.5 +the 462.5 0.5 +feeling 463.0 0.5 +started 463.5 0.5 +coming 464.0 0.5 +back 464.5 0.5 +to 465.0 0.5 +his 465.5 0.5 +legs. 466.0 0.5 ++ 466.5 0.5 +"""He" 467.0 0.5 +caught 467.5 0.5 +that 468.0 0.5 +thing 468.5 0.5 +in 469.0 0.5 +his 469.5 0.5 +hand 470.0 0.5 +after 470.5 0.5 +a 471.0 0.5 +fifty-foot 471.5 0.5 +"dive,""" 472.0 0.5 +Professor 472.5 0.5 +McGonagall 473.0 0.5 +told 473.5 0.5 +Wood. 474.0 0.5 +"""Didn't" 474.5 0.5 +even 475.0 0.5 +scratch 475.5 0.5 +himself. 476.0 0.5 +Charlie 476.5 0.5 +Weasley 477.0 0.5 +couldn't 477.5 0.5 +have 478.0 0.5 +done 478.5 0.5 +"it.""" 479.0 0.5 ++ 479.5 0.5 +Wood 480.0 0.5 +was 480.5 0.5 +now 481.0 0.5 +looking 481.5 0.5 +as 482.0 0.5 +though 482.5 0.5 +all 483.0 0.5 +his 483.5 0.5 +dreams 484.0 0.5 +had 484.5 0.5 +come 485.0 0.5 +true 485.5 0.5 +at 486.0 0.5 +once. 486.5 0.5 ++ 487.0 0.5 +"""Ever" 487.5 0.5 +seen 488.0 0.5 +a 488.5 0.5 +game 489.0 0.5 +of 489.5 0.5 +Quidditch, 490.0 0.5 +"Potter?""" 490.5 0.5 +he 491.0 0.5 +asked 491.5 0.5 +excitedly. 492.0 0.5 ++ 492.5 0.5 +"""Wood's" 493.0 0.5 +captain 493.5 0.5 +of 494.0 0.5 +the 494.5 0.5 +Gryffindor 495.0 0.5 +"team,""" 495.5 0.5 +Professor 496.0 0.5 +McGonagall 496.5 0.5 +explained. 497.0 0.5 ++ 497.5 0.5 +"""He's" 498.0 0.5 +just 498.5 0.5 +the 499.0 0.5 +build 499.5 0.5 +for 500.0 0.5 +a 500.5 0.5 +Seeker, 501.0 0.5 +"too,""" 501.5 0.5 +said 502.0 0.5 +Wood, 502.5 0.5 +now 503.0 0.5 +walking 503.5 0.5 +around 504.0 0.5 +Harry 504.5 0.5 +and 505.0 0.5 +staring 505.5 0.5 +at 506.0 0.5 +him. 506.5 0.5 +"""Light" 507.0 0.5 +— 507.5 0.5 +speedy 508.0 0.5 +— 508.5 0.5 +we'll 509.0 0.5 +have 509.5 0.5 +to 510.0 0.5 +get 510.5 0.5 +him 511.0 0.5 +a 511.5 0.5 +decent 512.0 0.5 +broom, 512.5 0.5 +Professor 513.0 0.5 +— 513.5 0.5 +a 514.0 0.5 +Nimbus 514.5 0.5 +Two 515.0 0.5 +Thousand 515.5 0.5 +or 516.0 0.5 +a 516.5 0.5 +Cleansweep 517.0 0.5 +Seven, 517.5 0.5 +I'd 518.0 0.5 +"say.""" 518.5 0.5 ++ 519.0 0.5 +"""I" 519.5 0.5 +shall 520.0 0.5 +speak 520.5 0.5 +to 521.0 0.5 +Professor 521.5 0.5 +Dumbledore 522.0 0.5 +and 522.5 0.5 +see 523.0 0.5 +if 523.5 0.5 +we 524.0 0.5 +can't 524.5 0.5 +bend 525.0 0.5 +the 525.5 0.5 +first-year 526.0 0.5 +rule. 526.5 0.5 +Heaven 527.0 0.5 +knows, 527.5 0.5 +we 528.0 0.5 +need 528.5 0.5 +a 529.0 0.5 +better 529.5 0.5 +team 530.0 0.5 +than 530.5 0.5 +last 531.0 0.5 +year. 531.5 0.5 +Flattened 532.0 0.5 +in 532.5 0.5 +that 533.0 0.5 +last 533.5 0.5 +match 534.0 0.5 +by 534.5 0.5 +Slytherin, 535.0 0.5 +I 535.5 0.5 +couldn't 536.0 0.5 +look 536.5 0.5 +Severus 537.0 0.5 +Snape 537.5 0.5 +in 538.0 0.5 +the 538.5 0.5 +face 539.0 0.5 +for 539.5 0.5 +weeks. 540.0 0.5 +"...""" 540.5 0.5 ++ 541.0 0.5 +Professor 541.5 0.5 +McGonagall 542.0 0.5 +peered 542.5 0.5 +sternly 543.0 0.5 +over 543.5 0.5 +her 544.0 0.5 +glasses 544.5 0.5 +at 545.0 0.5 +Harry. 545.5 0.5 ++ 546.0 0.5 +"""I" 546.5 0.5 +want 547.0 0.5 +to 547.5 0.5 +hear 548.0 0.5 +you're 548.5 0.5 +training 549.0 0.5 +hard, 549.5 0.5 +Potter, 550.0 0.5 +or 550.5 0.5 +I 551.0 0.5 +may 551.5 0.5 +change 552.0 0.5 +my 552.5 0.5 +mind 553.0 0.5 +about 553.5 0.5 +punishing 554.0 0.5 +"you.""" 554.5 0.5 ++ 555.0 0.5 +Then 555.5 0.5 +she 556.0 0.5 +suddenly 556.5 0.5 +smiled. 557.0 0.5 ++ 557.5 0.5 +"""Your" 558.0 0.5 +father 558.5 0.5 +would 559.0 0.5 +have 559.5 0.5 +been 560.0 0.5 +"proud,""" 560.5 0.5 +she 561.0 0.5 +said. 561.5 0.5 +"""He" 562.0 0.5 +was 562.5 0.5 +an 563.0 0.5 +excellent 563.5 0.5 +Quidditch 564.0 0.5 +player 564.5 0.5 +"himself.""" 565.0 0.5 ++ 565.5 0.5 ++ 566.0 0.5 +"""You're" 566.5 0.5 +"joking.""" italic 567.0 0.5 ++ 567.5 0.5 +It 568.0 0.5 +was 568.5 0.5 +dinnertime. 569.0 0.5 +Harry 569.5 0.5 +had 570.0 0.5 +just 570.5 0.5 +finished 571.0 0.5 +telling 571.5 0.5 +Ron 572.0 0.5 +what 572.5 0.5 +had 573.0 0.5 +happened 573.5 0.5 +when 574.0 0.5 +he'd 574.5 0.5 +left 575.0 0.5 +the 575.5 0.5 +grounds 576.0 0.5 +with 576.5 0.5 +Professor 577.0 0.5 +McGonagall. 577.5 0.5 +Ron 578.0 0.5 +had 578.5 0.5 +a 579.0 0.5 +piece 579.5 0.5 +of 580.0 0.5 +steak 580.5 0.5 +and 581.0 0.5 +kidney 581.5 0.5 +pie 582.0 0.5 +halfway 582.5 0.5 +to 583.0 0.5 +his 583.5 0.5 +mouth, 584.0 0.5 +but 584.5 0.5 +he'd 585.0 0.5 +forgotten 585.5 0.5 +all 586.0 0.5 +about 586.5 0.5 +it. 587.0 0.5 ++ 587.5 0.5 +"""Seeker?""" italic 588.0 0.5 +he 588.5 0.5 +said. 589.0 0.5 +"""But" 589.5 0.5 +first 590.0 0.5 +years 590.5 0.5 +never italic 591.0 0.5 +— 591.5 0.5 +you 592.0 0.5 +must 592.5 0.5 +be 593.0 0.5 +the 593.5 0.5 +youngest 594.0 0.5 +House 594.5 0.5 +player 595.0 0.5 +in 595.5 0.5 +about 596.0 0.5 +"—""" 596.5 0.5 ++ 597.0 0.5 +"""—" 597.5 0.5 +a 598.0 0.5 +"century,""" 598.5 0.5 +said 599.0 0.5 +Harry, 599.5 0.5 +shoveling 600.0 0.5 +pie 600.5 0.5 +into 601.0 0.5 +his 601.5 0.5 +mouth. 602.0 0.5 +He 602.5 0.5 +felt 603.0 0.5 +particularly 603.5 0.5 +hungry 604.0 0.5 +after 604.5 0.5 +the 605.0 0.5 +excitement 605.5 0.5 +of 606.0 0.5 +the 606.5 0.5 +afternoon. 607.0 0.5 +"""Wood" 607.5 0.5 +told 608.0 0.5 +"me.""" 608.5 0.5 ++ 609.0 0.5 +Ron 609.5 0.5 +was 610.0 0.5 +so 610.5 0.5 +amazed, 611.0 0.5 +so 611.5 0.5 +impressed, 612.0 0.5 +he 612.5 0.5 +just 613.0 0.5 +sat 613.5 0.5 +and 614.0 0.5 +gaped 614.5 0.5 +at 615.0 0.5 +Harry. 615.5 0.5 ++ 616.0 0.5 +"""I" 616.5 0.5 +start 617.0 0.5 +training 617.5 0.5 +next 618.0 0.5 +"week,""" 618.5 0.5 +said 619.0 0.5 +Harry. 619.5 0.5 +"""Only" 620.0 0.5 +don't 620.5 0.5 +tell 621.0 0.5 +anyone, 621.5 0.5 +Wood 622.0 0.5 +wants 622.5 0.5 +to 623.0 0.5 +keep 623.5 0.5 +it 624.0 0.5 +a 624.5 0.5 +"secret.""" 625.0 0.5 ++ 625.5 0.5 +Fred 626.0 0.5 +and 626.5 0.5 +George 627.0 0.5 +Weasley 627.5 0.5 +now 628.0 0.5 +came 628.5 0.5 +into 629.0 0.5 +the 629.5 0.5 +hall, 630.0 0.5 +spotted 630.5 0.5 +Harry, 631.0 0.5 +and 631.5 0.5 +hurried 632.0 0.5 +over. 632.5 0.5 ++ 633.0 0.5 +"""Well" 633.5 0.5 +"done,""" 634.0 0.5 +said 634.5 0.5 +George 635.0 0.5 +in 635.5 0.5 +a 636.0 0.5 +low 636.5 0.5 +voice. 637.0 0.5 +"""Wood" 637.5 0.5 +told 638.0 0.5 +us. 638.5 0.5 +We're 639.0 0.5 +on 639.5 0.5 +the 640.0 0.5 +team 640.5 0.5 +too 641.0 0.5 +— 641.5 0.5 +"Beaters.""" 642.0 0.5 ++ 642.5 0.5 +"""I" 643.0 0.5 +tell 643.5 0.5 +you, 644.0 0.5 +we're 644.5 0.5 +going 645.0 0.5 +to 645.5 0.5 +win 646.0 0.5 +that 646.5 0.5 +Quidditch 647.0 0.5 +Cup 647.5 0.5 +for 648.0 0.5 +sure 648.5 0.5 +this 649.0 0.5 +"year,""" 649.5 0.5 +said 650.0 0.5 +Fred. 650.5 0.5 +"""We" 651.0 0.5 +haven't 651.5 0.5 +won 652.0 0.5 +since 652.5 0.5 +Charlie 653.0 0.5 +left, 653.5 0.5 +but 654.0 0.5 +this 654.5 0.5 +year's 655.0 0.5 +team 655.5 0.5 +is 656.0 0.5 +going 656.5 0.5 +to 657.0 0.5 +be 657.5 0.5 +brilliant. 658.0 0.5 +You 658.5 0.5 +must 659.0 0.5 +be 659.5 0.5 +good, 660.0 0.5 +Harry, 660.5 0.5 +Wood 661.0 0.5 +was 661.5 0.5 +almost 662.0 0.5 +skipping 662.5 0.5 +when 663.0 0.5 +he 663.5 0.5 +told 664.0 0.5 +"us.""" 664.5 0.5 ++ 665.0 0.5 +"""Anyway," 665.5 0.5 +we've 666.0 0.5 +got 666.5 0.5 +to 667.0 0.5 +go, 667.5 0.5 +Lee 668.0 0.5 +Jordan 668.5 0.5 +reckons 669.0 0.5 +he's 669.5 0.5 +found 670.0 0.5 +a 670.5 0.5 +new 671.0 0.5 +secret 671.5 0.5 +passageway 672.0 0.5 +out 672.5 0.5 +of 673.0 0.5 +the 673.5 0.5 +"school.""" 674.0 0.5 ++ 674.5 0.5 +"""Bet" 675.0 0.5 +it's 675.5 0.5 +that 676.0 0.5 +one 676.5 0.5 +behind 677.0 0.5 +the 677.5 0.5 +statue 678.0 0.5 +of 678.5 0.5 +Gregory 679.0 0.5 +the 679.5 0.5 +Smarmy 680.0 0.5 +that 680.5 0.5 +we 681.0 0.5 +found 681.5 0.5 +in 682.0 0.5 +our 682.5 0.5 +first 683.0 0.5 +week. 683.5 0.5 +See 684.0 0.5 +"you.""" 684.5 0.5 ++ 685 16 diff --git a/data/language/harrypotter/task-harry_run-3_events.tsv b/data/language/harrypotter/task-harry_run-3_events.tsv new file mode 100644 index 00000000..427a1ece --- /dev/null +++ b/data/language/harrypotter/task-harry_run-3_events.tsv @@ -0,0 +1,1061 @@ +word format onset duration ++ 0 10 +Fred 10.0 0.5 +and 10.5 0.5 +George 11.0 0.5 +had 11.5 0.5 +hardly 12.0 0.5 +disappeared 12.5 0.5 +when 13.0 0.5 +someone 13.5 0.5 +far 14.0 0.5 +less 14.5 0.5 +welcome 15.0 0.5 +turned 15.5 0.5 +up: 16.0 0.5 +Malfoy, 16.5 0.5 +flanked 17.0 0.5 +by 17.5 0.5 +Crabbe 18.0 0.5 +and 18.5 0.5 +Goyle. 19.0 0.5 ++ 19.5 0.5 +"""Having" 20.0 0.5 +a 20.5 0.5 +last 21.0 0.5 +meal, 21.5 0.5 +Potter? 22.0 0.5 +When 22.5 0.5 +are 23.0 0.5 +you 23.5 0.5 +getting 24.0 0.5 +the 24.5 0.5 +train 25.0 0.5 +back 25.5 0.5 +to 26.0 0.5 +the 26.5 0.5 +"Muggles?""" 27.0 0.5 ++ 27.5 0.5 +"""You're" 28.0 0.5 +a 28.5 0.5 +lot 29.0 0.5 +braver 29.5 0.5 +now 30.0 0.5 +that 30.5 0.5 +you're 31.0 0.5 +back 31.5 0.5 +on 32.0 0.5 +the 32.5 0.5 +ground 33.0 0.5 +and 33.5 0.5 +you've 34.0 0.5 +got 34.5 0.5 +your 35.0 0.5 +little 35.5 0.5 +friends 36.0 0.5 +with 36.5 0.5 +"you,""" 37.0 0.5 +said 37.5 0.5 +Harry 38.0 0.5 +coolly. 38.5 0.5 +There 39.0 0.5 +was 39.5 0.5 +of 40.0 0.5 +course 40.5 0.5 +nothing 41.0 0.5 +at 41.5 0.5 +all 42.0 0.5 +little 42.5 0.5 +about 43.0 0.5 +Crabbe 43.5 0.5 +and 44.0 0.5 +Goyle, 44.5 0.5 +but 45.0 0.5 +as 45.5 0.5 +the 46.0 0.5 +High 46.5 0.5 +Table 47.0 0.5 +was 47.5 0.5 +full 48.0 0.5 +of 48.5 0.5 +teachers, 49.0 0.5 +neither 49.5 0.5 +of 50.0 0.5 +them 50.5 0.5 +could 51.0 0.5 +do 51.5 0.5 +more 52.0 0.5 +than 52.5 0.5 +crack 53.0 0.5 +their 53.5 0.5 +knuckles 54.0 0.5 +and 54.5 0.5 +scowl. 55.0 0.5 ++ 55.5 0.5 +"""I'd" 56.0 0.5 +take 56.5 0.5 +you 57.0 0.5 +on 57.5 0.5 +anytime 58.0 0.5 +on 58.5 0.5 +my 59.0 0.5 +"own,""" 59.5 0.5 +said 60.0 0.5 +Malfoy. 60.5 0.5 +"""Tonight," 61.0 0.5 +if 61.5 0.5 +you 62.0 0.5 +want. 62.5 0.5 +Wizard's 63.0 0.5 +duel. 63.5 0.5 +Wands 64.0 0.5 +only 64.5 0.5 +— 65.0 0.5 +no 65.5 0.5 +contact. 66.0 0.5 +What's 66.5 0.5 +the 67.0 0.5 +matter? 67.5 0.5 +Never 68.0 0.5 +heard 68.5 0.5 +of 69.0 0.5 +a 69.5 0.5 +wizard's 70.0 0.5 +duel 70.5 0.5 +before, 71.0 0.5 +I 71.5 0.5 +"suppose?""" 72.0 0.5 ++ 72.5 0.5 +"""Of" 73.0 0.5 +course 73.5 0.5 +he 74.0 0.5 +"has,""" 74.5 0.5 +said 75.0 0.5 +Ron, 75.5 0.5 +wheeling 76.0 0.5 +around. 76.5 0.5 +"""I'm" 77.0 0.5 +his 77.5 0.5 +second, 78.0 0.5 +who's 78.5 0.5 +"yours?""" 79.0 0.5 ++ 79.5 0.5 +Malfoy 80.0 0.5 +looked 80.5 0.5 +at 81.0 0.5 +Crabbe 81.5 0.5 +and 82.0 0.5 +Goyle, 82.5 0.5 +sizing 83.0 0.5 +them 83.5 0.5 +up. 84.0 0.5 ++ 84.5 0.5 +"""Crabbe,""" 85.0 0.5 +he 85.5 0.5 +said. 86.0 0.5 +"""Midnight" 86.5 0.5 +all 87.0 0.5 +right? 87.5 0.5 +We'll 88.0 0.5 +meet 88.5 0.5 +you 89.0 0.5 +in 89.5 0.5 +the 90.0 0.5 +trophy 90.5 0.5 +room; 91.0 0.5 +that's 91.5 0.5 +always 92.0 0.5 +"unlocked.""" 92.5 0.5 ++ 93.0 0.5 +When 93.5 0.5 +Malfoy 94.0 0.5 +had 94.5 0.5 +gone, 95.0 0.5 +Ron 95.5 0.5 +and 96.0 0.5 +Harry 96.5 0.5 +looked 97.0 0.5 +at 97.5 0.5 +each 98.0 0.5 +other. 98.5 0.5 ++ 99.0 0.5 +"""What" 99.5 0.5 +is italic 100.0 0.5 +a 100.5 0.5 +wizard's 101.0 0.5 +"duel?""" 101.5 0.5 +said 102.0 0.5 +Harry. 102.5 0.5 +"""And" 103.0 0.5 +what 103.5 0.5 +do 104.0 0.5 +you 104.5 0.5 +mean, 105.0 0.5 +you're 105.5 0.5 +my 106.0 0.5 +"second?""" 106.5 0.5 ++ 107.0 0.5 +"""Well," 107.5 0.5 +a 108.0 0.5 +second's 108.5 0.5 +there 109.0 0.5 +to 109.5 0.5 +take 110.0 0.5 +over 110.5 0.5 +if 111.0 0.5 +you 111.5 0.5 +"die,""" 112.0 0.5 +said 112.5 0.5 +Ron 113.0 0.5 +casually, 113.5 0.5 +getting 114.0 0.5 +started 114.5 0.5 +at 115.0 0.5 +last 115.5 0.5 +on 116.0 0.5 +his 116.5 0.5 +cold 117.0 0.5 +pie. 117.5 0.5 +Catching 118.0 0.5 +the 118.5 0.5 +look 119.0 0.5 +on 119.5 0.5 +Harry's 120.0 0.5 +face, 120.5 0.5 +he 121.0 0.5 +added 121.5 0.5 +quickly, 122.0 0.5 +"""But" 122.5 0.5 +people 123.0 0.5 +only 123.5 0.5 +die 124.0 0.5 +in 124.5 0.5 +proper 125.0 0.5 +duels, 125.5 0.5 +you 126.0 0.5 +know, 126.5 0.5 +with 127.0 0.5 +real 127.5 0.5 +wizards. 128.0 0.5 +The 128.5 0.5 +most 129.0 0.5 +you 129.5 0.5 +and 130.0 0.5 +Malfoy'll 130.5 0.5 +be 131.0 0.5 +able 131.5 0.5 +to 132.0 0.5 +do 132.5 0.5 +is 133.0 0.5 +send 133.5 0.5 +sparks 134.0 0.5 +at 134.5 0.5 +each 135.0 0.5 +other. 135.5 0.5 +Neither 136.0 0.5 +of 136.5 0.5 +you 137.0 0.5 +knows 137.5 0.5 +enough 138.0 0.5 +magic 138.5 0.5 +to 139.0 0.5 +do 139.5 0.5 +any 140.0 0.5 +real 140.5 0.5 +damage. 141.0 0.5 +I 141.5 0.5 +bet 142.0 0.5 +he 142.5 0.5 +expected 143.0 0.5 +you 143.5 0.5 +to 144.0 0.5 +refuse, 144.5 0.5 +"anyway.""" 145.0 0.5 ++ 145.5 0.5 +"""And" 146.0 0.5 +what 146.5 0.5 +if 147.0 0.5 +I 147.5 0.5 +wave 148.0 0.5 +my 148.5 0.5 +wand 149.0 0.5 +and 149.5 0.5 +nothing 150.0 0.5 +"happens?""" 150.5 0.5 ++ 151.0 0.5 +"""Throw" 151.5 0.5 +it 152.0 0.5 +away 152.5 0.5 +and 153.0 0.5 +punch 153.5 0.5 +him 154.0 0.5 +on 154.5 0.5 +the 155.0 0.5 +"nose,""" 155.5 0.5 +Ron 156.0 0.5 +suggested. 156.5 0.5 ++ 157.0 0.5 +"""Excuse" 157.5 0.5 +"me.""" 158.0 0.5 ++ 158.5 0.5 +They 159.0 0.5 +both 159.5 0.5 +looked 160.0 0.5 +up. 160.5 0.5 +It 161.0 0.5 +was 161.5 0.5 +Hermione 162.0 0.5 +Granger. 162.5 0.5 ++ 163.0 0.5 +"""Can't" 163.5 0.5 +a 164.0 0.5 +person 164.5 0.5 +eat 165.0 0.5 +in 165.5 0.5 +peace 166.0 0.5 +in 166.5 0.5 +this 167.0 0.5 +"place?""" 167.5 0.5 +said 168.0 0.5 +Ron. 168.5 0.5 ++ 169.0 0.5 +Hermione 169.5 0.5 +ignored 170.0 0.5 +him 170.5 0.5 +and 171.0 0.5 +spoke 171.5 0.5 +to 172.0 0.5 +Harry. 172.5 0.5 +"""I" 173.0 0.5 +couldn't 173.5 0.5 +help 174.0 0.5 +overhearing 174.5 0.5 +what 175.0 0.5 +you 175.5 0.5 +and 176.0 0.5 +Malfoy 176.5 0.5 +were 177.0 0.5 +saying 177.5 0.5 +"—""" 178.0 0.5 ++ 178.5 0.5 +"""Bet" 179.0 0.5 +you 179.5 0.5 +"could,""" 180.0 0.5 +Ron 180.5 0.5 +muttered. 181.0 0.5 ++ 181.5 0.5 +"""—" 182.0 0.5 +and 182.5 0.5 +you 183.0 0.5 +mustn't italic 183.5 0.5 +go 184.0 0.5 +wandering 184.5 0.5 +around 185.0 0.5 +the 185.5 0.5 +school 186.0 0.5 +at 186.5 0.5 +night, 187.0 0.5 +think 187.5 0.5 +of 188.0 0.5 +the 188.5 0.5 +points 189.0 0.5 +you'll 189.5 0.5 +lose 190.0 0.5 +Gryffindor 190.5 0.5 +if 191.0 0.5 +you're 191.5 0.5 +caught, 192.0 0.5 +and 192.5 0.5 +you're 193.0 0.5 +bound 193.5 0.5 +to 194.0 0.5 +be. 194.5 0.5 +It's 195.0 0.5 +really 195.5 0.5 +very 196.0 0.5 +selfish 196.5 0.5 +of 197.0 0.5 +"you.""" 197.5 0.5 ++ 198.0 0.5 +"""And" 198.5 0.5 +it's 199.0 0.5 +really 199.5 0.5 +none 200.0 0.5 +of 200.5 0.5 +your 201.0 0.5 +"business,""" 201.5 0.5 +said 202.0 0.5 +Harry. 202.5 0.5 ++ 203.0 0.5 +"""Good-bye,""" 203.5 0.5 +said 204.0 0.5 +Ron. 204.5 0.5 ++ 205.0 0.5 ++ 205.5 0.5 +All 206.0 0.5 +the 206.5 0.5 +same, 207.0 0.5 +it 207.5 0.5 +wasn't 208.0 0.5 +what 208.5 0.5 +you'd 209.0 0.5 +call 209.5 0.5 +the 210.0 0.5 +perfect 210.5 0.5 +end 211.0 0.5 +to 211.5 0.5 +the 212.0 0.5 +day, 212.5 0.5 +Harry 213.0 0.5 +thought, 213.5 0.5 +as 214.0 0.5 +he 214.5 0.5 +lay 215.0 0.5 +awake 215.5 0.5 +much 216.0 0.5 +later 216.5 0.5 +listening 217.0 0.5 +to 217.5 0.5 +Dean 218.0 0.5 +and 218.5 0.5 +Seamus 219.0 0.5 +falling 219.5 0.5 +asleep 220.0 0.5 +(Neville 220.5 0.5 +wasn't 221.0 0.5 +back 221.5 0.5 +from 222.0 0.5 +the 222.5 0.5 +hospital 223.0 0.5 +wing). 223.5 0.5 +Ron 224.0 0.5 +had 224.5 0.5 +spent 225.0 0.5 +all 225.5 0.5 +evening 226.0 0.5 +giving 226.5 0.5 +him 227.0 0.5 +advice 227.5 0.5 +such 228.0 0.5 +as 228.5 0.5 +"""If" 229.0 0.5 +he 229.5 0.5 +tries 230.0 0.5 +to 230.5 0.5 +curse 231.0 0.5 +you, 231.5 0.5 +you'd 232.0 0.5 +better 232.5 0.5 +dodge 233.0 0.5 +it, 233.5 0.5 +because 234.0 0.5 +I 234.5 0.5 +can't 235.0 0.5 +remember 235.5 0.5 +how 236.0 0.5 +to 236.5 0.5 +block 237.0 0.5 +"them.""" 237.5 0.5 +There 238.0 0.5 +was 238.5 0.5 +a 239.0 0.5 +very 239.5 0.5 +good 240.0 0.5 +chance 240.5 0.5 +they 241.0 0.5 +were 241.5 0.5 +going 242.0 0.5 +to 242.5 0.5 +get 243.0 0.5 +caught 243.5 0.5 +by 244.0 0.5 +Filch 244.5 0.5 +or 245.0 0.5 +Mrs. 245.5 0.5 +Norris, 246.0 0.5 +and 246.5 0.5 +Harry 247.0 0.5 +felt 247.5 0.5 +he 248.0 0.5 +was 248.5 0.5 +pushing 249.0 0.5 +his 249.5 0.5 +luck, 250.0 0.5 +breaking 250.5 0.5 +another 251.0 0.5 +school 251.5 0.5 +rule 252.0 0.5 +today. 252.5 0.5 +On 253.0 0.5 +the 253.5 0.5 +other 254.0 0.5 +hand, 254.5 0.5 +Malfoy's 255.0 0.5 +sneering 255.5 0.5 +face 256.0 0.5 +kept 256.5 0.5 +looming 257.0 0.5 +up 257.5 0.5 +out 258.0 0.5 +of 258.5 0.5 +the 259.0 0.5 +darkness 259.5 0.5 +— 260.0 0.5 +this 260.5 0.5 +was 261.0 0.5 +his 261.5 0.5 +big 262.0 0.5 +chance 262.5 0.5 +to 263.0 0.5 +beat 263.5 0.5 +Malfoy 264.0 0.5 +face-to-face. 264.5 0.5 +He 265.0 0.5 +couldn't 265.5 0.5 +miss 266.0 0.5 +it. 266.5 0.5 ++ 267.0 0.5 +"""Half-past" 267.5 0.5 +"eleven,""" 268.0 0.5 +Ron 268.5 0.5 +muttered 269.0 0.5 +at 269.5 0.5 +last, 270.0 0.5 +"""we'd" 270.5 0.5 +better 271.0 0.5 +"go.""" 271.5 0.5 ++ 272.0 0.5 +They 272.5 0.5 +pulled 273.0 0.5 +on 273.5 0.5 +their 274.0 0.5 +bathrobes, 274.5 0.5 +picked 275.0 0.5 +up 275.5 0.5 +their 276.0 0.5 +wands, 276.5 0.5 +and 277.0 0.5 +crept 277.5 0.5 +across 278.0 0.5 +the 278.5 0.5 +tower 279.0 0.5 +room, 279.5 0.5 +down 280.0 0.5 +the 280.5 0.5 +spiral 281.0 0.5 +staircase, 281.5 0.5 +and 282.0 0.5 +into 282.5 0.5 +the 283.0 0.5 +Gryffindor 283.5 0.5 +common 284.0 0.5 +room. 284.5 0.5 +A 285.0 0.5 +few 285.5 0.5 +embers 286.0 0.5 +were 286.5 0.5 +still 287.0 0.5 +glowing 287.5 0.5 +in 288.0 0.5 +the 288.5 0.5 +fireplace, 289.0 0.5 +turning 289.5 0.5 +all 290.0 0.5 +the 290.5 0.5 +armchairs 291.0 0.5 +into 291.5 0.5 +hunched 292.0 0.5 +black 292.5 0.5 +shadows. 293.0 0.5 +They 293.5 0.5 +had 294.0 0.5 +almost 294.5 0.5 +reached 295.0 0.5 +the 295.5 0.5 +portrait 296.0 0.5 +hole 296.5 0.5 +when 297.0 0.5 +a 297.5 0.5 +voice 298.0 0.5 +spoke 298.5 0.5 +from 299.0 0.5 +the 299.5 0.5 +chair 300.0 0.5 +nearest 300.5 0.5 +them, 301.0 0.5 +"""I" 301.5 0.5 +can't 302.0 0.5 +believe 302.5 0.5 +you're 303.0 0.5 +going 303.5 0.5 +to 304.0 0.5 +do 304.5 0.5 +this, 305.0 0.5 +"Harry.""" 305.5 0.5 ++ 306.0 0.5 +A 306.5 0.5 +lamp 307.0 0.5 +flickered 307.5 0.5 +on. 308.0 0.5 +It 308.5 0.5 +was 309.0 0.5 +Hermione 309.5 0.5 +Granger, 310.0 0.5 +wearing 310.5 0.5 +a 311.0 0.5 +pink 311.5 0.5 +bathrobe 312.0 0.5 +and 312.5 0.5 +a 313.0 0.5 +frown. 313.5 0.5 ++ 314.0 0.5 +"""You!""" italic 314.5 0.5 +said 315.0 0.5 +Ron 315.5 0.5 +furiously. 316.0 0.5 +"""Go" 316.5 0.5 +back 317.0 0.5 +to 317.5 0.5 +"bed!""" 318.0 0.5 ++ 318.5 0.5 +"""I" 319.0 0.5 +almost 319.5 0.5 +told 320.0 0.5 +your 320.5 0.5 +"brother,""" 321.0 0.5 +Hermione 321.5 0.5 +snapped, 322.0 0.5 +"""Percy" 322.5 0.5 +— 323.0 0.5 +he's 323.5 0.5 +a 324.0 0.5 +prefect, 324.5 0.5 +he'd 325.0 0.5 +put 325.5 0.5 +a 326.0 0.5 +stop 326.5 0.5 +to 327.0 0.5 +"this.""" 327.5 0.5 ++ 328.0 0.5 +Harry 328.5 0.5 +couldn't 329.0 0.5 +believe 329.5 0.5 +anyone 330.0 0.5 +could 330.5 0.5 +be 331.0 0.5 +so 331.5 0.5 +interfering. 332.0 0.5 ++ 332.5 0.5 +"""Come" 333.0 0.5 +"on,""" 333.5 0.5 +he 334.0 0.5 +said 334.5 0.5 +to 335.0 0.5 +Ron. 335.5 0.5 +He 336.0 0.5 +pushed 336.5 0.5 +open 337.0 0.5 +the 337.5 0.5 +portrait 338.0 0.5 +of 338.5 0.5 +the 339.0 0.5 +Fat 339.5 0.5 +Lady 340.0 0.5 +and 340.5 0.5 +climbed 341.0 0.5 +through 341.5 0.5 +the 342.0 0.5 +hole. 342.5 0.5 ++ 343.0 0.5 +Hermione 343.5 0.5 +wasn't 344.0 0.5 +going 344.5 0.5 +to 345.0 0.5 +give 345.5 0.5 +up 346.0 0.5 +that 346.5 0.5 +easily. 347.0 0.5 +She 347.5 0.5 +followed 348.0 0.5 +Ron 348.5 0.5 +through 349.0 0.5 +the 349.5 0.5 +portrait 350.0 0.5 +hole, 350.5 0.5 +hissing 351.0 0.5 +at 351.5 0.5 +them 352.0 0.5 +like 352.5 0.5 +an 353.0 0.5 +angry 353.5 0.5 +goose. 354.0 0.5 +"""Don't" 354.5 0.5 +you 355.0 0.5 +care italic 355.5 0.5 +about 356.0 0.5 +Gryffindor, 356.5 0.5 +do 357.0 0.5 +you 357.5 0.5 +only italic 358.0 0.5 +care 358.5 0.5 +about 359.0 0.5 +yourselves, 359.5 0.5 +I 360.0 0.5 +don't 360.5 0.5 +want 361.0 0.5 +Slytherin 361.5 0.5 +to 362.0 0.5 +win 362.5 0.5 +the 363.0 0.5 +House 363.5 0.5 +Cup, 364.0 0.5 +and 364.5 0.5 +you'll 365.0 0.5 +lose 365.5 0.5 +all 366.0 0.5 +the 366.5 0.5 +points 367.0 0.5 +I 367.5 0.5 +got 368.0 0.5 +from 368.5 0.5 +Professor 369.0 0.5 +McGonagall 369.5 0.5 +for 370.0 0.5 +knowing 370.5 0.5 +about 371.0 0.5 +Switching 371.5 0.5 +"Spells.""" 372.0 0.5 ++ 372.5 0.5 +"""Go" 373.0 0.5 +"away.""" 373.5 0.5 ++ 374.0 0.5 +"""All" 374.5 0.5 +right, 375.0 0.5 +but 375.5 0.5 +I 376.0 0.5 +warned 376.5 0.5 +you, 377.0 0.5 +you 377.5 0.5 +just 378.0 0.5 +remember 378.5 0.5 +what 379.0 0.5 +I 379.5 0.5 +said 380.0 0.5 +when 380.5 0.5 +you're 381.0 0.5 +on 381.5 0.5 +the 382.0 0.5 +train 382.5 0.5 +home 383.0 0.5 +tomorrow, 383.5 0.5 +you're 384.0 0.5 +so 384.5 0.5 +"—""" 385.0 0.5 ++ 385.5 0.5 +But 386.0 0.5 +what 386.5 0.5 +they 387.0 0.5 +were, 387.5 0.5 +they 388.0 0.5 +didn't 388.5 0.5 +find 389.0 0.5 +out. 389.5 0.5 +Hermione 390.0 0.5 +had 390.5 0.5 +turned 391.0 0.5 +to 391.5 0.5 +the 392.0 0.5 +portrait 392.5 0.5 +of 393.0 0.5 +the 393.5 0.5 +Fat 394.0 0.5 +Lady 394.5 0.5 +to 395.0 0.5 +get 395.5 0.5 +back 396.0 0.5 +inside 396.5 0.5 +and 397.0 0.5 +found 397.5 0.5 +herself 398.0 0.5 +facing 398.5 0.5 +an 399.0 0.5 +empty 399.5 0.5 +painting. 400.0 0.5 +The 400.5 0.5 +Fat 401.0 0.5 +Lady 401.5 0.5 +had 402.0 0.5 +gone 402.5 0.5 +on 403.0 0.5 +a 403.5 0.5 +nighttime 404.0 0.5 +visit 404.5 0.5 +and 405.0 0.5 +Hermione 405.5 0.5 +was 406.0 0.5 +locked 406.5 0.5 +out 407.0 0.5 +of 407.5 0.5 +Gryffindor 408.0 0.5 +Tower. 408.5 0.5 ++ 409.0 0.5 +"""Now" 409.5 0.5 +what 410.0 0.5 +am 410.5 0.5 +I 411.0 0.5 +going 411.5 0.5 +to 412.0 0.5 +"do?""" 412.5 0.5 +she 413.0 0.5 +asked 413.5 0.5 +shrilly. 414.0 0.5 ++ 414.5 0.5 +"""That's" 415.0 0.5 +your 415.5 0.5 +"problem,""" 416.0 0.5 +said 416.5 0.5 +Ron. 417.0 0.5 +"""We've" 417.5 0.5 +got 418.0 0.5 +to 418.5 0.5 +go, 419.0 0.5 +we're 419.5 0.5 +going 420.0 0.5 +to 420.5 0.5 +be 421.0 0.5 +"late.""" 421.5 0.5 ++ 422.0 0.5 +They 422.5 0.5 +hadn't 423.0 0.5 +even 423.5 0.5 +reached 424.0 0.5 +the 424.5 0.5 +end 425.0 0.5 +of 425.5 0.5 +the 426.0 0.5 +corridor 426.5 0.5 +when 427.0 0.5 +Hermione 427.5 0.5 +caught 428.0 0.5 +up 428.5 0.5 +with 429.0 0.5 +them. 429.5 0.5 ++ 430.0 0.5 +"""I'm" 430.5 0.5 +coming 431.0 0.5 +with 431.5 0.5 +"you,""" 432.0 0.5 +she 432.5 0.5 +said. 433.0 0.5 +"""You" 433.5 0.5 +are 434.0 0.5 +"not.""" italic 434.5 0.5 ++ 435.0 0.5 +"""D'you" 435.5 0.5 +think 436.0 0.5 +I'm 436.5 0.5 +going 437.0 0.5 +to 437.5 0.5 +stand 438.0 0.5 +out 438.5 0.5 +here 439.0 0.5 +and 439.5 0.5 +wait 440.0 0.5 +for 440.5 0.5 +Filch 441.0 0.5 +to 441.5 0.5 +catch 442.0 0.5 +me? 442.5 0.5 +If 443.0 0.5 +he 443.5 0.5 +finds 444.0 0.5 +all 444.5 0.5 +three 445.0 0.5 +of 445.5 0.5 +us 446.0 0.5 +I'll 446.5 0.5 +tell 447.0 0.5 +him 447.5 0.5 +the 448.0 0.5 +truth, 448.5 0.5 +that 449.0 0.5 +I 449.5 0.5 +was 450.0 0.5 +trying 450.5 0.5 +to 451.0 0.5 +stop 451.5 0.5 +you, 452.0 0.5 +and 452.5 0.5 +you 453.0 0.5 +can 453.5 0.5 +back 454.0 0.5 +me 454.5 0.5 +"up.""" 455.0 0.5 ++ 455.5 0.5 +"""You've" 456.0 0.5 +got 456.5 0.5 +some 457.0 0.5 +nerve 457.5 0.5 +"—""" 458.0 0.5 +said 458.5 0.5 +Ron 459.0 0.5 +loudly. 459.5 0.5 ++ 460.0 0.5 +"""Shut" 460.5 0.5 +up, 461.0 0.5 +both 461.5 0.5 +of 462.0 0.5 +"you!""" 462.5 0.5 +said 463.0 0.5 +Harry 463.5 0.5 +sharply. 464.0 0.5 +"""I" 464.5 0.5 +heard 465.0 0.5 +"something.""" 465.5 0.5 ++ 466.0 0.5 +It 466.5 0.5 +was 467.0 0.5 +a 467.5 0.5 +sort 468.0 0.5 +of 468.5 0.5 +snuffling. 469.0 0.5 ++ 469.5 0.5 +"""Mrs." 470.0 0.5 +"Norris?""" 470.5 0.5 +breathed 471.0 0.5 +Ron, 471.5 0.5 +squinting 472.0 0.5 +through 472.5 0.5 +the 473.0 0.5 +dark. 473.5 0.5 ++ 474.0 0.5 +It 474.5 0.5 +wasn't 475.0 0.5 +Mrs. 475.5 0.5 +Norris. 476.0 0.5 +It 476.5 0.5 +was 477.0 0.5 +Neville. 477.5 0.5 +He 478.0 0.5 +was 478.5 0.5 +curled 479.0 0.5 +up 479.5 0.5 +on 480.0 0.5 +the 480.5 0.5 +floor, 481.0 0.5 +fast 481.5 0.5 +asleep, 482.0 0.5 +but 482.5 0.5 +jerked 483.0 0.5 +suddenly 483.5 0.5 +awake 484.0 0.5 +as 484.5 0.5 +they 485.0 0.5 +crept 485.5 0.5 +nearer. 486.0 0.5 ++ 486.5 0.5 +"""Thank" 487.0 0.5 +goodness 487.5 0.5 +you 488.0 0.5 +found 488.5 0.5 +me! 489.0 0.5 +I've 489.5 0.5 +been 490.0 0.5 +out 490.5 0.5 +here 491.0 0.5 +for 491.5 0.5 +hours, 492.0 0.5 +I 492.5 0.5 +couldn't 493.0 0.5 +remember 493.5 0.5 +the 494.0 0.5 +new 494.5 0.5 +password 495.0 0.5 +to 495.5 0.5 +get 496.0 0.5 +in 496.5 0.5 +to 497.0 0.5 +"bed.""" 497.5 0.5 ++ 498.0 0.5 +"""Keep" 498.5 0.5 +your 499.0 0.5 +voice 499.5 0.5 +down, 500.0 0.5 +Neville. 500.5 0.5 +The 501.0 0.5 +password's 501.5 0.5 +'Pig 502.0 0.5 +snout' 502.5 0.5 +but 503.0 0.5 +it 503.5 0.5 +won't 504.0 0.5 +help 504.5 0.5 +you 505.0 0.5 +now, 505.5 0.5 +the 506.0 0.5 +Fat 506.5 0.5 +Lady's 507.0 0.5 +gone 507.5 0.5 +off 508.0 0.5 +"somewhere.""" 508.5 0.5 ++ 509.0 0.5 +"""How's" 509.5 0.5 +your 510.0 0.5 +"arm?""" 510.5 0.5 +said 511.0 0.5 +Harry. 511.5 0.5 ++ 512.0 0.5 +"""Fine,""" 512.5 0.5 +said 513.0 0.5 +Neville, 513.5 0.5 +showing 514.0 0.5 +them. 514.5 0.5 +"""Madam" 515.0 0.5 +Pomfrey 515.5 0.5 +mended 516.0 0.5 +it 516.5 0.5 +in 517.0 0.5 +about 517.5 0.5 +a 518.0 0.5 +"minute.""" 518.5 0.5 ++ 519.0 0.5 +"""Good" 519.5 0.5 +— 520.0 0.5 +well, 520.5 0.5 +look, 521.0 0.5 +Neville, 521.5 0.5 +we've 522.0 0.5 +got 522.5 0.5 +to 523.0 0.5 +be 523.5 0.5 +somewhere, 524.0 0.5 +we'll 524.5 0.5 +see 525.0 0.5 +you 525.5 0.5 +later 526.0 0.5 +"—""" 526.5 0.5 ++ 527.0 0.5 +"""Don't" 527.5 0.5 +leave 528.0 0.5 +"me!""" 528.5 0.5 +said 529.0 0.5 +Neville, 529.5 0.5 +scrambling 530.0 0.5 +to 530.5 0.5 +his 531.0 0.5 +feet, 531.5 0.5 +"""I" 532.0 0.5 +don't 532.5 0.5 +want 533.0 0.5 +to 533.5 0.5 +stay 534.0 0.5 +here 534.5 0.5 +alone, 535.0 0.5 +the 535.5 0.5 +Bloody 536.0 0.5 +Baron's 536.5 0.5 +been 537.0 0.5 +past 537.5 0.5 +twice 538.0 0.5 +"already.""" 538.5 0.5 ++ 539.0 16 diff --git a/data/language/harrypotter/task-harry_run-4_events.tsv b/data/language/harrypotter/task-harry_run-4_events.tsv new file mode 100644 index 00000000..11b3b18b --- /dev/null +++ b/data/language/harrypotter/task-harry_run-4_events.tsv @@ -0,0 +1,1465 @@ +word format onset duration ++ 0 10 +Ron 10.0 0.5 +looked 10.5 0.5 +at 11.0 0.5 +his 11.5 0.5 +watch 12.0 0.5 +and 12.5 0.5 +then 13.0 0.5 +glared 13.5 0.5 +furiously 14.0 0.5 +at 14.5 0.5 +Hermione 15.0 0.5 +and 15.5 0.5 +Neville. 16.0 0.5 ++ 16.5 0.5 +"""If" 17.0 0.5 +either 17.5 0.5 +of 18.0 0.5 +you 18.5 0.5 +get 19.0 0.5 +us 19.5 0.5 +caught, 20.0 0.5 +I'll 20.5 0.5 +never 21.0 0.5 +rest 21.5 0.5 +until 22.0 0.5 +I've 22.5 0.5 +learned 23.0 0.5 +that 23.5 0.5 +Curse 24.0 0.5 +of 24.5 0.5 +the 25.0 0.5 +Bogies 25.5 0.5 +Quirrell 26.0 0.5 +told 26.5 0.5 +us 27.0 0.5 +about, 27.5 0.5 +and 28.0 0.5 +used 28.5 0.5 +it 29.0 0.5 +on 29.5 0.5 +"you.""" 30.0 0.5 ++ 30.5 0.5 +Hermione 31.0 0.5 +opened 31.5 0.5 +her 32.0 0.5 +mouth, 32.5 0.5 +perhaps 33.0 0.5 +to 33.5 0.5 +tell 34.0 0.5 +Ron 34.5 0.5 +exactly 35.0 0.5 +how 35.5 0.5 +to 36.0 0.5 +use 36.5 0.5 +the 37.0 0.5 +Curse 37.5 0.5 +of 38.0 0.5 +the 38.5 0.5 +Bogies, 39.0 0.5 +but 39.5 0.5 +Harry 40.0 0.5 +hissed 40.5 0.5 +at 41.0 0.5 +her 41.5 0.5 +to 42.0 0.5 +be 42.5 0.5 +quiet 43.0 0.5 +and 43.5 0.5 +beckoned 44.0 0.5 +them 44.5 0.5 +all 45.0 0.5 +forward. 45.5 0.5 ++ 46.0 0.5 +They 46.5 0.5 +flitted 47.0 0.5 +along 47.5 0.5 +corridors 48.0 0.5 +striped 48.5 0.5 +with 49.0 0.5 +bars 49.5 0.5 +of 50.0 0.5 +moonlight 50.5 0.5 +from 51.0 0.5 +the 51.5 0.5 +high 52.0 0.5 +windows. 52.5 0.5 +At 53.0 0.5 +every 53.5 0.5 +turn 54.0 0.5 +Harry 54.5 0.5 +expected 55.0 0.5 +to 55.5 0.5 +run 56.0 0.5 +into 56.5 0.5 +Filch 57.0 0.5 +or 57.5 0.5 +Mrs. 58.0 0.5 +Norris, 58.5 0.5 +but 59.0 0.5 +they 59.5 0.5 +were 60.0 0.5 +lucky. 60.5 0.5 +They 61.0 0.5 +sped 61.5 0.5 +up 62.0 0.5 +a 62.5 0.5 +staircase 63.0 0.5 +to 63.5 0.5 +the 64.0 0.5 +third 64.5 0.5 +floor 65.0 0.5 +and 65.5 0.5 +tiptoed 66.0 0.5 +toward 66.5 0.5 +the 67.0 0.5 +trophy 67.5 0.5 +room. 68.0 0.5 ++ 68.5 0.5 +Malfoy 69.0 0.5 +and 69.5 0.5 +Crabbe 70.0 0.5 +weren't 70.5 0.5 +there 71.0 0.5 +yet. 71.5 0.5 +The 72.0 0.5 +crystal 72.5 0.5 +trophy 73.0 0.5 +cases 73.5 0.5 +glimmered 74.0 0.5 +where 74.5 0.5 +the 75.0 0.5 +moonlight 75.5 0.5 +caught 76.0 0.5 +them. 76.5 0.5 +Cups, 77.0 0.5 +shields, 77.5 0.5 +plates, 78.0 0.5 +and 78.5 0.5 +statues 79.0 0.5 +winked 79.5 0.5 +silver 80.0 0.5 +and 80.5 0.5 +gold 81.0 0.5 +in 81.5 0.5 +the 82.0 0.5 +darkness. 82.5 0.5 +They 83.0 0.5 +edged 83.5 0.5 +along 84.0 0.5 +the 84.5 0.5 +walls, 85.0 0.5 +keeping 85.5 0.5 +their 86.0 0.5 +eyes 86.5 0.5 +on 87.0 0.5 +the 87.5 0.5 +doors 88.0 0.5 +at 88.5 0.5 +either 89.0 0.5 +end 89.5 0.5 +of 90.0 0.5 +the 90.5 0.5 +room. 91.0 0.5 +Harry 91.5 0.5 +took 92.0 0.5 +out 92.5 0.5 +his 93.0 0.5 +wand 93.5 0.5 +in 94.0 0.5 +case 94.5 0.5 +Malfoy 95.0 0.5 +leapt 95.5 0.5 +in 96.0 0.5 +and 96.5 0.5 +started 97.0 0.5 +at 97.5 0.5 +once. 98.0 0.5 +The 98.5 0.5 +minutes 99.0 0.5 +crept 99.5 0.5 +by. 100.0 0.5 ++ 100.5 0.5 +"""He's" 101.0 0.5 +late, 101.5 0.5 +maybe 102.0 0.5 +he's 102.5 0.5 +chickened 103.0 0.5 +"out,""" 103.5 0.5 +Ron 104.0 0.5 +whispered. 104.5 0.5 ++ 105.0 0.5 +Then 105.5 0.5 +a 106.0 0.5 +noise 106.5 0.5 +in 107.0 0.5 +the 107.5 0.5 +next 108.0 0.5 +room 108.5 0.5 +made 109.0 0.5 +them 109.5 0.5 +jump. 110.0 0.5 +Harry 110.5 0.5 +had 111.0 0.5 +only 111.5 0.5 +just 112.0 0.5 +raised 112.5 0.5 +his 113.0 0.5 +wand 113.5 0.5 +when 114.0 0.5 +they 114.5 0.5 +heard 115.0 0.5 +someone 115.5 0.5 +speak 116.0 0.5 +— 116.5 0.5 +and 117.0 0.5 +it 117.5 0.5 +wasn't 118.0 0.5 +Malfoy. 118.5 0.5 ++ 119.0 0.5 +"""Sniff" 119.5 0.5 +around, 120.0 0.5 +my 120.5 0.5 +sweet, 121.0 0.5 +they 121.5 0.5 +might 122.0 0.5 +be 122.5 0.5 +lurking 123.0 0.5 +in 123.5 0.5 +a 124.0 0.5 +"corner.""" 124.5 0.5 ++ 125.0 0.5 +It 125.5 0.5 +was 126.0 0.5 +Filch 126.5 0.5 +speaking 127.0 0.5 +to 127.5 0.5 +Mrs. 128.0 0.5 +Norris. 128.5 0.5 +Horror-struck, 129.0 0.5 +Harry 129.5 0.5 +waved 130.0 0.5 +madly 130.5 0.5 +at 131.0 0.5 +the 131.5 0.5 +other 132.0 0.5 +three 132.5 0.5 +to 133.0 0.5 +follow 133.5 0.5 +him 134.0 0.5 +as 134.5 0.5 +quickly 135.0 0.5 +as 135.5 0.5 +possible; 136.0 0.5 +they 136.5 0.5 +scurried 137.0 0.5 +silently 137.5 0.5 +toward 138.0 0.5 +the 138.5 0.5 +door, 139.0 0.5 +away 139.5 0.5 +from 140.0 0.5 +Filch's 140.5 0.5 +voice. 141.0 0.5 +Neville's 141.5 0.5 +robes 142.0 0.5 +had 142.5 0.5 +barely 143.0 0.5 +whipped 143.5 0.5 +round 144.0 0.5 +the 144.5 0.5 +corner 145.0 0.5 +when 145.5 0.5 +they 146.0 0.5 +heard 146.5 0.5 +Filch 147.0 0.5 +enter 147.5 0.5 +the 148.0 0.5 +trophy 148.5 0.5 +room. 149.0 0.5 ++ 149.5 0.5 +"""They're" 150.0 0.5 +in 150.5 0.5 +here 151.0 0.5 +"somewhere,""" 151.5 0.5 +they 152.0 0.5 +heard 152.5 0.5 +him 153.0 0.5 +mutter, 153.5 0.5 +"""probably" 154.0 0.5 +"hiding.""" 154.5 0.5 ++ 155.0 0.5 +"""This" 155.5 0.5 +"way!""" 156.0 0.5 +Harry 156.5 0.5 +mouthed 157.0 0.5 +to 157.5 0.5 +the 158.0 0.5 +others 158.5 0.5 +and, 159.0 0.5 +petrified, 159.5 0.5 +they 160.0 0.5 +began 160.5 0.5 +to 161.0 0.5 +creep 161.5 0.5 +down 162.0 0.5 +a 162.5 0.5 +long 163.0 0.5 +gallery 163.5 0.5 +full 164.0 0.5 +of 164.5 0.5 +suits 165.0 0.5 +of 165.5 0.5 +armor. 166.0 0.5 ++ 166.5 0.5 +They 167.0 0.5 +could 167.5 0.5 +hear 168.0 0.5 +Filch 168.5 0.5 +getting 169.0 0.5 +nearer. 169.5 0.5 +Neville 170.0 0.5 +suddenly 170.5 0.5 +let 171.0 0.5 +out 171.5 0.5 +a 172.0 0.5 +frightened 172.5 0.5 +squeak 173.0 0.5 +and 173.5 0.5 +broke 174.0 0.5 +into 174.5 0.5 +a 175.0 0.5 +run 175.5 0.5 +— 176.0 0.5 +he 176.5 0.5 +tripped, 177.0 0.5 +grabbed 177.5 0.5 +Ron 178.0 0.5 +around 178.5 0.5 +the 179.0 0.5 +waist, 179.5 0.5 +and 180.0 0.5 +the 180.5 0.5 +pair 181.0 0.5 +of 181.5 0.5 +them 182.0 0.5 +toppled 182.5 0.5 +right 183.0 0.5 +into 183.5 0.5 +a 184.0 0.5 +suit 184.5 0.5 +of 185.0 0.5 +armor. 185.5 0.5 ++ 186.0 0.5 +The 186.5 0.5 +clanging 187.0 0.5 +and 187.5 0.5 +crashing 188.0 0.5 +were 188.5 0.5 +enough 189.0 0.5 +to 189.5 0.5 +wake 190.0 0.5 +the 190.5 0.5 +whole 191.0 0.5 +castle. 191.5 0.5 ++ 192.0 0.5 +"""RUN!""" 192.5 0.5 +Harry 193.0 0.5 +yelled, 193.5 0.5 +and 194.0 0.5 +the 194.5 0.5 +four 195.0 0.5 +of 195.5 0.5 +them 196.0 0.5 +sprinted 196.5 0.5 +down 197.0 0.5 +the 197.5 0.5 +gallery, 198.0 0.5 +not 198.5 0.5 +looking 199.0 0.5 +back 199.5 0.5 +to 200.0 0.5 +see 200.5 0.5 +whether 201.0 0.5 +Filch 201.5 0.5 +was 202.0 0.5 +following 202.5 0.5 +— 203.0 0.5 +they 203.5 0.5 +swung 204.0 0.5 +around 204.5 0.5 +the 205.0 0.5 +doorpost 205.5 0.5 +and 206.0 0.5 +galloped 206.5 0.5 +down 207.0 0.5 +one 207.5 0.5 +corridor 208.0 0.5 +then 208.5 0.5 +another, 209.0 0.5 +Harry 209.5 0.5 +in 210.0 0.5 +the 210.5 0.5 +lead, 211.0 0.5 +without 211.5 0.5 +any 212.0 0.5 +idea 212.5 0.5 +where 213.0 0.5 +they 213.5 0.5 +were 214.0 0.5 +or 214.5 0.5 +where 215.0 0.5 +they 215.5 0.5 +were 216.0 0.5 +going 216.5 0.5 +— 217.0 0.5 +they 217.5 0.5 +ripped 218.0 0.5 +through 218.5 0.5 +a 219.0 0.5 +tapestry 219.5 0.5 +and 220.0 0.5 +found 220.5 0.5 +themselves 221.0 0.5 +in 221.5 0.5 +a 222.0 0.5 +hidden 222.5 0.5 +passageway, 223.0 0.5 +hurtled 223.5 0.5 +along 224.0 0.5 +it 224.5 0.5 +and 225.0 0.5 +came 225.5 0.5 +out 226.0 0.5 +near 226.5 0.5 +their 227.0 0.5 +Charms 227.5 0.5 +classroom, 228.0 0.5 +which 228.5 0.5 +they 229.0 0.5 +knew 229.5 0.5 +was 230.0 0.5 +miles 230.5 0.5 +from 231.0 0.5 +the 231.5 0.5 +trophy 232.0 0.5 +room. 232.5 0.5 ++ 233.0 0.5 +"""I" 233.5 0.5 +think 234.0 0.5 +we've 234.5 0.5 +lost 235.0 0.5 +"him,""" 235.5 0.5 +Harry 236.0 0.5 +panted, 236.5 0.5 +leaning 237.0 0.5 +against 237.5 0.5 +the 238.0 0.5 +cold 238.5 0.5 +wall 239.0 0.5 +and 239.5 0.5 +wiping 240.0 0.5 +his 240.5 0.5 +forehead. 241.0 0.5 +Neville 241.5 0.5 +was 242.0 0.5 +bent 242.5 0.5 +double, 243.0 0.5 +wheezing 243.5 0.5 +and 244.0 0.5 +spluttering. 244.5 0.5 ++ 245.0 0.5 +"""I" 245.5 0.5 +— 246.0 0.5 +told italic 246.5 0.5 +— 247.0 0.5 +"you,""" 247.5 0.5 +Hermione 248.0 0.5 +gasped, 248.5 0.5 +clutching 249.0 0.5 +at 249.5 0.5 +the 250.0 0.5 +stitch 250.5 0.5 +in 251.0 0.5 +her 251.5 0.5 +chest, 252.0 0.5 +"""I" 252.5 0.5 +— 253.0 0.5 +told 253.5 0.5 +— 254.0 0.5 +"you.""" 254.5 0.5 ++ 255.0 0.5 +"""We've" 255.5 0.5 +got 256.0 0.5 +to 256.5 0.5 +get 257.0 0.5 +back 257.5 0.5 +to 258.0 0.5 +Gryffindor 258.5 0.5 +"Tower,""" 259.0 0.5 +said 259.5 0.5 +Ron, 260.0 0.5 +"""quickly" 260.5 0.5 +as 261.0 0.5 +"possible.""" 261.5 0.5 ++ 262.0 0.5 +"""Malfoy" 262.5 0.5 +tricked 263.0 0.5 +"you,""" 263.5 0.5 +Hermione 264.0 0.5 +said 264.5 0.5 +to 265.0 0.5 +Harry. 265.5 0.5 +"""You" 266.0 0.5 +realize 266.5 0.5 +that, 267.0 0.5 +don't 267.5 0.5 +you? 268.0 0.5 +He 268.5 0.5 +was 269.0 0.5 +never 269.5 0.5 +going 270.0 0.5 +to 270.5 0.5 +meet 271.0 0.5 +you 271.5 0.5 +— 272.0 0.5 +Filch 272.5 0.5 +knew 273.0 0.5 +someone 273.5 0.5 +was 274.0 0.5 +going 274.5 0.5 +to 275.0 0.5 +be 275.5 0.5 +in 276.0 0.5 +the 276.5 0.5 +trophy 277.0 0.5 +room, 277.5 0.5 +Malfoy 278.0 0.5 +must 278.5 0.5 +have 279.0 0.5 +tipped 279.5 0.5 +him 280.0 0.5 +"off.""" 280.5 0.5 ++ 281.0 0.5 +Harry 281.5 0.5 +thought 282.0 0.5 +she 282.5 0.5 +was 283.0 0.5 +probably 283.5 0.5 +right, 284.0 0.5 +but 284.5 0.5 +he 285.0 0.5 +wasn't 285.5 0.5 +going 286.0 0.5 +to 286.5 0.5 +tell 287.0 0.5 +her 287.5 0.5 +that. 288.0 0.5 ++ 288.5 0.5 +"""Let's" 289.0 0.5 +"go.""" 289.5 0.5 ++ 290.0 0.5 +It 290.5 0.5 +wasn't 291.0 0.5 +going 291.5 0.5 +to 292.0 0.5 +be 292.5 0.5 +that 293.0 0.5 +simple. 293.5 0.5 +They 294.0 0.5 +hadn't 294.5 0.5 +gone 295.0 0.5 +more 295.5 0.5 +than 296.0 0.5 +a 296.5 0.5 +dozen 297.0 0.5 +paces 297.5 0.5 +when 298.0 0.5 +a 298.5 0.5 +doorknob 299.0 0.5 +rattled 299.5 0.5 +and 300.0 0.5 +something 300.5 0.5 +came 301.0 0.5 +shooting 301.5 0.5 +out 302.0 0.5 +of 302.5 0.5 +a 303.0 0.5 +classroom 303.5 0.5 +in 304.0 0.5 +front 304.5 0.5 +of 305.0 0.5 +them. 305.5 0.5 ++ 306.0 0.5 +It 306.5 0.5 +was 307.0 0.5 +Peeves. 307.5 0.5 +He 308.0 0.5 +caught 308.5 0.5 +sight 309.0 0.5 +of 309.5 0.5 +them 310.0 0.5 +and 310.5 0.5 +gave 311.0 0.5 +a 311.5 0.5 +squeal 312.0 0.5 +of 312.5 0.5 +delight. 313.0 0.5 ++ 313.5 0.5 +"""Shut" 314.0 0.5 +up, 314.5 0.5 +Peeves 315.0 0.5 +— 315.5 0.5 +please 316.0 0.5 +— 316.5 0.5 +you'll 317.0 0.5 +get 317.5 0.5 +us 318.0 0.5 +thrown 318.5 0.5 +"out.""" 319.0 0.5 ++ 319.5 0.5 +Peeves 320.0 0.5 +cackled. 320.5 0.5 ++ 321.0 0.5 +"""Wandering" 321.5 0.5 +around 322.0 0.5 +at 322.5 0.5 +midnight, 323.0 0.5 +Ickle 323.5 0.5 +Firsties? 324.0 0.5 +Tut, 324.5 0.5 +tut, 325.0 0.5 +tut. 325.5 0.5 +Naughty, 326.0 0.5 +naughty, 326.5 0.5 +you'll 327.0 0.5 +get 327.5 0.5 +"caughty.""" 328.0 0.5 ++ 328.5 0.5 +"""Not" 329.0 0.5 +if 329.5 0.5 +you 330.0 0.5 +don't 330.5 0.5 +give 331.0 0.5 +us 331.5 0.5 +away, 332.0 0.5 +Peeves, 332.5 0.5 +"please.""" 333.0 0.5 ++ 333.5 0.5 +"""Should" 334.0 0.5 +tell 334.5 0.5 +Filch, 335.0 0.5 +I 335.5 0.5 +"should,""" 336.0 0.5 +said 336.5 0.5 +Peeves 337.0 0.5 +in 337.5 0.5 +a 338.0 0.5 +saintly 338.5 0.5 +voice, 339.0 0.5 +but 339.5 0.5 +his 340.0 0.5 +eyes 340.5 0.5 +glittered 341.0 0.5 +wickedly. 341.5 0.5 +"""It's" 342.0 0.5 +for 342.5 0.5 +your 343.0 0.5 +own 343.5 0.5 +good, 344.0 0.5 +you 344.5 0.5 +"know.""" 345.0 0.5 ++ 345.5 0.5 +"""Get" 346.0 0.5 +out 346.5 0.5 +of 347.0 0.5 +the 347.5 0.5 +"way,""" 348.0 0.5 +snapped 348.5 0.5 +Ron, 349.0 0.5 +taking 349.5 0.5 +a 350.0 0.5 +swipe 350.5 0.5 +at 351.0 0.5 +Peeves 351.5 0.5 +— 352.0 0.5 +this 352.5 0.5 +was 353.0 0.5 +a 353.5 0.5 +big 354.0 0.5 +mistake. 354.5 0.5 ++ 355.0 0.5 +"""STUDENTS" 355.5 0.5 +OUT 356.0 0.5 +OF 356.5 0.5 +"BED!""" 357.0 0.5 +Peeves 357.5 0.5 +bellowed, 358.0 0.5 +"""STUDENTS" 358.5 0.5 +OUT 359.0 0.5 +OF 359.5 0.5 +BED 360.0 0.5 +DOWN 360.5 0.5 +THE 361.0 0.5 +CHARMS 361.5 0.5 +"CORRIDOR!""" 362.0 0.5 ++ 362.5 0.5 +Ducking 363.0 0.5 +under 363.5 0.5 +Peeves, 364.0 0.5 +they 364.5 0.5 +ran 365.0 0.5 +for 365.5 0.5 +their 366.0 0.5 +lives, 366.5 0.5 +right 367.0 0.5 +to 367.5 0.5 +the 368.0 0.5 +end 368.5 0.5 +of 369.0 0.5 +the 369.5 0.5 +corridor 370.0 0.5 +where 370.5 0.5 +they 371.0 0.5 +slammed 371.5 0.5 +into 372.0 0.5 +a 372.5 0.5 +door 373.0 0.5 +— 373.5 0.5 +and 374.0 0.5 +it 374.5 0.5 +was 375.0 0.5 +locked. 375.5 0.5 ++ 376.0 0.5 +"""This" 376.5 0.5 +is 377.0 0.5 +"it!""" 377.5 0.5 +Ron 378.0 0.5 +moaned, 378.5 0.5 +as 379.0 0.5 +they 379.5 0.5 +pushed 380.0 0.5 +helplessly 380.5 0.5 +at 381.0 0.5 +the 381.5 0.5 +door, 382.0 0.5 +"""We're" 382.5 0.5 +done 383.0 0.5 +for! 383.5 0.5 +This 384.0 0.5 +is 384.5 0.5 +the 385.0 0.5 +"end!""" 385.5 0.5 ++ 386.0 0.5 +They 386.5 0.5 +could 387.0 0.5 +hear 387.5 0.5 +footsteps, 388.0 0.5 +Filch 388.5 0.5 +running 389.0 0.5 +as 389.5 0.5 +fast 390.0 0.5 +as 390.5 0.5 +he 391.0 0.5 +could 391.5 0.5 +toward 392.0 0.5 +Peeves's 392.5 0.5 +shouts. 393.0 0.5 ++ 393.5 0.5 +"""Oh," 394.0 0.5 +move 394.5 0.5 +"over,""" 395.0 0.5 +Hermione 395.5 0.5 +snarled. 396.0 0.5 +She 396.5 0.5 +grabbed 397.0 0.5 +Harry's 397.5 0.5 +wand, 398.0 0.5 +tapped 398.5 0.5 +the 399.0 0.5 +lock, 399.5 0.5 +and 400.0 0.5 +whispered, 400.5 0.5 +"""Alohomora!""" italic 401.0 0.5 ++ 401.5 0.5 +The 402.0 0.5 +lock 402.5 0.5 +clicked 403.0 0.5 +and 403.5 0.5 +the 404.0 0.5 +door 404.5 0.5 +swung 405.0 0.5 +open 405.5 0.5 +— 406.0 0.5 +they 406.5 0.5 +piled 407.0 0.5 +through 407.5 0.5 +it, 408.0 0.5 +shut 408.5 0.5 +it 409.0 0.5 +quickly, 409.5 0.5 +and 410.0 0.5 +pressed 410.5 0.5 +their 411.0 0.5 +ears 411.5 0.5 +against 412.0 0.5 +it, 412.5 0.5 +listening. 413.0 0.5 ++ 413.5 0.5 +"""Which" 414.0 0.5 +way 414.5 0.5 +did 415.0 0.5 +they 415.5 0.5 +go, 416.0 0.5 +"Peeves?""" 416.5 0.5 +Filch 417.0 0.5 +was 417.5 0.5 +saying. 418.0 0.5 +"""Quick," 418.5 0.5 +tell 419.0 0.5 +"me.""" 419.5 0.5 +"""Say" 420.0 0.5 +"'please.'""" 420.5 0.5 ++ 421.0 0.5 +"""Don't" 421.5 0.5 +mess 422.0 0.5 +with 422.5 0.5 +me, 423.0 0.5 +Peeves, 423.5 0.5 +now 424.0 0.5 +where italic 424.5 0.5 +did italic 425.0 0.5 +they italic 425.5 0.5 +"go?""" italic 426.0 0.5 +"""Shan't" 426.5 0.5 +say 427.0 0.5 +nothing 427.5 0.5 +if 428.0 0.5 +you 428.5 0.5 +don't 429.0 0.5 +say 429.5 0.5 +"please,""" 430.0 0.5 +said 430.5 0.5 +Peeves 431.0 0.5 +in 431.5 0.5 +his 432.0 0.5 +annoying 432.5 0.5 +singsong 433.0 0.5 +voice. 433.5 0.5 +"""All" 434.0 0.5 +right 434.5 0.5 +— 435.0 0.5 +"please.""" italic 435.5 0.5 ++ 436.0 0.5 +"""NOTHING!" 436.5 0.5 +Ha 437.0 0.5 +haaa! 437.5 0.5 +Told 438.0 0.5 +you 438.5 0.5 +I 439.0 0.5 +wouldn't 439.5 0.5 +say 440.0 0.5 +nothing 440.5 0.5 +if 441.0 0.5 +you 441.5 0.5 +didn't 442.0 0.5 +say 442.5 0.5 +please! 443.0 0.5 +Ha 443.5 0.5 +ha! 444.0 0.5 +"Haaaaaa!""" 444.5 0.5 +And 445.0 0.5 +they 445.5 0.5 +heard 446.0 0.5 +the 446.5 0.5 +sound 447.0 0.5 +of 447.5 0.5 +Peeves 448.0 0.5 +whooshing 448.5 0.5 +away 449.0 0.5 +and 449.5 0.5 +Filch 450.0 0.5 +cursing 450.5 0.5 +in 451.0 0.5 +rage. 451.5 0.5 ++ 452.0 0.5 +"""He" 452.5 0.5 +thinks 453.0 0.5 +this 453.5 0.5 +door 454.0 0.5 +is 454.5 0.5 +"locked,""" 455.0 0.5 +Harry 455.5 0.5 +whispered. 456.0 0.5 +"""I" 456.5 0.5 +think 457.0 0.5 +we'll 457.5 0.5 +be 458.0 0.5 +okay 458.5 0.5 +— 459.0 0.5 +get 459.5 0.5 +off, italic 460.0 0.5 +"Neville!""" 460.5 0.5 +For 461.0 0.5 +Neville 461.5 0.5 +had 462.0 0.5 +been 462.5 0.5 +tugging 463.0 0.5 +on 463.5 0.5 +the 464.0 0.5 +sleeve 464.5 0.5 +of 465.0 0.5 +Harry's 465.5 0.5 +bathrobe 466.0 0.5 +for 466.5 0.5 +the 467.0 0.5 +last 467.5 0.5 +minute. 468.0 0.5 +"""What?""" italic 468.5 0.5 ++ 469.0 0.5 +Harry 469.5 0.5 +turned 470.0 0.5 +around 470.5 0.5 +— 471.0 0.5 +and 471.5 0.5 +saw, 472.0 0.5 +quite 472.5 0.5 +clearly, 473.0 0.5 +what. 473.5 0.5 +For 474.0 0.5 +a 474.5 0.5 +moment, 475.0 0.5 +he 475.5 0.5 +was 476.0 0.5 +sure 476.5 0.5 +he'd 477.0 0.5 +walked 477.5 0.5 +into 478.0 0.5 +a 478.5 0.5 +nightmare 479.0 0.5 +— 479.5 0.5 +this 480.0 0.5 +was 480.5 0.5 +too 481.0 0.5 +much, 481.5 0.5 +on 482.0 0.5 +top 482.5 0.5 +of 483.0 0.5 +everything 483.5 0.5 +that 484.0 0.5 +had 484.5 0.5 +happened 485.0 0.5 +so 485.5 0.5 +far. 486.0 0.5 ++ 486.5 0.5 +They 487.0 0.5 +weren't 487.5 0.5 +in 488.0 0.5 +a 488.5 0.5 +room, 489.0 0.5 +as 489.5 0.5 +he 490.0 0.5 +had 490.5 0.5 +supposed. 491.0 0.5 +They 491.5 0.5 +were 492.0 0.5 +in 492.5 0.5 +a 493.0 0.5 +corridor. 493.5 0.5 +The 494.0 0.5 +forbidden 494.5 0.5 +corridor 495.0 0.5 +on 495.5 0.5 +the 496.0 0.5 +third 496.5 0.5 +floor. 497.0 0.5 +And 497.5 0.5 +now 498.0 0.5 +they 498.5 0.5 +knew 499.0 0.5 +why 499.5 0.5 +it 500.0 0.5 +was 500.5 0.5 +forbidden. 501.0 0.5 ++ 501.5 0.5 +They 502.0 0.5 +were 502.5 0.5 +looking 503.0 0.5 +straight 503.5 0.5 +into 504.0 0.5 +the 504.5 0.5 +eyes 505.0 0.5 +of 505.5 0.5 +a 506.0 0.5 +monstrous 506.5 0.5 +dog, 507.0 0.5 +a 507.5 0.5 +dog 508.0 0.5 +that 508.5 0.5 +filled 509.0 0.5 +the 509.5 0.5 +whole 510.0 0.5 +space 510.5 0.5 +between 511.0 0.5 +ceiling 511.5 0.5 +and 512.0 0.5 +floor. 512.5 0.5 +It 513.0 0.5 +had 513.5 0.5 +three 514.0 0.5 +heads. 514.5 0.5 +Three 515.0 0.5 +pairs 515.5 0.5 +of 516.0 0.5 +rolling, 516.5 0.5 +mad 517.0 0.5 +eyes; 517.5 0.5 +three 518.0 0.5 +noses, 518.5 0.5 +twitching 519.0 0.5 +and 519.5 0.5 +quivering 520.0 0.5 +in 520.5 0.5 +their 521.0 0.5 +direction; 521.5 0.5 +three 522.0 0.5 +drooling 522.5 0.5 +mouths, 523.0 0.5 +saliva 523.5 0.5 +hanging 524.0 0.5 +in 524.5 0.5 +slippery 525.0 0.5 +ropes 525.5 0.5 +from 526.0 0.5 +yellowish 526.5 0.5 +fangs. 527.0 0.5 ++ 527.5 0.5 +It 528.0 0.5 +was 528.5 0.5 +standing 529.0 0.5 +quite 529.5 0.5 +still, 530.0 0.5 +all 530.5 0.5 +six 531.0 0.5 +eyes 531.5 0.5 +staring 532.0 0.5 +at 532.5 0.5 +them, 533.0 0.5 +and 533.5 0.5 +Harry 534.0 0.5 +knew 534.5 0.5 +that 535.0 0.5 +the 535.5 0.5 +only 536.0 0.5 +reason 536.5 0.5 +they 537.0 0.5 +weren't 537.5 0.5 +already 538.0 0.5 +dead 538.5 0.5 +was 539.0 0.5 +that 539.5 0.5 +their 540.0 0.5 +sudden 540.5 0.5 +appearance 541.0 0.5 +had 541.5 0.5 +taken 542.0 0.5 +it 542.5 0.5 +by 543.0 0.5 +surprise, 543.5 0.5 +but 544.0 0.5 +it 544.5 0.5 +was 545.0 0.5 +quickly 545.5 0.5 +getting 546.0 0.5 +over 546.5 0.5 +that, 547.0 0.5 +there 547.5 0.5 +was 548.0 0.5 +no 548.5 0.5 +mistaking 549.0 0.5 +what 549.5 0.5 +those 550.0 0.5 +thunderous 550.5 0.5 +growls 551.0 0.5 +meant. 551.5 0.5 ++ 552.0 0.5 +Harry 552.5 0.5 +groped 553.0 0.5 +for 553.5 0.5 +the 554.0 0.5 +doorknob 554.5 0.5 +— 555.0 0.5 +between 555.5 0.5 +Filch 556.0 0.5 +and 556.5 0.5 +death, 557.0 0.5 +he'd 557.5 0.5 +take 558.0 0.5 +Filch. 558.5 0.5 ++ 559.0 0.5 +They 559.5 0.5 +fell 560.0 0.5 +backward 560.5 0.5 +— 561.0 0.5 +Harry 561.5 0.5 +slammed 562.0 0.5 +the 562.5 0.5 +door 563.0 0.5 +shut, 563.5 0.5 +and 564.0 0.5 +they 564.5 0.5 +ran, 565.0 0.5 +they 565.5 0.5 +almost 566.0 0.5 +flew, 566.5 0.5 +back 567.0 0.5 +down 567.5 0.5 +the 568.0 0.5 +corridor. 568.5 0.5 +Filch 569.0 0.5 +must 569.5 0.5 +have 570.0 0.5 +hurried 570.5 0.5 +off 571.0 0.5 +to 571.5 0.5 +look 572.0 0.5 +for 572.5 0.5 +them 573.0 0.5 +somewhere 573.5 0.5 +else, 574.0 0.5 +because 574.5 0.5 +they 575.0 0.5 +didn't 575.5 0.5 +see 576.0 0.5 +him 576.5 0.5 +anywhere, 577.0 0.5 +but 577.5 0.5 +they 578.0 0.5 +hardly 578.5 0.5 +cared 579.0 0.5 +— 579.5 0.5 +all 580.0 0.5 +they 580.5 0.5 +wanted 581.0 0.5 +to 581.5 0.5 +do 582.0 0.5 +was 582.5 0.5 +put 583.0 0.5 +as 583.5 0.5 +much 584.0 0.5 +space 584.5 0.5 +as 585.0 0.5 +possible 585.5 0.5 +between 586.0 0.5 +them 586.5 0.5 +and 587.0 0.5 +that 587.5 0.5 +monster. 588.0 0.5 +They 588.5 0.5 +didn't 589.0 0.5 +stop 589.5 0.5 +running 590.0 0.5 +until 590.5 0.5 +they 591.0 0.5 +reached 591.5 0.5 +the 592.0 0.5 +portrait 592.5 0.5 +of 593.0 0.5 +the 593.5 0.5 +Fat 594.0 0.5 +Lady 594.5 0.5 +on 595.0 0.5 +the 595.5 0.5 +seventh 596.0 0.5 +floor. 596.5 0.5 ++ 597.0 0.5 +"""Where" 597.5 0.5 +on 598.0 0.5 +earth 598.5 0.5 +have 599.0 0.5 +you 599.5 0.5 +all 600.0 0.5 +"been?""" 600.5 0.5 +she 601.0 0.5 +asked, 601.5 0.5 +looking 602.0 0.5 +at 602.5 0.5 +their 603.0 0.5 +bathrobes 603.5 0.5 +hanging 604.0 0.5 +off 604.5 0.5 +their 605.0 0.5 +shoulders 605.5 0.5 +and 606.0 0.5 +their 606.5 0.5 +flushed, 607.0 0.5 +sweaty 607.5 0.5 +faces. 608.0 0.5 ++ 608.5 0.5 +"""Never" 609.0 0.5 +mind 609.5 0.5 +that 610.0 0.5 +— 610.5 0.5 +pig 611.0 0.5 +snout, 611.5 0.5 +pig 612.0 0.5 +"snout,""" 612.5 0.5 +panted 613.0 0.5 +Harry, 613.5 0.5 +and 614.0 0.5 +the 614.5 0.5 +portrait 615.0 0.5 +swung 615.5 0.5 +forward. 616.0 0.5 +They 616.5 0.5 +scrambled 617.0 0.5 +into 617.5 0.5 +the 618.0 0.5 +common 618.5 0.5 +room 619.0 0.5 +and 619.5 0.5 +collapsed, 620.0 0.5 +trembling, 620.5 0.5 +into 621.0 0.5 +armchairs. 621.5 0.5 ++ 622.0 0.5 +It 622.5 0.5 +was 623.0 0.5 +a 623.5 0.5 +while 624.0 0.5 +before 624.5 0.5 +any 625.0 0.5 +of 625.5 0.5 +them 626.0 0.5 +said 626.5 0.5 +anything. 627.0 0.5 +Neville, 627.5 0.5 +indeed, 628.0 0.5 +looked 628.5 0.5 +as 629.0 0.5 +if 629.5 0.5 +he'd 630.0 0.5 +never 630.5 0.5 +speak 631.0 0.5 +again. 631.5 0.5 ++ 632.0 0.5 +"""What" 632.5 0.5 +do 633.0 0.5 +they 633.5 0.5 +think 634.0 0.5 +they're 634.5 0.5 +doing, 635.0 0.5 +keeping 635.5 0.5 +a 636.0 0.5 +thing 636.5 0.5 +like 637.0 0.5 +that 637.5 0.5 +locked 638.0 0.5 +up 638.5 0.5 +in 639.0 0.5 +a 639.5 0.5 +"school?""" 640.0 0.5 +said 640.5 0.5 +Ron 641.0 0.5 +finally. 641.5 0.5 +"""If" 642.0 0.5 +any 642.5 0.5 +dog 643.0 0.5 +needs 643.5 0.5 +exercise, 644.0 0.5 +that 644.5 0.5 +one 645.0 0.5 +"does.""" 645.5 0.5 ++ 646.0 0.5 +Hermione 646.5 0.5 +had 647.0 0.5 +got 647.5 0.5 +both 648.0 0.5 +her 648.5 0.5 +breath 649.0 0.5 +and 649.5 0.5 +her 650.0 0.5 +bad 650.5 0.5 +temper 651.0 0.5 +back 651.5 0.5 +again. 652.0 0.5 ++ 652.5 0.5 +"""You" 653.0 0.5 +don't 653.5 0.5 +use 654.0 0.5 +your 654.5 0.5 +eyes, 655.0 0.5 +any 655.5 0.5 +of 656.0 0.5 +you, 656.5 0.5 +do 657.0 0.5 +"you?""" 657.5 0.5 +she 658.0 0.5 +snapped. 658.5 0.5 +"""Didn't" 659.0 0.5 +you 659.5 0.5 +see 660.0 0.5 +what 660.5 0.5 +it 661.0 0.5 +was 661.5 0.5 +standing 662.0 0.5 +"on?""" 662.5 0.5 ++ 663.0 0.5 +"""The" 663.5 0.5 +"floor?""" 664.0 0.5 +Harry 664.5 0.5 +suggested. 665.0 0.5 +"""I" 665.5 0.5 +wasn't 666.0 0.5 +looking 666.5 0.5 +at 667.0 0.5 +its 667.5 0.5 +feet, 668.0 0.5 +I 668.5 0.5 +was 669.0 0.5 +too 669.5 0.5 +busy 670.0 0.5 +with 670.5 0.5 +its 671.0 0.5 +"heads.""" 671.5 0.5 ++ 672.0 0.5 +"""No," 672.5 0.5 +not italic 673.0 0.5 +the 673.5 0.5 +floor. 674.0 0.5 +It 674.5 0.5 +was 675.0 0.5 +standing 675.5 0.5 +on 676.0 0.5 +a 676.5 0.5 +trapdoor. 677.0 0.5 +It's 677.5 0.5 +obviously 678.0 0.5 +guarding 678.5 0.5 +"something.""" 679.0 0.5 ++ 679.5 0.5 +She 680.0 0.5 +stood 680.5 0.5 +up, 681.0 0.5 +glaring 681.5 0.5 +at 682.0 0.5 +them. 682.5 0.5 ++ 683.0 0.5 +"""I" 683.5 0.5 +hope 684.0 0.5 +you're 684.5 0.5 +pleased 685.0 0.5 +with 685.5 0.5 +yourselves. 686.0 0.5 +We 686.5 0.5 +could 687.0 0.5 +all 687.5 0.5 +have 688.0 0.5 +been 688.5 0.5 +killed 689.0 0.5 +— 689.5 0.5 +or 690.0 0.5 +worse, 690.5 0.5 +expelled. 691.0 0.5 +Now, 691.5 0.5 +if 692.0 0.5 +you 692.5 0.5 +don't 693.0 0.5 +mind, 693.5 0.5 +I'm 694.0 0.5 +going 694.5 0.5 +to 695.0 0.5 +"bed.""" 695.5 0.5 ++ 696.0 0.5 +Ron 696.5 0.5 +stared 697.0 0.5 +after 697.5 0.5 +her, 698.0 0.5 +his 698.5 0.5 +mouth 699.0 0.5 +open. 699.5 0.5 +"""No," 700.0 0.5 +we 700.5 0.5 +don't 701.0 0.5 +"mind,""" 701.5 0.5 +he 702.0 0.5 +said. 702.5 0.5 +"""You'd" 703.0 0.5 +think 703.5 0.5 +we 704.0 0.5 +dragged 704.5 0.5 +her 705.0 0.5 +along, 705.5 0.5 +wouldn't 706.0 0.5 +"you?""" 706.5 0.5 ++ 707.0 0.5 +But 707.5 0.5 +Hermione 708.0 0.5 +had 708.5 0.5 +given 709.0 0.5 +Harry 709.5 0.5 +something 710.0 0.5 +else 710.5 0.5 +to 711.0 0.5 +think 711.5 0.5 +about 712.0 0.5 +as 712.5 0.5 +he 713.0 0.5 +climbed 713.5 0.5 +back 714.0 0.5 +into 714.5 0.5 +bed. 715.0 0.5 +The 715.5 0.5 +dog 716.0 0.5 +was 716.5 0.5 +guarding 717.0 0.5 +something. 717.5 0.5 +. 718.0 0.5 +. . 718.5 0.5 +. . . 719.0 0.5 +What 719.5 0.5 +had 720.0 0.5 +Hagrid 720.5 0.5 +said? 721.0 0.5 +Gringotts 721.5 0.5 +was 722.0 0.5 +the 722.5 0.5 +safest 723.0 0.5 +place 723.5 0.5 +in 724.0 0.5 +the 724.5 0.5 +world 725.0 0.5 +for 725.5 0.5 +something 726.0 0.5 +you 726.5 0.5 +wanted 727.0 0.5 +to 727.5 0.5 +hide 728.0 0.5 +— 728.5 0.5 +except 729.0 0.5 +perhaps 729.5 0.5 +Hogwarts. 730.0 0.5 ++ 730.5 0.5 +It 731.0 0.5 +looked 731.5 0.5 +as 732.0 0.5 +though 732.5 0.5 +Harry 733.0 0.5 +had 733.5 0.5 +found 734.0 0.5 +out 734.5 0.5 +where 735.0 0.5 +the 735.5 0.5 +grubby 736.0 0.5 +little 736.5 0.5 +package 737.0 0.5 +from 737.5 0.5 +vault 738.0 0.5 +seven 738.5 0.5 +hundred 739.0 0.5 +and 739.5 0.5 +thirteen 740.0 0.5 +was. 740.5 0.5 ++ 741.0 16 diff --git a/data/language/harrypotter/task-harry_run-5_events.tsv b/data/language/harrypotter/task-harry_run-5_events.tsv new file mode 100644 index 00000000..fbaf4386 --- /dev/null +++ b/data/language/harrypotter/task-harry_run-5_events.tsv @@ -0,0 +1,1635 @@ +word format onset duration ++ 0 10 +Malfoy 10.0 0.5 +couldn't 10.5 0.5 +believe 11.0 0.5 +his 11.5 0.5 +eyes 12.0 0.5 +when 12.5 0.5 +he 13.0 0.5 +saw 13.5 0.5 +that 14.0 0.5 +Harry 14.5 0.5 +and 15.0 0.5 +Ron 15.5 0.5 +were 16.0 0.5 +still 16.5 0.5 +at 17.0 0.5 +Hogwarts 17.5 0.5 +the 18.0 0.5 +next 18.5 0.5 +day, 19.0 0.5 +looking 19.5 0.5 +tired 20.0 0.5 +but 20.5 0.5 +perfectly 21.0 0.5 +cheerful. 21.5 0.5 +Indeed, 22.0 0.5 +by 22.5 0.5 +the 23.0 0.5 +next 23.5 0.5 +morning 24.0 0.5 +Harry 24.5 0.5 +and 25.0 0.5 +Ron 25.5 0.5 +thought 26.0 0.5 +that 26.5 0.5 +meeting 27.0 0.5 +the 27.5 0.5 +three-headed 28.0 0.5 +dog 28.5 0.5 +had 29.0 0.5 +been 29.5 0.5 +an 30.0 0.5 +excellent 30.5 0.5 +adventure, 31.0 0.5 +and 31.5 0.5 +they 32.0 0.5 +were 32.5 0.5 +quite 33.0 0.5 +keen 33.5 0.5 +to 34.0 0.5 +have 34.5 0.5 +another 35.0 0.5 +one. 35.5 0.5 +In 36.0 0.5 +the 36.5 0.5 +meantime, 37.0 0.5 +Harry 37.5 0.5 +filled 38.0 0.5 +Ron 38.5 0.5 +in 39.0 0.5 +about 39.5 0.5 +the 40.0 0.5 +package 40.5 0.5 +that 41.0 0.5 +seemed 41.5 0.5 +to 42.0 0.5 +have 42.5 0.5 +been 43.0 0.5 +moved 43.5 0.5 +from 44.0 0.5 +Gringotts 44.5 0.5 +to 45.0 0.5 +Hogwarts, 45.5 0.5 +and 46.0 0.5 +they 46.5 0.5 +spent 47.0 0.5 +a 47.5 0.5 +lot 48.0 0.5 +of 48.5 0.5 +time 49.0 0.5 +wondering 49.5 0.5 +what 50.0 0.5 +could 50.5 0.5 +possibly 51.0 0.5 +need 51.5 0.5 +such 52.0 0.5 +heavy 52.5 0.5 +protection. 53.0 0.5 ++ 53.5 0.5 +"""It's" 54.0 0.5 +either 54.5 0.5 +really 55.0 0.5 +valuable 55.5 0.5 +or 56.0 0.5 +really 56.5 0.5 +"dangerous,""" 57.0 0.5 +said 57.5 0.5 +Ron. 58.0 0.5 ++ 58.5 0.5 +"""Or" 59.0 0.5 +"both,""" 59.5 0.5 +said 60.0 0.5 +Harry. 60.5 0.5 ++ 61.0 0.5 +But 61.5 0.5 +as 62.0 0.5 +all 62.5 0.5 +they 63.0 0.5 +knew 63.5 0.5 +for 64.0 0.5 +sure 64.5 0.5 +about 65.0 0.5 +the 65.5 0.5 +mysterious 66.0 0.5 +object 66.5 0.5 +was 67.0 0.5 +that 67.5 0.5 +it 68.0 0.5 +was 68.5 0.5 +about 69.0 0.5 +two 69.5 0.5 +inches 70.0 0.5 +long, 70.5 0.5 +they 71.0 0.5 +didn't 71.5 0.5 +have 72.0 0.5 +much 72.5 0.5 +chance 73.0 0.5 +of 73.5 0.5 +guessing 74.0 0.5 +what 74.5 0.5 +it 75.0 0.5 +was 75.5 0.5 +without 76.0 0.5 +further 76.5 0.5 +clues. 77.0 0.5 ++ 77.5 0.5 +Neither 78.0 0.5 +Neville 78.5 0.5 +nor 79.0 0.5 +Hermione 79.5 0.5 +showed 80.0 0.5 +the 80.5 0.5 +slightest 81.0 0.5 +interest 81.5 0.5 +in 82.0 0.5 +what 82.5 0.5 +lay 83.0 0.5 +underneath 83.5 0.5 +the 84.0 0.5 +dog 84.5 0.5 +and 85.0 0.5 +the 85.5 0.5 +trapdoor. 86.0 0.5 +All 86.5 0.5 +Neville 87.0 0.5 +cared 87.5 0.5 +about 88.0 0.5 +was 88.5 0.5 +never 89.0 0.5 +going 89.5 0.5 +near 90.0 0.5 +the 90.5 0.5 +dog 91.0 0.5 +again. 91.5 0.5 ++ 92.0 0.5 +Hermione 92.5 0.5 +was 93.0 0.5 +now 93.5 0.5 +refusing 94.0 0.5 +to 94.5 0.5 +speak 95.0 0.5 +to 95.5 0.5 +Harry 96.0 0.5 +and 96.5 0.5 +Ron, 97.0 0.5 +but 97.5 0.5 +she 98.0 0.5 +was 98.5 0.5 +such 99.0 0.5 +a 99.5 0.5 +bossy 100.0 0.5 +know-it-all 100.5 0.5 +that 101.0 0.5 +they 101.5 0.5 +saw 102.0 0.5 +this 102.5 0.5 +as 103.0 0.5 +an 103.5 0.5 +added 104.0 0.5 +bonus. 104.5 0.5 +All 105.0 0.5 +they 105.5 0.5 +really 106.0 0.5 +wanted 106.5 0.5 +now 107.0 0.5 +was 107.5 0.5 +a 108.0 0.5 +way 108.5 0.5 +of 109.0 0.5 +getting 109.5 0.5 +back 110.0 0.5 +at 110.5 0.5 +Malfoy, 111.0 0.5 +and 111.5 0.5 +to 112.0 0.5 +their 112.5 0.5 +great 113.0 0.5 +delight, 113.5 0.5 +just 114.0 0.5 +such 114.5 0.5 +a 115.0 0.5 +thing 115.5 0.5 +arrived 116.0 0.5 +in 116.5 0.5 +the 117.0 0.5 +mail 117.5 0.5 +about 118.0 0.5 +a 118.5 0.5 +week 119.0 0.5 +later. 119.5 0.5 ++ 120.0 0.5 +As 120.5 0.5 +the 121.0 0.5 +owls 121.5 0.5 +flooded 122.0 0.5 +into 122.5 0.5 +the 123.0 0.5 +Great 123.5 0.5 +Hall 124.0 0.5 +as 124.5 0.5 +usual, 125.0 0.5 +everyone's 125.5 0.5 +attention 126.0 0.5 +was 126.5 0.5 +caught 127.0 0.5 +at 127.5 0.5 +once 128.0 0.5 +by 128.5 0.5 +a 129.0 0.5 +long, 129.5 0.5 +thin 130.0 0.5 +package 130.5 0.5 +carried 131.0 0.5 +by 131.5 0.5 +six 132.0 0.5 +large 132.5 0.5 +screech 133.0 0.5 +owls. 133.5 0.5 +Harry 134.0 0.5 +was 134.5 0.5 +just 135.0 0.5 +as 135.5 0.5 +interested 136.0 0.5 +as 136.5 0.5 +everyone 137.0 0.5 +else 137.5 0.5 +to 138.0 0.5 +see 138.5 0.5 +what 139.0 0.5 +was 139.5 0.5 +in 140.0 0.5 +this 140.5 0.5 +large 141.0 0.5 +parcel, 141.5 0.5 +and 142.0 0.5 +was 142.5 0.5 +amazed 143.0 0.5 +when 143.5 0.5 +the 144.0 0.5 +owls 144.5 0.5 +soared 145.0 0.5 +down 145.5 0.5 +and 146.0 0.5 +dropped 146.5 0.5 +it 147.0 0.5 +right 147.5 0.5 +in 148.0 0.5 +front 148.5 0.5 +of 149.0 0.5 +him, 149.5 0.5 +knocking 150.0 0.5 +his 150.5 0.5 +bacon 151.0 0.5 +to 151.5 0.5 +the 152.0 0.5 +floor. 152.5 0.5 +They 153.0 0.5 +had 153.5 0.5 +hardly 154.0 0.5 +fluttered 154.5 0.5 +out 155.0 0.5 +of 155.5 0.5 +the 156.0 0.5 +way 156.5 0.5 +when 157.0 0.5 +another 157.5 0.5 +owl 158.0 0.5 +dropped 158.5 0.5 +a 159.0 0.5 +letter 159.5 0.5 +on 160.0 0.5 +top 160.5 0.5 +of 161.0 0.5 +the 161.5 0.5 +parcel. 162.0 0.5 ++ 162.5 0.5 +Harry 163.0 0.5 +ripped 163.5 0.5 +open 164.0 0.5 +the 164.5 0.5 +letter 165.0 0.5 +first, 165.5 0.5 +which 166.0 0.5 +was 166.5 0.5 +lucky, 167.0 0.5 +because 167.5 0.5 +it 168.0 0.5 +said: 168.5 0.5 ++ 169.0 0.5 +DO 169.5 0.5 +NOT 170.0 0.5 +OPEN 170.5 0.5 +THE 171.0 0.5 +PARCEL 171.5 0.5 +AT 172.0 0.5 +THE 172.5 0.5 +TABLE. 173.0 0.5 +It 173.5 0.5 +contains 174.0 0.5 +your 174.5 0.5 +new 175.0 0.5 +Nimbus 175.5 0.5 +Two 176.0 0.5 +Thousand, 176.5 0.5 +but 177.0 0.5 +I 177.5 0.5 +don't 178.0 0.5 +want 178.5 0.5 +everybody 179.0 0.5 +knowing 179.5 0.5 +you've 180.0 0.5 +got 180.5 0.5 +a 181.0 0.5 +broomstick 181.5 0.5 +or 182.0 0.5 +they'll 182.5 0.5 +all 183.0 0.5 +want 183.5 0.5 +one. 184.0 0.5 +Oliver 184.5 0.5 +Wood 185.0 0.5 +will 185.5 0.5 +meet 186.0 0.5 +you 186.5 0.5 +tonight 187.0 0.5 +on 187.5 0.5 +the 188.0 0.5 +Quidditch 188.5 0.5 +field 189.0 0.5 +at 189.5 0.5 +seven 190.0 0.5 +o'clock 190.5 0.5 +for 191.0 0.5 +your 191.5 0.5 +first 192.0 0.5 +training 192.5 0.5 +session. 193.0 0.5 +Professor italic 193.5 0.5 +McGonagall italic 194.0 0.5 ++ 194.5 0.5 +Harry 195.0 0.5 +had 195.5 0.5 +difficulty 196.0 0.5 +hiding 196.5 0.5 +his 197.0 0.5 +glee 197.5 0.5 +as 198.0 0.5 +he 198.5 0.5 +handed 199.0 0.5 +the 199.5 0.5 +note 200.0 0.5 +to 200.5 0.5 +Ron 201.0 0.5 +to 201.5 0.5 +read. 202.0 0.5 ++ 202.5 0.5 +"""A" 203.0 0.5 +Nimbus 203.5 0.5 +Two 204.0 0.5 +"Thousand!""" 204.5 0.5 +Ron 205.0 0.5 +moaned 205.5 0.5 +enviously. 206.0 0.5 +"""I've" 206.5 0.5 +never 207.0 0.5 +even 207.5 0.5 +touched 208.0 0.5 +"one.""" 208.5 0.5 ++ 209.0 0.5 +They 209.5 0.5 +left 210.0 0.5 +the 210.5 0.5 +hall 211.0 0.5 +quickly, 211.5 0.5 +wanting 212.0 0.5 +to 212.5 0.5 +unwrap 213.0 0.5 +the 213.5 0.5 +broomstick 214.0 0.5 +in 214.5 0.5 +private 215.0 0.5 +before 215.5 0.5 +their 216.0 0.5 +first 216.5 0.5 +class, 217.0 0.5 +but 217.5 0.5 +halfway 218.0 0.5 +across 218.5 0.5 +the 219.0 0.5 +entrance 219.5 0.5 +hall 220.0 0.5 +they 220.5 0.5 +found 221.0 0.5 +the 221.5 0.5 +way 222.0 0.5 +upstairs 222.5 0.5 +barred 223.0 0.5 +by 223.5 0.5 +Crabbe 224.0 0.5 +and 224.5 0.5 +Goyle. 225.0 0.5 +Malfoy 225.5 0.5 +seized 226.0 0.5 +the 226.5 0.5 +package 227.0 0.5 +from 227.5 0.5 +Harry 228.0 0.5 +and 228.5 0.5 +felt 229.0 0.5 +it. 229.5 0.5 ++ 230.0 0.5 +"""That's" 230.5 0.5 +a 231.0 0.5 +"broomstick,""" 231.5 0.5 +he 232.0 0.5 +said, 232.5 0.5 +throwing 233.0 0.5 +it 233.5 0.5 +back 234.0 0.5 +to 234.5 0.5 +Harry 235.0 0.5 +with 235.5 0.5 +a 236.0 0.5 +mixture 236.5 0.5 +of 237.0 0.5 +jealousy 237.5 0.5 +and 238.0 0.5 +spite 238.5 0.5 +on 239.0 0.5 +his 239.5 0.5 +face. 240.0 0.5 +"""You'll" 240.5 0.5 +be 241.0 0.5 +in 241.5 0.5 +for 242.0 0.5 +it 242.5 0.5 +this 243.0 0.5 +time, 243.5 0.5 +Potter, 244.0 0.5 +first 244.5 0.5 +years 245.0 0.5 +aren't 245.5 0.5 +allowed 246.0 0.5 +"them.""" 246.5 0.5 ++ 247.0 0.5 +Ron 247.5 0.5 +couldn't 248.0 0.5 +resist 248.5 0.5 +it. 249.0 0.5 ++ 249.5 0.5 +"""It's" 250.0 0.5 +not 250.5 0.5 +any 251.0 0.5 +old 251.5 0.5 +"broomstick,""" 252.0 0.5 +he 252.5 0.5 +said, 253.0 0.5 +"""it's" 253.5 0.5 +a 254.0 0.5 +Nimbus 254.5 0.5 +Two 255.0 0.5 +Thousand. 255.5 0.5 +What 256.0 0.5 +did 256.5 0.5 +you 257.0 0.5 +say 257.5 0.5 +you've 258.0 0.5 +got 258.5 0.5 +at 259.0 0.5 +home, 259.5 0.5 +Malfoy, 260.0 0.5 +a 260.5 0.5 +Comet 261.0 0.5 +Two 261.5 0.5 +"Sixty?""" 262.0 0.5 +Ron 262.5 0.5 +grinned 263.0 0.5 +at 263.5 0.5 +Harry. 264.0 0.5 +"""Comets" 264.5 0.5 +look 265.0 0.5 +flashy, 265.5 0.5 +but 266.0 0.5 +they're 266.5 0.5 +not 267.0 0.5 +in 267.5 0.5 +the 268.0 0.5 +same 268.5 0.5 +league 269.0 0.5 +as 269.5 0.5 +the 270.0 0.5 +"Nimbus.""" 270.5 0.5 ++ 271.0 0.5 +"""What" 271.5 0.5 +would 272.0 0.5 +you 272.5 0.5 +know 273.0 0.5 +about 273.5 0.5 +it, 274.0 0.5 +Weasley, 274.5 0.5 +you 275.0 0.5 +couldn't 275.5 0.5 +afford 276.0 0.5 +half 276.5 0.5 +the 277.0 0.5 +"handle,""" 277.5 0.5 +Malfoy 278.0 0.5 +snapped 278.5 0.5 +back. 279.0 0.5 +"""I" 279.5 0.5 +suppose 280.0 0.5 +you 280.5 0.5 +and 281.0 0.5 +your 281.5 0.5 +brothers 282.0 0.5 +have 282.5 0.5 +to 283.0 0.5 +save 283.5 0.5 +up 284.0 0.5 +twig 284.5 0.5 +by 285.0 0.5 +"twig.""" 285.5 0.5 ++ 286.0 0.5 +Before 286.5 0.5 +Ron 287.0 0.5 +could 287.5 0.5 +answer, 288.0 0.5 +Professor 288.5 0.5 +Flitwick 289.0 0.5 +appeared 289.5 0.5 +at 290.0 0.5 +Malfoy's 290.5 0.5 +elbow. 291.0 0.5 ++ 291.5 0.5 +"""Not" 292.0 0.5 +arguing, 292.5 0.5 +I 293.0 0.5 +hope, 293.5 0.5 +"boys?""" 294.0 0.5 +he 294.5 0.5 +squeaked. 295.0 0.5 ++ 295.5 0.5 +"""Potter's" 296.0 0.5 +been 296.5 0.5 +sent 297.0 0.5 +a 297.5 0.5 +broomstick, 298.0 0.5 +"Professor,""" 298.5 0.5 +said 299.0 0.5 +Malfoy 299.5 0.5 +quickly. 300.0 0.5 ++ 300.5 0.5 +"""Yes," 301.0 0.5 +yes, 301.5 0.5 +that's 302.0 0.5 +"right,""" 302.5 0.5 +said 303.0 0.5 +Professor 303.5 0.5 +Flitwick, 304.0 0.5 +beaming 304.5 0.5 +at 305.0 0.5 +Harry. 305.5 0.5 +"""Professor" 306.0 0.5 +McGonagall 306.5 0.5 +told 307.0 0.5 +me 307.5 0.5 +all 308.0 0.5 +about 308.5 0.5 +the 309.0 0.5 +special 309.5 0.5 +circumstances, 310.0 0.5 +Potter. 310.5 0.5 +And 311.0 0.5 +what 311.5 0.5 +model 312.0 0.5 +is 312.5 0.5 +"it?""" 313.0 0.5 ++ 313.5 0.5 +"""A" 314.0 0.5 +Nimbus 314.5 0.5 +Two 315.0 0.5 +Thousand, 315.5 0.5 +"sit,""" 316.0 0.5 +said 316.5 0.5 +Harry, 317.0 0.5 +fighting 317.5 0.5 +not 318.0 0.5 +to 318.5 0.5 +laugh 319.0 0.5 +at 319.5 0.5 +the 320.0 0.5 +look 320.5 0.5 +of 321.0 0.5 +horror 321.5 0.5 +on 322.0 0.5 +Malfoy's 322.5 0.5 +face. 323.0 0.5 +"""And" 323.5 0.5 +it's 324.0 0.5 +really 324.5 0.5 +thanks 325.0 0.5 +to 325.5 0.5 +Malfoy 326.0 0.5 +here 326.5 0.5 +that 327.0 0.5 +I've 327.5 0.5 +got 328.0 0.5 +"it,""" 328.5 0.5 +he 329.0 0.5 +added. 329.5 0.5 ++ 330.0 0.5 +Harry 330.5 0.5 +and 331.0 0.5 +Ron 331.5 0.5 +headed 332.0 0.5 +upstairs, 332.5 0.5 +smothering 333.0 0.5 +their 333.5 0.5 +laughter 334.0 0.5 +at 334.5 0.5 +Malfoy's 335.0 0.5 +obvious 335.5 0.5 +rage 336.0 0.5 +and 336.5 0.5 +confusion. 337.0 0.5 ++ 337.5 0.5 +"""Well," 338.0 0.5 +it's 338.5 0.5 +"true,""" 339.0 0.5 +Harry 339.5 0.5 +chortled 340.0 0.5 +as 340.5 0.5 +they 341.0 0.5 +reached 341.5 0.5 +the 342.0 0.5 +top 342.5 0.5 +of 343.0 0.5 +the 343.5 0.5 +marble 344.0 0.5 +staircase, 344.5 0.5 +"""If" 345.0 0.5 +he 345.5 0.5 +hadn't 346.0 0.5 +stolen 346.5 0.5 +Neville's 347.0 0.5 +Remembrall 347.5 0.5 +I 348.0 0.5 +wouldn't 348.5 0.5 +be 349.0 0.5 +on 349.5 0.5 +the 350.0 0.5 +"team....""" 350.5 0.5 ++ 351.0 0.5 +"""So" 351.5 0.5 +I 352.0 0.5 +suppose 352.5 0.5 +you 353.0 0.5 +think 353.5 0.5 +that's 354.0 0.5 +a 354.5 0.5 +reward 355.0 0.5 +for 355.5 0.5 +breaking 356.0 0.5 +"rules?""" 356.5 0.5 +came 357.0 0.5 +an 357.5 0.5 +angry 358.0 0.5 +voice 358.5 0.5 +from 359.0 0.5 +just 359.5 0.5 +behind 360.0 0.5 +them. 360.5 0.5 +Hermione 361.0 0.5 +was 361.5 0.5 +stomping 362.0 0.5 +up 362.5 0.5 +the 363.0 0.5 +stairs, 363.5 0.5 +looking 364.0 0.5 +disapprovingly 364.5 0.5 +at 365.0 0.5 +the 365.5 0.5 +package 366.0 0.5 +in 366.5 0.5 +Harry's 367.0 0.5 +hand. 367.5 0.5 ++ 368.0 0.5 +"""I" 368.5 0.5 +thought 369.0 0.5 +you 369.5 0.5 +weren't 370.0 0.5 +speaking 370.5 0.5 +to 371.0 0.5 +"us?""" 371.5 0.5 +said 372.0 0.5 +Harry. 372.5 0.5 ++ 373.0 0.5 +"""Yes," 373.5 0.5 +don't 374.0 0.5 +stop 374.5 0.5 +"now,""" 375.0 0.5 +said 375.5 0.5 +Ron, 376.0 0.5 +"""it's" 376.5 0.5 +doing 377.0 0.5 +us 377.5 0.5 +so 378.0 0.5 +much 378.5 0.5 +"good.""" 379.0 0.5 ++ 379.5 0.5 +Hermione 380.0 0.5 +marched 380.5 0.5 +away 381.0 0.5 +with 381.5 0.5 +her 382.0 0.5 +nose 382.5 0.5 +in 383.0 0.5 +the 383.5 0.5 +air. 384.0 0.5 ++ 384.5 0.5 +Harry 385.0 0.5 +had 385.5 0.5 +a 386.0 0.5 +lot 386.5 0.5 +of 387.0 0.5 +trouble 387.5 0.5 +keeping 388.0 0.5 +his 388.5 0.5 +mind 389.0 0.5 +on 389.5 0.5 +his 390.0 0.5 +lessons 390.5 0.5 +that 391.0 0.5 +day. 391.5 0.5 +It 392.0 0.5 +kept 392.5 0.5 +wandering 393.0 0.5 +up 393.5 0.5 +to 394.0 0.5 +the 394.5 0.5 +dormitory 395.0 0.5 +where 395.5 0.5 +his 396.0 0.5 +new 396.5 0.5 +broomstick 397.0 0.5 +was 397.5 0.5 +lying 398.0 0.5 +under 398.5 0.5 +his 399.0 0.5 +bed, 399.5 0.5 +or 400.0 0.5 +straying 400.5 0.5 +off 401.0 0.5 +to 401.5 0.5 +the 402.0 0.5 +Quidditch 402.5 0.5 +field 403.0 0.5 +where 403.5 0.5 +he'd 404.0 0.5 +be 404.5 0.5 +learning 405.0 0.5 +to 405.5 0.5 +play 406.0 0.5 +that 406.5 0.5 +night. 407.0 0.5 +He 407.5 0.5 +bolted 408.0 0.5 +his 408.5 0.5 +dinner 409.0 0.5 +that 409.5 0.5 +evening 410.0 0.5 +without 410.5 0.5 +noticing 411.0 0.5 +what 411.5 0.5 +he 412.0 0.5 +was 412.5 0.5 +eating, 413.0 0.5 +and 413.5 0.5 +then 414.0 0.5 +rushed 414.5 0.5 +upstairs 415.0 0.5 +with 415.5 0.5 +Ron 416.0 0.5 +to 416.5 0.5 +unwrap 417.0 0.5 +the 417.5 0.5 +Nimbus 418.0 0.5 +Two 418.5 0.5 +Thousand 419.0 0.5 +at 419.5 0.5 +last. 420.0 0.5 ++ 420.5 0.5 +"""Wow,""" 421.0 0.5 +Ron 421.5 0.5 +sighed, 422.0 0.5 +as 422.5 0.5 +the 423.0 0.5 +broomstick 423.5 0.5 +rolled 424.0 0.5 +onto 424.5 0.5 +Harry's 425.0 0.5 +bedspread. 425.5 0.5 ++ 426.0 0.5 +Even 426.5 0.5 +Harry, 427.0 0.5 +who 427.5 0.5 +knew 428.0 0.5 +nothing 428.5 0.5 +about 429.0 0.5 +the 429.5 0.5 +different 430.0 0.5 +brooms, 430.5 0.5 +thought 431.0 0.5 +it 431.5 0.5 +looked 432.0 0.5 +wonderful. 432.5 0.5 +Sleek 433.0 0.5 +and 433.5 0.5 +shiny, 434.0 0.5 +with 434.5 0.5 +a 435.0 0.5 +mahogany 435.5 0.5 +handle, 436.0 0.5 +it 436.5 0.5 +had 437.0 0.5 +a 437.5 0.5 +long 438.0 0.5 +tail 438.5 0.5 +of 439.0 0.5 +neat, 439.5 0.5 +straight 440.0 0.5 +twigs 440.5 0.5 +and 441.0 0.5 +Nimbus 441.5 0.5 +Two 442.0 0.5 +Thousand 442.5 0.5 +written 443.0 0.5 +in 443.5 0.5 +gold 444.0 0.5 +near 444.5 0.5 +the 445.0 0.5 +top. 445.5 0.5 ++ 446.0 0.5 +As 446.5 0.5 +seven 447.0 0.5 +o'clock 447.5 0.5 +drew 448.0 0.5 +nearer, 448.5 0.5 +Harry 449.0 0.5 +left 449.5 0.5 +the 450.0 0.5 +castle 450.5 0.5 +and 451.0 0.5 +set 451.5 0.5 +off 452.0 0.5 +in 452.5 0.5 +the 453.0 0.5 +dusk 453.5 0.5 +toward 454.0 0.5 +the 454.5 0.5 +Quidditch 455.0 0.5 +field. 455.5 0.5 +Held 456.0 0.5 +never 456.5 0.5 +been 457.0 0.5 +inside 457.5 0.5 +the 458.0 0.5 +stadium 458.5 0.5 +before. 459.0 0.5 +Hundreds 459.5 0.5 +of 460.0 0.5 +seats 460.5 0.5 +were 461.0 0.5 +raised 461.5 0.5 +in 462.0 0.5 +stands 462.5 0.5 +around 463.0 0.5 +the 463.5 0.5 +field 464.0 0.5 +so 464.5 0.5 +that 465.0 0.5 +the 465.5 0.5 +spectators 466.0 0.5 +were 466.5 0.5 +high 467.0 0.5 +enough 467.5 0.5 +to 468.0 0.5 +see 468.5 0.5 +what 469.0 0.5 +was 469.5 0.5 +going 470.0 0.5 +on. 470.5 0.5 +At 471.0 0.5 +either 471.5 0.5 +end 472.0 0.5 +of 472.5 0.5 +the 473.0 0.5 +field 473.5 0.5 +were 474.0 0.5 +three 474.5 0.5 +golden 475.0 0.5 +poles 475.5 0.5 +with 476.0 0.5 +hoops 476.5 0.5 +on 477.0 0.5 +the 477.5 0.5 +end. 478.0 0.5 +They 478.5 0.5 +reminded 479.0 0.5 +Harry 479.5 0.5 +of 480.0 0.5 +the 480.5 0.5 +little 481.0 0.5 +plastic 481.5 0.5 +sticks 482.0 0.5 +Muggle 482.5 0.5 +children 483.0 0.5 +blew 483.5 0.5 +bubbles 484.0 0.5 +through, 484.5 0.5 +except 485.0 0.5 +that 485.5 0.5 +they 486.0 0.5 +were 486.5 0.5 +fifty 487.0 0.5 +feet 487.5 0.5 +high. 488.0 0.5 ++ 488.5 0.5 +Too 489.0 0.5 +eager 489.5 0.5 +to 490.0 0.5 +fly 490.5 0.5 +again 491.0 0.5 +to 491.5 0.5 +wait 492.0 0.5 +for 492.5 0.5 +Wood, 493.0 0.5 +Harry 493.5 0.5 +mounted 494.0 0.5 +his 494.5 0.5 +broomstick 495.0 0.5 +and 495.5 0.5 +kicked 496.0 0.5 +off 496.5 0.5 +from 497.0 0.5 +the 497.5 0.5 +ground. 498.0 0.5 +What 498.5 0.5 +a 499.0 0.5 +feeling 499.5 0.5 +— 500.0 0.5 +he 500.5 0.5 +swooped 501.0 0.5 +in 501.5 0.5 +and 502.0 0.5 +out 502.5 0.5 +of 503.0 0.5 +the 503.5 0.5 +goal 504.0 0.5 +posts 504.5 0.5 +and 505.0 0.5 +then 505.5 0.5 +sped 506.0 0.5 +up 506.5 0.5 +and 507.0 0.5 +down 507.5 0.5 +the 508.0 0.5 +field. 508.5 0.5 +The 509.0 0.5 +Nimbus 509.5 0.5 +Two 510.0 0.5 +Thousand 510.5 0.5 +turned 511.0 0.5 +wherever 511.5 0.5 +he 512.0 0.5 +wanted 512.5 0.5 +at 513.0 0.5 +his 513.5 0.5 +lightest 514.0 0.5 +touch. 514.5 0.5 ++ 515.0 0.5 +"""Hey," 515.5 0.5 +Potter, 516.0 0.5 +come 516.5 0.5 +"down!""" 517.0 0.5 ++ 517.5 0.5 +Oliver 518.0 0.5 +Wood 518.5 0.5 +had 519.0 0.5 +arrived. 519.5 0.5 +He 520.0 0.5 +was 520.5 0.5 +carrying 521.0 0.5 +a 521.5 0.5 +large 522.0 0.5 +wooden 522.5 0.5 +crate 523.0 0.5 +under 523.5 0.5 +his 524.0 0.5 +arm. 524.5 0.5 +Harry 525.0 0.5 +landed 525.5 0.5 +next 526.0 0.5 +to 526.5 0.5 +him. 527.0 0.5 ++ 527.5 0.5 +"""Very" 528.0 0.5 +"nice,""" 528.5 0.5 +said 529.0 0.5 +Wood, 529.5 0.5 +his 530.0 0.5 +eyes 530.5 0.5 +glinting. 531.0 0.5 +"""I" 531.5 0.5 +see 532.0 0.5 +what 532.5 0.5 +McGonagall 533.0 0.5 +meant... 533.5 0.5 +you 534.0 0.5 +really 534.5 0.5 +are 535.0 0.5 +a 535.5 0.5 +natural. 536.0 0.5 +I'm 536.5 0.5 +just 537.0 0.5 +going 537.5 0.5 +to 538.0 0.5 +teach 538.5 0.5 +you 539.0 0.5 +the 539.5 0.5 +rules 540.0 0.5 +this 540.5 0.5 +evening, 541.0 0.5 +then 541.5 0.5 +you'll 542.0 0.5 +be 542.5 0.5 +joining 543.0 0.5 +team 543.5 0.5 +practice 544.0 0.5 +three 544.5 0.5 +times 545.0 0.5 +a 545.5 0.5 +"week.""" 546.0 0.5 ++ 546.5 0.5 +He 547.0 0.5 +opened 547.5 0.5 +the 548.0 0.5 +crate. 548.5 0.5 +Inside 549.0 0.5 +were 549.5 0.5 +four 550.0 0.5 +different-sized 550.5 0.5 +balls. 551.0 0.5 ++ 551.5 0.5 +"""Right,""" 552.0 0.5 +said 552.5 0.5 +Wood. 553.0 0.5 +"""Now," 553.5 0.5 +Quidditch 554.0 0.5 +is 554.5 0.5 +easy 555.0 0.5 +enough 555.5 0.5 +to 556.0 0.5 +understand, 556.5 0.5 +even 557.0 0.5 +if 557.5 0.5 +it's 558.0 0.5 +not 558.5 0.5 +too 559.0 0.5 +easy 559.5 0.5 +to 560.0 0.5 +play. 560.5 0.5 +There 561.0 0.5 +are 561.5 0.5 +seven 562.0 0.5 +players 562.5 0.5 +on 563.0 0.5 +each 563.5 0.5 +side. 564.0 0.5 +Three 564.5 0.5 +of 565.0 0.5 +them 565.5 0.5 +are 566.0 0.5 +called 566.5 0.5 +"Chasers.""" 567.0 0.5 ++ 567.5 0.5 +"""Three" 568.0 0.5 +"Chasers,""" 568.5 0.5 +Harry 569.0 0.5 +repeated, 569.5 0.5 +as 570.0 0.5 +Wood 570.5 0.5 +took 571.0 0.5 +out 571.5 0.5 +a 572.0 0.5 +bright 572.5 0.5 +red 573.0 0.5 +ball 573.5 0.5 +about 574.0 0.5 +the 574.5 0.5 +size 575.0 0.5 +of 575.5 0.5 +a 576.0 0.5 +soccer 576.5 0.5 +ball. 577.0 0.5 ++ 577.5 0.5 +"""This" 578.0 0.5 +ball's 578.5 0.5 +called 579.0 0.5 +the 579.5 0.5 +"Quaffle,""" 580.0 0.5 +said 580.5 0.5 +Wood. 581.0 0.5 +"""The" 581.5 0.5 +Chasers 582.0 0.5 +throw 582.5 0.5 +the 583.0 0.5 +Quaffle 583.5 0.5 +to 584.0 0.5 +each 584.5 0.5 +other 585.0 0.5 +and 585.5 0.5 +try 586.0 0.5 +and 586.5 0.5 +get 587.0 0.5 +it 587.5 0.5 +through 588.0 0.5 +one 588.5 0.5 +of 589.0 0.5 +the 589.5 0.5 +hoops 590.0 0.5 +to 590.5 0.5 +score 591.0 0.5 +a 591.5 0.5 +goal. 592.0 0.5 +Ten 592.5 0.5 +points 593.0 0.5 +every 593.5 0.5 +time 594.0 0.5 +the 594.5 0.5 +Quaffle 595.0 0.5 +goes 595.5 0.5 +through 596.0 0.5 +one 596.5 0.5 +of 597.0 0.5 +the 597.5 0.5 +hoops. 598.0 0.5 +Follow 598.5 0.5 +"me?""" 599.0 0.5 ++ 599.5 0.5 +"""The" 600.0 0.5 +Chasers 600.5 0.5 +throw 601.0 0.5 +the 601.5 0.5 +Quaffle 602.0 0.5 +and 602.5 0.5 +put 603.0 0.5 +it 603.5 0.5 +through 604.0 0.5 +the 604.5 0.5 +hoops 605.0 0.5 +to 605.5 0.5 +"score,""" 606.0 0.5 +Harry 606.5 0.5 +recited. 607.0 0.5 +"""So" 607.5 0.5 +— 608.0 0.5 +that's 608.5 0.5 +sort 609.0 0.5 +of 609.5 0.5 +like 610.0 0.5 +basketball 610.5 0.5 +on 611.0 0.5 +broomsticks 611.5 0.5 +with 612.0 0.5 +six 612.5 0.5 +hoops, 613.0 0.5 +isn't 613.5 0.5 +"it?""" 614.0 0.5 ++ 614.5 0.5 +"""What's" 615.0 0.5 +"basketball?""" 615.5 0.5 +said 616.0 0.5 +Wood 616.5 0.5 +curiously. 617.0 0.5 ++ 617.5 0.5 +"""Never" 618.0 0.5 +"mind,""" 618.5 0.5 +said 619.0 0.5 +Harry 619.5 0.5 +quickly. 620.0 0.5 ++ 620.5 0.5 +"""Now," 621.0 0.5 +there's 621.5 0.5 +another 622.0 0.5 +player 622.5 0.5 +on 623.0 0.5 +each 623.5 0.5 +side 624.0 0.5 +who's 624.5 0.5 +called 625.0 0.5 +the 625.5 0.5 +Keeper 626.0 0.5 +-I'm 626.5 0.5 +Keeper 627.0 0.5 +for 627.5 0.5 +Gryffindor. 628.0 0.5 +I 628.5 0.5 +have 629.0 0.5 +to 629.5 0.5 +fly 630.0 0.5 +around 630.5 0.5 +our 631.0 0.5 +hoops 631.5 0.5 +and 632.0 0.5 +stop 632.5 0.5 +the 633.0 0.5 +other 633.5 0.5 +team 634.0 0.5 +from 634.5 0.5 +"scoring.""" 635.0 0.5 ++ 635.5 0.5 +"""Three" 636.0 0.5 +Chasers, 636.5 0.5 +one 637.0 0.5 +"Keeper,""" 637.5 0.5 +said 638.0 0.5 +Harry, 638.5 0.5 +who 639.0 0.5 +was 639.5 0.5 +determined 640.0 0.5 +to 640.5 0.5 +remember 641.0 0.5 +it 641.5 0.5 +all. 642.0 0.5 +"""And" 642.5 0.5 +they 643.0 0.5 +play 643.5 0.5 +with 644.0 0.5 +the 644.5 0.5 +Quaffle. 645.0 0.5 +Okay, 645.5 0.5 +got 646.0 0.5 +that. 646.5 0.5 +So 647.0 0.5 +what 647.5 0.5 +are 648.0 0.5 +they 648.5 0.5 +"for?""" 649.0 0.5 +He 649.5 0.5 +pointed 650.0 0.5 +at 650.5 0.5 +the 651.0 0.5 +three 651.5 0.5 +balls 652.0 0.5 +left 652.5 0.5 +inside 653.0 0.5 +the 653.5 0.5 +box. 654.0 0.5 ++ 654.5 0.5 +"""I'll" 655.0 0.5 +show 655.5 0.5 +you 656.0 0.5 +"now,""" 656.5 0.5 +said 657.0 0.5 +Wood. 657.5 0.5 +"""Take" 658.0 0.5 +"this.""" 658.5 0.5 ++ 659.0 0.5 +He 659.5 0.5 +handed 660.0 0.5 +Harry 660.5 0.5 +a 661.0 0.5 +small 661.5 0.5 +club, 662.0 0.5 +a 662.5 0.5 +bit 663.0 0.5 +like 663.5 0.5 +a 664.0 0.5 +short 664.5 0.5 +baseball 665.0 0.5 +bat. 665.5 0.5 ++ 666.0 0.5 +"""I'm" 666.5 0.5 +going 667.0 0.5 +to 667.5 0.5 +show 668.0 0.5 +you 668.5 0.5 +what 669.0 0.5 +the 669.5 0.5 +Bludgers 670.0 0.5 +"do,""" 670.5 0.5 +Wood 671.0 0.5 +said. 671.5 0.5 +"""These" 672.0 0.5 +two 672.5 0.5 +are 673.0 0.5 +the 673.5 0.5 +"Bludgers.""" 674.0 0.5 ++ 674.5 0.5 +He 675.0 0.5 +showed 675.5 0.5 +Harry 676.0 0.5 +two 676.5 0.5 +identical 677.0 0.5 +balls, 677.5 0.5 +jet 678.0 0.5 +black 678.5 0.5 +and 679.0 0.5 +slightly 679.5 0.5 +smaller 680.0 0.5 +than 680.5 0.5 +the 681.0 0.5 +red 681.5 0.5 +Quaffle. 682.0 0.5 +Harry 682.5 0.5 +noticed 683.0 0.5 +that 683.5 0.5 +they 684.0 0.5 +seemed 684.5 0.5 +to 685.0 0.5 +be 685.5 0.5 +straining 686.0 0.5 +to 686.5 0.5 +escape 687.0 0.5 +the 687.5 0.5 +straps 688.0 0.5 +holding 688.5 0.5 +them 689.0 0.5 +inside 689.5 0.5 +the 690.0 0.5 +box. 690.5 0.5 ++ 691.0 0.5 +"""Stand" 691.5 0.5 +"back,""" 692.0 0.5 +Wood 692.5 0.5 +warned 693.0 0.5 +Harry. 693.5 0.5 +He 694.0 0.5 +bent 694.5 0.5 +down 695.0 0.5 +and 695.5 0.5 +freed 696.0 0.5 +one 696.5 0.5 +of 697.0 0.5 +the 697.5 0.5 +Bludgers. 698.0 0.5 ++ 698.5 0.5 +At 699.0 0.5 +once, 699.5 0.5 +the 700.0 0.5 +black 700.5 0.5 +ball 701.0 0.5 +rose 701.5 0.5 +high 702.0 0.5 +in 702.5 0.5 +the 703.0 0.5 +air 703.5 0.5 +and 704.0 0.5 +then 704.5 0.5 +pelted 705.0 0.5 +straight 705.5 0.5 +at 706.0 0.5 +Harry's 706.5 0.5 +face. 707.0 0.5 +Harry 707.5 0.5 +swung 708.0 0.5 +at 708.5 0.5 +it 709.0 0.5 +with 709.5 0.5 +the 710.0 0.5 +bat 710.5 0.5 +to 711.0 0.5 +stop 711.5 0.5 +it 712.0 0.5 +from 712.5 0.5 +breaking 713.0 0.5 +his 713.5 0.5 +nose, 714.0 0.5 +and 714.5 0.5 +sent 715.0 0.5 +it 715.5 0.5 +zigzagging 716.0 0.5 +away 716.5 0.5 +into 717.0 0.5 +the 717.5 0.5 +air 718.0 0.5 +— 718.5 0.5 +it 719.0 0.5 +zoomed 719.5 0.5 +around 720.0 0.5 +their 720.5 0.5 +heads 721.0 0.5 +and 721.5 0.5 +then 722.0 0.5 +shot 722.5 0.5 +at 723.0 0.5 +Wood, 723.5 0.5 +who 724.0 0.5 +dived 724.5 0.5 +on 725.0 0.5 +top 725.5 0.5 +of 726.0 0.5 +it 726.5 0.5 +and 727.0 0.5 +managed 727.5 0.5 +to 728.0 0.5 +pin 728.5 0.5 +it 729.0 0.5 +to 729.5 0.5 +the 730.0 0.5 +ground. 730.5 0.5 ++ 731.0 0.5 +"""See?""" 731.5 0.5 +Wood 732.0 0.5 +panted, 732.5 0.5 +forcing 733.0 0.5 +the 733.5 0.5 +struggling 734.0 0.5 +Bludger 734.5 0.5 +back 735.0 0.5 +into 735.5 0.5 +the 736.0 0.5 +crate 736.5 0.5 +and 737.0 0.5 +strapping 737.5 0.5 +it 738.0 0.5 +down 738.5 0.5 +safely. 739.0 0.5 +"""The" 739.5 0.5 +Bludgers 740.0 0.5 +rocket 740.5 0.5 +around, 741.0 0.5 +trying 741.5 0.5 +to 742.0 0.5 +knock 742.5 0.5 +players 743.0 0.5 +off 743.5 0.5 +their 744.0 0.5 +brooms. 744.5 0.5 +That's 745.0 0.5 +why 745.5 0.5 +you 746.0 0.5 +have 746.5 0.5 +two 747.0 0.5 +Beaters 747.5 0.5 +on 748.0 0.5 +each 748.5 0.5 +team 749.0 0.5 +— 749.5 0.5 +the 750.0 0.5 +Weasley 750.5 0.5 +twins 751.0 0.5 +are 751.5 0.5 +ours 752.0 0.5 +— 752.5 0.5 +it's 753.0 0.5 +their 753.5 0.5 +job 754.0 0.5 +to 754.5 0.5 +protect 755.0 0.5 +their 755.5 0.5 +side 756.0 0.5 +from 756.5 0.5 +the 757.0 0.5 +Bludgers 757.5 0.5 +and 758.0 0.5 +try 758.5 0.5 +and 759.0 0.5 +knock 759.5 0.5 +them 760.0 0.5 +toward 760.5 0.5 +the 761.0 0.5 +other 761.5 0.5 +team. 762.0 0.5 +So 762.5 0.5 +— 763.0 0.5 +think 763.5 0.5 +you've 764.0 0.5 +got 764.5 0.5 +all 765.0 0.5 +"that?""" 765.5 0.5 ++ 766.0 0.5 +"""Three" 766.5 0.5 +Chasers 767.0 0.5 +try 767.5 0.5 +and 768.0 0.5 +score 768.5 0.5 +with 769.0 0.5 +the 769.5 0.5 +Quaffle; 770.0 0.5 +the 770.5 0.5 +Keeper 771.0 0.5 +guards 771.5 0.5 +the 772.0 0.5 +goal 772.5 0.5 +posts; 773.0 0.5 +the 773.5 0.5 +Beaters 774.0 0.5 +keep 774.5 0.5 +the 775.0 0.5 +Bludgers 775.5 0.5 +away 776.0 0.5 +from 776.5 0.5 +their 777.0 0.5 +"team,""" 777.5 0.5 +Harry 778.0 0.5 +reeled 778.5 0.5 +off. 779.0 0.5 ++ 779.5 0.5 +"""Very" 780.0 0.5 +"good,""" 780.5 0.5 +said 781.0 0.5 +Wood. 781.5 0.5 ++ 782.0 0.5 +"""Er" 782.5 0.5 +— 783.0 0.5 +have 783.5 0.5 +the 784.0 0.5 +Bludgers 784.5 0.5 +ever 785.0 0.5 +killed 785.5 0.5 +"anyone?""" 786.0 0.5 +Harry 786.5 0.5 +asked, 787.0 0.5 +hoping 787.5 0.5 +he 788.0 0.5 +sounded 788.5 0.5 +offhand. 789.0 0.5 ++ 789.5 0.5 +"""Never" 790.0 0.5 +at 790.5 0.5 +Hogwarts. 791.0 0.5 +We've 791.5 0.5 +had 792.0 0.5 +a 792.5 0.5 +couple 793.0 0.5 +of 793.5 0.5 +broken 794.0 0.5 +jaws 794.5 0.5 +but 795.0 0.5 +nothing 795.5 0.5 +worse 796.0 0.5 +than 796.5 0.5 +that. 797.0 0.5 +Now, 797.5 0.5 +the 798.0 0.5 +last 798.5 0.5 +member 799.0 0.5 +of 799.5 0.5 +the 800.0 0.5 +team 800.5 0.5 +is 801.0 0.5 +the 801.5 0.5 +Seeker. 802.0 0.5 +That's 802.5 0.5 +you. 803.0 0.5 +And 803.5 0.5 +you 804.0 0.5 +don't 804.5 0.5 +have 805.0 0.5 +to 805.5 0.5 +worry 806.0 0.5 +about 806.5 0.5 +the 807.0 0.5 +Quaffle 807.5 0.5 +or 808.0 0.5 +the 808.5 0.5 +Bludgers 809.0 0.5 +"—""" 809.5 0.5 ++ 810.0 0.5 +"""—" 810.5 0.5 +unless 811.0 0.5 +they 811.5 0.5 +crack 812.0 0.5 +my 812.5 0.5 +head 813.0 0.5 +"open.""" 813.5 0.5 ++ 814.0 0.5 +"""Don't" 814.5 0.5 +worry, 815.0 0.5 +the 815.5 0.5 +Weasleys 816.0 0.5 +are 816.5 0.5 +more 817.0 0.5 +than 817.5 0.5 +a 818.0 0.5 +match 818.5 0.5 +for 819.0 0.5 +the 819.5 0.5 +Bludgers 820.0 0.5 +— 820.5 0.5 +I 821.0 0.5 +mean, 821.5 0.5 +they're 822.0 0.5 +like 822.5 0.5 +a 823.0 0.5 +pair 823.5 0.5 +of 824.0 0.5 +human 824.5 0.5 +Bludgers 825.0 0.5 +"themselves.""" 825.5 0.5 ++ 826.0 16 diff --git a/data/language/harrypotter/task-harry_run-6_events.tsv b/data/language/harrypotter/task-harry_run-6_events.tsv new file mode 100644 index 00000000..b2e10ac0 --- /dev/null +++ b/data/language/harrypotter/task-harry_run-6_events.tsv @@ -0,0 +1,1299 @@ +word format onset duration ++ 0 10 +Wood 10.0 0.5 +reached 10.5 0.5 +into 11.0 0.5 +the 11.5 0.5 +crate 12.0 0.5 +and 12.5 0.5 +took 13.0 0.5 +out 13.5 0.5 +the 14.0 0.5 +fourth 14.5 0.5 +and 15.0 0.5 +last 15.5 0.5 +ball. 16.0 0.5 +Compared 16.5 0.5 +with 17.0 0.5 +the 17.5 0.5 +Quaffle 18.0 0.5 +and 18.5 0.5 +the 19.0 0.5 +Bludgers, 19.5 0.5 +it 20.0 0.5 +was 20.5 0.5 +tiny, 21.0 0.5 +about 21.5 0.5 +the 22.0 0.5 +size 22.5 0.5 +of 23.0 0.5 +a 23.5 0.5 +large 24.0 0.5 +walnut. 24.5 0.5 +It 25.0 0.5 +was 25.5 0.5 +bright 26.0 0.5 +gold 26.5 0.5 +and 27.0 0.5 +had 27.5 0.5 +little 28.0 0.5 +fluttering 28.5 0.5 +silver 29.0 0.5 +wings. 29.5 0.5 ++ 30.0 0.5 +"""This,""" italic 30.5 0.5 +said 31.0 0.5 +Wood, 31.5 0.5 +"""is" 32.0 0.5 +the 32.5 0.5 +Golden 33.0 0.5 +Snitch, 33.5 0.5 +and 34.0 0.5 +it's 34.5 0.5 +the 35.0 0.5 +most 35.5 0.5 +important 36.0 0.5 +ball 36.5 0.5 +of 37.0 0.5 +the 37.5 0.5 +lot. 38.0 0.5 +It's 38.5 0.5 +very 39.0 0.5 +hard 39.5 0.5 +to 40.0 0.5 +catch 40.5 0.5 +because 41.0 0.5 +it's 41.5 0.5 +so 42.0 0.5 +fast 42.5 0.5 +and 43.0 0.5 +difficult 43.5 0.5 +to 44.0 0.5 +see. 44.5 0.5 +It's 45.0 0.5 +the 45.5 0.5 +Seeker's 46.0 0.5 +job 46.5 0.5 +to 47.0 0.5 +catch 47.5 0.5 +it. 48.0 0.5 +You've 48.5 0.5 +got 49.0 0.5 +to 49.5 0.5 +weave 50.0 0.5 +in 50.5 0.5 +and 51.0 0.5 +out 51.5 0.5 +of 52.0 0.5 +the 52.5 0.5 +Chasers, 53.0 0.5 +Beaters, 53.5 0.5 +Bludgers, 54.0 0.5 +and 54.5 0.5 +Quaffle 55.0 0.5 +to 55.5 0.5 +get 56.0 0.5 +it 56.5 0.5 +before 57.0 0.5 +the 57.5 0.5 +other 58.0 0.5 +team's 58.5 0.5 +Seeker, 59.0 0.5 +because 59.5 0.5 +whichever 60.0 0.5 +Seeker 60.5 0.5 +catches 61.0 0.5 +the 61.5 0.5 +Snitch 62.0 0.5 +wins 62.5 0.5 +his 63.0 0.5 +team 63.5 0.5 +an 64.0 0.5 +extra 64.5 0.5 +hundred 65.0 0.5 +and 65.5 0.5 +fifty 66.0 0.5 +points, 66.5 0.5 +so 67.0 0.5 +they 67.5 0.5 +nearly 68.0 0.5 +always 68.5 0.5 +win. 69.0 0.5 +That's 69.5 0.5 +why 70.0 0.5 +Seekers 70.5 0.5 +get 71.0 0.5 +fouled 71.5 0.5 +so 72.0 0.5 +much. 72.5 0.5 +A 73.0 0.5 +game 73.5 0.5 +of 74.0 0.5 +Quidditch 74.5 0.5 +only 75.0 0.5 +ends 75.5 0.5 +when 76.0 0.5 +the 76.5 0.5 +Snitch 77.0 0.5 +is 77.5 0.5 +caught, 78.0 0.5 +so 78.5 0.5 +it 79.0 0.5 +can 79.5 0.5 +go 80.0 0.5 +on 80.5 0.5 +for 81.0 0.5 +ages 81.5 0.5 +— 82.0 0.5 +I 82.5 0.5 +think 83.0 0.5 +the 83.5 0.5 +record 84.0 0.5 +is 84.5 0.5 +three 85.0 0.5 +months, 85.5 0.5 +they 86.0 0.5 +had 86.5 0.5 +to 87.0 0.5 +keep 87.5 0.5 +bringing 88.0 0.5 +on 88.5 0.5 +substitutes 89.0 0.5 +so 89.5 0.5 +the 90.0 0.5 +players 90.5 0.5 +could 91.0 0.5 +get 91.5 0.5 +some 92.0 0.5 +sleep. 92.5 0.5 ++ 93.0 0.5 +"""Well," 93.5 0.5 +that's 94.0 0.5 +it 94.5 0.5 +— 95.0 0.5 +any 95.5 0.5 +"questions?""" 96.0 0.5 ++ 96.5 0.5 +Harry 97.0 0.5 +shook 97.5 0.5 +his 98.0 0.5 +head. 98.5 0.5 +He 99.0 0.5 +understood 99.5 0.5 +what 100.0 0.5 +he 100.5 0.5 +had 101.0 0.5 +to 101.5 0.5 +do 102.0 0.5 +all 102.5 0.5 +right, 103.0 0.5 +it 103.5 0.5 +was 104.0 0.5 +doing 104.5 0.5 +it 105.0 0.5 +that 105.5 0.5 +was 106.0 0.5 +going 106.5 0.5 +to 107.0 0.5 +be 107.5 0.5 +the 108.0 0.5 +problem. 108.5 0.5 ++ 109.0 0.5 +"""We" 109.5 0.5 +won't 110.0 0.5 +practice 110.5 0.5 +with 111.0 0.5 +the 111.5 0.5 +Snitch 112.0 0.5 +"yet,""" 112.5 0.5 +said 113.0 0.5 +Wood, 113.5 0.5 +carefully 114.0 0.5 +shutting 114.5 0.5 +it 115.0 0.5 +back 115.5 0.5 +inside 116.0 0.5 +the 116.5 0.5 +crate, 117.0 0.5 +"""it's" 117.5 0.5 +too 118.0 0.5 +dark, 118.5 0.5 +we 119.0 0.5 +might 119.5 0.5 +lose 120.0 0.5 +it. 120.5 0.5 +Let's 121.0 0.5 +try 121.5 0.5 +you 122.0 0.5 +out 122.5 0.5 +with 123.0 0.5 +a 123.5 0.5 +few 124.0 0.5 +of 124.5 0.5 +"these.""" 125.0 0.5 ++ 125.5 0.5 +He 126.0 0.5 +pulled 126.5 0.5 +a 127.0 0.5 +bag 127.5 0.5 +of 128.0 0.5 +ordinary 128.5 0.5 +golf 129.0 0.5 +balls 129.5 0.5 +out 130.0 0.5 +of 130.5 0.5 +his 131.0 0.5 +pocket 131.5 0.5 +and 132.0 0.5 +a 132.5 0.5 +few 133.0 0.5 +minutes 133.5 0.5 +later, 134.0 0.5 +he 134.5 0.5 +and 135.0 0.5 +Harry 135.5 0.5 +were 136.0 0.5 +up 136.5 0.5 +in 137.0 0.5 +the 137.5 0.5 +air, 138.0 0.5 +Wood 138.5 0.5 +throwing 139.0 0.5 +the 139.5 0.5 +golf 140.0 0.5 +balls 140.5 0.5 +as 141.0 0.5 +hard 141.5 0.5 +as 142.0 0.5 +he 142.5 0.5 +could 143.0 0.5 +in 143.5 0.5 +every 144.0 0.5 +direction 144.5 0.5 +for 145.0 0.5 +Harry 145.5 0.5 +to 146.0 0.5 +catch. 146.5 0.5 ++ 147.0 0.5 +Harry 147.5 0.5 +didn't 148.0 0.5 +miss 148.5 0.5 +a 149.0 0.5 +single 149.5 0.5 +one, 150.0 0.5 +and 150.5 0.5 +Wood 151.0 0.5 +was 151.5 0.5 +delighted. 152.0 0.5 +After 152.5 0.5 +half 153.0 0.5 +an 153.5 0.5 +hour, 154.0 0.5 +night 154.5 0.5 +had 155.0 0.5 +really 155.5 0.5 +fallen 156.0 0.5 +and 156.5 0.5 +they 157.0 0.5 +couldn't 157.5 0.5 +carry 158.0 0.5 +on. 158.5 0.5 ++ 159.0 0.5 +"""That" 159.5 0.5 +Quidditch 160.0 0.5 +cup'll 160.5 0.5 +have 161.0 0.5 +our 161.5 0.5 +name 162.0 0.5 +on 162.5 0.5 +it 163.0 0.5 +this 163.5 0.5 +"year,""" 164.0 0.5 +said 164.5 0.5 +Wood 165.0 0.5 +happily 165.5 0.5 +as 166.0 0.5 +they 166.5 0.5 +trudged 167.0 0.5 +back 167.5 0.5 +up 168.0 0.5 +to 168.5 0.5 +the 169.0 0.5 +castle. 169.5 0.5 +"""I" 170.0 0.5 +wouldn't 170.5 0.5 +be 171.0 0.5 +surprised 171.5 0.5 +if 172.0 0.5 +you 172.5 0.5 +turn 173.0 0.5 +out 173.5 0.5 +better 174.0 0.5 +than 174.5 0.5 +Charlie 175.0 0.5 +Weasley, 175.5 0.5 +and 176.0 0.5 +he 176.5 0.5 +could 177.0 0.5 +have 177.5 0.5 +played 178.0 0.5 +for 178.5 0.5 +England 179.0 0.5 +if 179.5 0.5 +he 180.0 0.5 +hadn't 180.5 0.5 +gone 181.0 0.5 +off 181.5 0.5 +chasing 182.0 0.5 +"dragons.""" 182.5 0.5 ++ 183.0 0.5 +Perhaps 183.5 0.5 +it 184.0 0.5 +was 184.5 0.5 +because 185.0 0.5 +he 185.5 0.5 +was 186.0 0.5 +now 186.5 0.5 +so 187.0 0.5 +busy, 187.5 0.5 +what 188.0 0.5 +with 188.5 0.5 +Quidditch 189.0 0.5 +practice 189.5 0.5 +three 190.0 0.5 +evenings 190.5 0.5 +a 191.0 0.5 +week 191.5 0.5 +on 192.0 0.5 +top 192.5 0.5 +of 193.0 0.5 +all 193.5 0.5 +his 194.0 0.5 +homework, 194.5 0.5 +but 195.0 0.5 +Harry 195.5 0.5 +could 196.0 0.5 +hardly 196.5 0.5 +believe 197.0 0.5 +it 197.5 0.5 +when 198.0 0.5 +he 198.5 0.5 +realized 199.0 0.5 +that 199.5 0.5 +he'd 200.0 0.5 +already 200.5 0.5 +been 201.0 0.5 +at 201.5 0.5 +Hogwarts 202.0 0.5 +two 202.5 0.5 +months. 203.0 0.5 +The 203.5 0.5 +castle 204.0 0.5 +felt 204.5 0.5 +more 205.0 0.5 +like 205.5 0.5 +home 206.0 0.5 +than 206.5 0.5 +Privet 207.0 0.5 +Drive 207.5 0.5 +ever 208.0 0.5 +had. 208.5 0.5 +His 209.0 0.5 +lessons, 209.5 0.5 +too, 210.0 0.5 +were 210.5 0.5 +becoming 211.0 0.5 +more 211.5 0.5 +and 212.0 0.5 +more 212.5 0.5 +interesting 213.0 0.5 +now 213.5 0.5 +that 214.0 0.5 +they 214.5 0.5 +had 215.0 0.5 +mastered 215.5 0.5 +the 216.0 0.5 +basics. 216.5 0.5 ++ 217.0 0.5 +On 217.5 0.5 +Halloween 218.0 0.5 +morning 218.5 0.5 +they 219.0 0.5 +woke 219.5 0.5 +to 220.0 0.5 +the 220.5 0.5 +delicious 221.0 0.5 +smell 221.5 0.5 +of 222.0 0.5 +baking 222.5 0.5 +pumpkin 223.0 0.5 +wafting 223.5 0.5 +through 224.0 0.5 +the 224.5 0.5 +corridors. 225.0 0.5 +Even 225.5 0.5 +better, 226.0 0.5 +Professor 226.5 0.5 +Flitwick 227.0 0.5 +announced 227.5 0.5 +in 228.0 0.5 +Charms 228.5 0.5 +that 229.0 0.5 +he 229.5 0.5 +thought 230.0 0.5 +they 230.5 0.5 +were 231.0 0.5 +ready 231.5 0.5 +to 232.0 0.5 +start 232.5 0.5 +making 233.0 0.5 +objects 233.5 0.5 +fly, 234.0 0.5 +something 234.5 0.5 +they 235.0 0.5 +had 235.5 0.5 +all 236.0 0.5 +been 236.5 0.5 +dying 237.0 0.5 +to 237.5 0.5 +try 238.0 0.5 +since 238.5 0.5 +they'd 239.0 0.5 +seen 239.5 0.5 +him 240.0 0.5 +make 240.5 0.5 +Neville's 241.0 0.5 +toad 241.5 0.5 +zoom 242.0 0.5 +around 242.5 0.5 +the 243.0 0.5 +classroom. 243.5 0.5 +Professor 244.0 0.5 +Flitwick 244.5 0.5 +put 245.0 0.5 +the 245.5 0.5 +class 246.0 0.5 +into 246.5 0.5 +pairs 247.0 0.5 +to 247.5 0.5 +practice. 248.0 0.5 +Harry's 248.5 0.5 +partner 249.0 0.5 +was 249.5 0.5 +Seamus 250.0 0.5 +Finnigan 250.5 0.5 +(which 251.0 0.5 +was 251.5 0.5 +a 252.0 0.5 +relief, 252.5 0.5 +because 253.0 0.5 +Neville 253.5 0.5 +had 254.0 0.5 +been 254.5 0.5 +trying 255.0 0.5 +to 255.5 0.5 +catch 256.0 0.5 +his 256.5 0.5 +eye). 257.0 0.5 +Ron, 257.5 0.5 +however, 258.0 0.5 +was 258.5 0.5 +to 259.0 0.5 +be 259.5 0.5 +working 260.0 0.5 +with 260.5 0.5 +Hermione 261.0 0.5 +Granger. 261.5 0.5 +It 262.0 0.5 +was 262.5 0.5 +hard 263.0 0.5 +to 263.5 0.5 +tell 264.0 0.5 +whether 264.5 0.5 +Ron 265.0 0.5 +or 265.5 0.5 +Hermione 266.0 0.5 +was 266.5 0.5 +angrier 267.0 0.5 +about 267.5 0.5 +this. 268.0 0.5 +She 268.5 0.5 +hadn't 269.0 0.5 +spoken 269.5 0.5 +to 270.0 0.5 +either 270.5 0.5 +of 271.0 0.5 +them 271.5 0.5 +since 272.0 0.5 +the 272.5 0.5 +day 273.0 0.5 +Harry's 273.5 0.5 +broomstick 274.0 0.5 +had 274.5 0.5 +arrived. 275.0 0.5 ++ 275.5 0.5 +"""Now," 276.0 0.5 +don't 276.5 0.5 +forget 277.0 0.5 +that 277.5 0.5 +nice 278.0 0.5 +wrist 278.5 0.5 +movement 279.0 0.5 +we've 279.5 0.5 +been 280.0 0.5 +"practicing!""" 280.5 0.5 +squeaked 281.0 0.5 +Professor 281.5 0.5 +Flitwick, 282.0 0.5 +perched 282.5 0.5 +on 283.0 0.5 +top 283.5 0.5 +of 284.0 0.5 +his 284.5 0.5 +pile 285.0 0.5 +of 285.5 0.5 +books 286.0 0.5 +as 286.5 0.5 +usual. 287.0 0.5 +"""Swish" 287.5 0.5 +and 288.0 0.5 +flick, 288.5 0.5 +remember, 289.0 0.5 +swish 289.5 0.5 +and 290.0 0.5 +flick. 290.5 0.5 +And 291.0 0.5 +saying 291.5 0.5 +the 292.0 0.5 +magic 292.5 0.5 +words 293.0 0.5 +properly 293.5 0.5 +is 294.0 0.5 +very 294.5 0.5 +important, 295.0 0.5 +too 295.5 0.5 +— 296.0 0.5 +never 296.5 0.5 +forget 297.0 0.5 +Wizard 297.5 0.5 +Baruffio, 298.0 0.5 +who 298.5 0.5 +said 299.0 0.5 +'s' 299.5 0.5 +instead 300.0 0.5 +of 300.5 0.5 +'f' 301.0 0.5 +and 301.5 0.5 +found 302.0 0.5 +himself 302.5 0.5 +on 303.0 0.5 +the 303.5 0.5 +floor 304.0 0.5 +with 304.5 0.5 +a 305.0 0.5 +buffalo 305.5 0.5 +on 306.0 0.5 +his 306.5 0.5 +"chest.""" 307.0 0.5 ++ 307.5 0.5 +It 308.0 0.5 +was 308.5 0.5 +very 309.0 0.5 +difficult. 309.5 0.5 +Harry 310.0 0.5 +and 310.5 0.5 +Seamus 311.0 0.5 +swished 311.5 0.5 +and 312.0 0.5 +flicked, 312.5 0.5 +but 313.0 0.5 +the 313.5 0.5 +feather 314.0 0.5 +they 314.5 0.5 +were 315.0 0.5 +supposed 315.5 0.5 +to 316.0 0.5 +be 316.5 0.5 +sending 317.0 0.5 +skyward 317.5 0.5 +just 318.0 0.5 +lay 318.5 0.5 +on 319.0 0.5 +the 319.5 0.5 +desktop. 320.0 0.5 +Seamus 320.5 0.5 +got 321.0 0.5 +so 321.5 0.5 +impatient 322.0 0.5 +that 322.5 0.5 +he 323.0 0.5 +prodded 323.5 0.5 +it 324.0 0.5 +with 324.5 0.5 +his 325.0 0.5 +wand 325.5 0.5 +and 326.0 0.5 +set 326.5 0.5 +fire 327.0 0.5 +to 327.5 0.5 +it 328.0 0.5 +— 328.5 0.5 +Harry 329.0 0.5 +had 329.5 0.5 +to 330.0 0.5 +put 330.5 0.5 +it 331.0 0.5 +out 331.5 0.5 +with 332.0 0.5 +his 332.5 0.5 +hat. 333.0 0.5 ++ 333.5 0.5 +Ron, 334.0 0.5 +at 334.5 0.5 +the 335.0 0.5 +next 335.5 0.5 +table, 336.0 0.5 +wasn't 336.5 0.5 +having 337.0 0.5 +much 337.5 0.5 +more 338.0 0.5 +luck. 338.5 0.5 ++ 339.0 0.5 +"""Wingardium" italic 339.5 0.5 +"Leviosa!""" italic 340.0 0.5 +he 340.5 0.5 +shouted, 341.0 0.5 +waving 341.5 0.5 +his 342.0 0.5 +long 342.5 0.5 +arms 343.0 0.5 +like 343.5 0.5 +a 344.0 0.5 +windmill. 344.5 0.5 ++ 345.0 0.5 +"""You're" 345.5 0.5 +saying 346.0 0.5 +it 346.5 0.5 +"wrong,""" 347.0 0.5 +Harry 347.5 0.5 +heard 348.0 0.5 +Hermione 348.5 0.5 +snap. 349.0 0.5 +"""It's" 349.5 0.5 +Wing-gar-dium italic 350.0 0.5 +Levi-o-sa, italic 350.5 0.5 +make 351.0 0.5 +the 351.5 0.5 +'gar' 352.0 0.5 +nice 352.5 0.5 +and 353.0 0.5 +"long.""" 353.5 0.5 ++ 354.0 0.5 +"""You" 354.5 0.5 +do 355.0 0.5 +it, 355.5 0.5 +then, 356.0 0.5 +if 356.5 0.5 +you're 357.0 0.5 +so 357.5 0.5 +"clever,""" 358.0 0.5 +Ron 358.5 0.5 +snarled. 359.0 0.5 ++ 359.5 0.5 +Hermione 360.0 0.5 +rolled 360.5 0.5 +up 361.0 0.5 +the 361.5 0.5 +sleeves 362.0 0.5 +of 362.5 0.5 +her 363.0 0.5 +gown, 363.5 0.5 +flicked 364.0 0.5 +her 364.5 0.5 +wand, 365.0 0.5 +and 365.5 0.5 +said, 366.0 0.5 +"""Wingardium" italic 366.5 0.5 +"Leviosa!""" italic 367.0 0.5 ++ 367.5 0.5 +Their 368.0 0.5 +feather 368.5 0.5 +rose 369.0 0.5 +off 369.5 0.5 +the 370.0 0.5 +desk 370.5 0.5 +and 371.0 0.5 +hovered 371.5 0.5 +about 372.0 0.5 +four 372.5 0.5 +feet 373.0 0.5 +above 373.5 0.5 +their 374.0 0.5 +heads. 374.5 0.5 ++ 375.0 0.5 +"""Oh," 375.5 0.5 +well 376.0 0.5 +"done!""" 376.5 0.5 +cried 377.0 0.5 +Professor 377.5 0.5 +Flitwick, 378.0 0.5 +clapping. 378.5 0.5 +"""Everyone" 379.0 0.5 +see 379.5 0.5 +here, 380.0 0.5 +Miss 380.5 0.5 +Granger's 381.0 0.5 +done 381.5 0.5 +"it!""" 382.0 0.5 ++ 382.5 0.5 +Ron 383.0 0.5 +was 383.5 0.5 +in 384.0 0.5 +a 384.5 0.5 +very 385.0 0.5 +bad 385.5 0.5 +mood 386.0 0.5 +by 386.5 0.5 +the 387.0 0.5 +end 387.5 0.5 +of 388.0 0.5 +the 388.5 0.5 +class. 389.0 0.5 ++ 389.5 0.5 +"""It's" 390.0 0.5 +no 390.5 0.5 +wonder 391.0 0.5 +no 391.5 0.5 +one 392.0 0.5 +can 392.5 0.5 +stand 393.0 0.5 +"her,""" 393.5 0.5 +he 394.0 0.5 +said 394.5 0.5 +to 395.0 0.5 +Harry 395.5 0.5 +as 396.0 0.5 +they 396.5 0.5 +pushed 397.0 0.5 +their 397.5 0.5 +way 398.0 0.5 +into 398.5 0.5 +the 399.0 0.5 +crowded 399.5 0.5 +corridor, 400.0 0.5 +"""she's" 400.5 0.5 +a 401.0 0.5 +nightmare, 401.5 0.5 +honestly. 402.0 0.5 +"""" 402.5 0.5 ++ 403.0 0.5 +Someone 403.5 0.5 +knocked 404.0 0.5 +into 404.5 0.5 +Harry 405.0 0.5 +as 405.5 0.5 +they 406.0 0.5 +hurried 406.5 0.5 +past 407.0 0.5 +him. 407.5 0.5 +It 408.0 0.5 +was 408.5 0.5 +Hermione. 409.0 0.5 +Harry 409.5 0.5 +caught 410.0 0.5 +a 410.5 0.5 +glimpse 411.0 0.5 +of 411.5 0.5 +her 412.0 0.5 +face 412.5 0.5 +— 413.0 0.5 +and 413.5 0.5 +was 414.0 0.5 +startled 414.5 0.5 +to 415.0 0.5 +see 415.5 0.5 +that 416.0 0.5 +she 416.5 0.5 +was 417.0 0.5 +in 417.5 0.5 +tears. 418.0 0.5 ++ 418.5 0.5 +"""I" 419.0 0.5 +think 419.5 0.5 +she 420.0 0.5 +heard 420.5 0.5 +"you.""" 421.0 0.5 ++ 421.5 0.5 +"""So?""" 422.0 0.5 +said 422.5 0.5 +Ron, 423.0 0.5 +but 423.5 0.5 +he 424.0 0.5 +looked 424.5 0.5 +a 425.0 0.5 +bit 425.5 0.5 +uncomfortable. 426.0 0.5 +"""She" 426.5 0.5 +must've 427.0 0.5 +noticed 427.5 0.5 +she's 428.0 0.5 +got 428.5 0.5 +no 429.0 0.5 +"friends.""" 429.5 0.5 ++ 430.0 0.5 +Hermione 430.5 0.5 +didn't 431.0 0.5 +turn 431.5 0.5 +up 432.0 0.5 +for 432.5 0.5 +the 433.0 0.5 +next 433.5 0.5 +class 434.0 0.5 +and 434.5 0.5 +wasn't 435.0 0.5 +seen 435.5 0.5 +all 436.0 0.5 +afternoon. 436.5 0.5 +On 437.0 0.5 +their 437.5 0.5 +way 438.0 0.5 +down 438.5 0.5 +to 439.0 0.5 +the 439.5 0.5 +Great 440.0 0.5 +Hall 440.5 0.5 +for 441.0 0.5 +the 441.5 0.5 +Halloween 442.0 0.5 +feast, 442.5 0.5 +Harry 443.0 0.5 +and 443.5 0.5 +Ron 444.0 0.5 +overheard 444.5 0.5 +Parvati 445.0 0.5 +Patil 445.5 0.5 +telling 446.0 0.5 +her 446.5 0.5 +friend 447.0 0.5 +Lavender 447.5 0.5 +that 448.0 0.5 +Hermione 448.5 0.5 +was 449.0 0.5 +crying 449.5 0.5 +in 450.0 0.5 +the 450.5 0.5 +girls' 451.0 0.5 +bathroom 451.5 0.5 +and 452.0 0.5 +wanted 452.5 0.5 +to 453.0 0.5 +be 453.5 0.5 +left 454.0 0.5 +alone. 454.5 0.5 +Ron 455.0 0.5 +looked 455.5 0.5 +still 456.0 0.5 +more 456.5 0.5 +awkward 457.0 0.5 +at 457.5 0.5 +this, 458.0 0.5 +but 458.5 0.5 +a 459.0 0.5 +moment 459.5 0.5 +later 460.0 0.5 +they 460.5 0.5 +had 461.0 0.5 +entered 461.5 0.5 +the 462.0 0.5 +Great 462.5 0.5 +Hall, 463.0 0.5 +where 463.5 0.5 +the 464.0 0.5 +Halloween 464.5 0.5 +decorations 465.0 0.5 +put 465.5 0.5 +Hermione 466.0 0.5 +out 466.5 0.5 +of 467.0 0.5 +their 467.5 0.5 +minds. 468.0 0.5 ++ 468.5 0.5 +A 469.0 0.5 +thousand 469.5 0.5 +live 470.0 0.5 +bats 470.5 0.5 +fluttered 471.0 0.5 +from 471.5 0.5 +the 472.0 0.5 +walls 472.5 0.5 +and 473.0 0.5 +ceiling 473.5 0.5 +while 474.0 0.5 +a 474.5 0.5 +thousand 475.0 0.5 +more 475.5 0.5 +swooped 476.0 0.5 +over 476.5 0.5 +the 477.0 0.5 +tables 477.5 0.5 +in 478.0 0.5 +low 478.5 0.5 +black 479.0 0.5 +clouds, 479.5 0.5 +making 480.0 0.5 +the 480.5 0.5 +candles 481.0 0.5 +in 481.5 0.5 +the 482.0 0.5 +pumpkins 482.5 0.5 +stutter. 483.0 0.5 +The 483.5 0.5 +feast 484.0 0.5 +appeared 484.5 0.5 +suddenly 485.0 0.5 +on 485.5 0.5 +the 486.0 0.5 +golden 486.5 0.5 +plates, 487.0 0.5 +as 487.5 0.5 +it 488.0 0.5 +had 488.5 0.5 +at 489.0 0.5 +the 489.5 0.5 +start-of-term 490.0 0.5 +banquet. 490.5 0.5 ++ 491.0 0.5 +Harry 491.5 0.5 +was 492.0 0.5 +just 492.5 0.5 +helping 493.0 0.5 +himself 493.5 0.5 +to 494.0 0.5 +a 494.5 0.5 +baked 495.0 0.5 +potato 495.5 0.5 +when 496.0 0.5 +Professor 496.5 0.5 +Quirrell 497.0 0.5 +came 497.5 0.5 +sprinting 498.0 0.5 +into 498.5 0.5 +the 499.0 0.5 +hall, 499.5 0.5 +his 500.0 0.5 +turban 500.5 0.5 +askew 501.0 0.5 +and 501.5 0.5 +terror 502.0 0.5 +on 502.5 0.5 +his 503.0 0.5 +face. 503.5 0.5 +Everyone 504.0 0.5 +stared 504.5 0.5 +as 505.0 0.5 +he 505.5 0.5 +reached 506.0 0.5 +Professor 506.5 0.5 +Dumbledore's 507.0 0.5 +chair, 507.5 0.5 +slumped 508.0 0.5 +against 508.5 0.5 +the 509.0 0.5 +table, 509.5 0.5 +and 510.0 0.5 +gasped, 510.5 0.5 +"""Troll" 511.0 0.5 +— 511.5 0.5 +in 512.0 0.5 +the 512.5 0.5 +dungeons 513.0 0.5 +— 513.5 0.5 +thought 514.0 0.5 +you 514.5 0.5 +ought 515.0 0.5 +to 515.5 0.5 +"know.""" 516.0 0.5 ++ 516.5 0.5 +He 517.0 0.5 +then 517.5 0.5 +sank 518.0 0.5 +to 518.5 0.5 +the 519.0 0.5 +floor 519.5 0.5 +in 520.0 0.5 +a 520.5 0.5 +dead 521.0 0.5 +faint. 521.5 0.5 ++ 522.0 0.5 +There 522.5 0.5 +was 523.0 0.5 +an 523.5 0.5 +uproar. 524.0 0.5 +It 524.5 0.5 +took 525.0 0.5 +several 525.5 0.5 +purple 526.0 0.5 +firecrackers 526.5 0.5 +exploding 527.0 0.5 +from 527.5 0.5 +the 528.0 0.5 +end 528.5 0.5 +of 529.0 0.5 +Professor 529.5 0.5 +Dumbledore's 530.0 0.5 +wand 530.5 0.5 +to 531.0 0.5 +bring 531.5 0.5 +silence. 532.0 0.5 ++ 532.5 0.5 +"""Prefects,""" 533.0 0.5 +he 533.5 0.5 +rumbled, 534.0 0.5 +"""lead" 534.5 0.5 +your 535.0 0.5 +Houses 535.5 0.5 +back 536.0 0.5 +to 536.5 0.5 +the 537.0 0.5 +dormitories 537.5 0.5 +"immediately!""" 538.0 0.5 ++ 538.5 0.5 +Percy 539.0 0.5 +was 539.5 0.5 +in 540.0 0.5 +his 540.5 0.5 +element. 541.0 0.5 ++ 541.5 0.5 +"""Follow" 542.0 0.5 +me! 542.5 0.5 +Stick 543.0 0.5 +together, 543.5 0.5 +first 544.0 0.5 +years! 544.5 0.5 +No 545.0 0.5 +need 545.5 0.5 +to 546.0 0.5 +fear 546.5 0.5 +the 547.0 0.5 +troll 547.5 0.5 +if 548.0 0.5 +you 548.5 0.5 +follow 549.0 0.5 +my 549.5 0.5 +orders! 550.0 0.5 +Stay 550.5 0.5 +close 551.0 0.5 +behind 551.5 0.5 +me, 552.0 0.5 +now. 552.5 0.5 +Make 553.0 0.5 +way, 553.5 0.5 +first 554.0 0.5 +years 554.5 0.5 +coming 555.0 0.5 +through! 555.5 0.5 +Excuse 556.0 0.5 +me, 556.5 0.5 +I'm 557.0 0.5 +a 557.5 0.5 +"prefect!""" 558.0 0.5 ++ 558.5 0.5 +"""How" 559.0 0.5 +could 559.5 0.5 +a 560.0 0.5 +troll 560.5 0.5 +get 561.0 0.5 +"in?""" 561.5 0.5 +Harry 562.0 0.5 +asked 562.5 0.5 +as 563.0 0.5 +they 563.5 0.5 +climbed 564.0 0.5 +the 564.5 0.5 +stairs. 565.0 0.5 ++ 565.5 0.5 +"""Don't" 566.0 0.5 +ask 566.5 0.5 +me, 567.0 0.5 +they're 567.5 0.5 +supposed 568.0 0.5 +to 568.5 0.5 +be 569.0 0.5 +really 569.5 0.5 +"stupid,""" 570.0 0.5 +said 570.5 0.5 +Ron. 571.0 0.5 +"""Maybe" 571.5 0.5 +Peeves 572.0 0.5 +let 572.5 0.5 +it 573.0 0.5 +in 573.5 0.5 +for 574.0 0.5 +a 574.5 0.5 +Halloween 575.0 0.5 +"joke.""" 575.5 0.5 ++ 576.0 0.5 +They 576.5 0.5 +passed 577.0 0.5 +different 577.5 0.5 +groups 578.0 0.5 +of 578.5 0.5 +people 579.0 0.5 +hurrying 579.5 0.5 +in 580.0 0.5 +different 580.5 0.5 +directions. 581.0 0.5 +As 581.5 0.5 +they 582.0 0.5 +jostled 582.5 0.5 +their 583.0 0.5 +way 583.5 0.5 +through 584.0 0.5 +a 584.5 0.5 +crowd 585.0 0.5 +of 585.5 0.5 +confused 586.0 0.5 +Hufflepuffs, 586.5 0.5 +Harry 587.0 0.5 +suddenly 587.5 0.5 +grabbed 588.0 0.5 +Ron's 588.5 0.5 +arm. 589.0 0.5 ++ 589.5 0.5 +"""I've" 590.0 0.5 +just 590.5 0.5 +thought 591.0 0.5 +— 591.5 0.5 +"Hermione.""" 592.0 0.5 ++ 592.5 0.5 +"""What" 593.0 0.5 +about 593.5 0.5 +"her?""" 594.0 0.5 ++ 594.5 0.5 +"""She" 595.0 0.5 +doesn't 595.5 0.5 +know 596.0 0.5 +about 596.5 0.5 +the 597.0 0.5 +"troll.""" 597.5 0.5 ++ 598.0 0.5 +Ron 598.5 0.5 +bit 599.0 0.5 +his 599.5 0.5 +lip. 600.0 0.5 ++ 600.5 0.5 +"""Oh," 601.0 0.5 +all 601.5 0.5 +"right,""" 602.0 0.5 +he 602.5 0.5 +snapped. 603.0 0.5 +"""But" 603.5 0.5 +Percy'd 604.0 0.5 +better 604.5 0.5 +not 605.0 0.5 +see 605.5 0.5 +"us.""" 606.0 0.5 ++ 606.5 0.5 +Ducking 607.0 0.5 +down, 607.5 0.5 +they 608.0 0.5 +joined 608.5 0.5 +the 609.0 0.5 +Hufflepuffs 609.5 0.5 +going 610.0 0.5 +the 610.5 0.5 +other 611.0 0.5 +way, 611.5 0.5 +slipped 612.0 0.5 +down 612.5 0.5 +a 613.0 0.5 +deserted 613.5 0.5 +side 614.0 0.5 +corridor, 614.5 0.5 +and 615.0 0.5 +hurried 615.5 0.5 +off 616.0 0.5 +toward 616.5 0.5 +the 617.0 0.5 +girls' 617.5 0.5 +bathroom. 618.0 0.5 +They 618.5 0.5 +had 619.0 0.5 +just 619.5 0.5 +turned 620.0 0.5 +the 620.5 0.5 +corner 621.0 0.5 +when 621.5 0.5 +they 622.0 0.5 +heard 622.5 0.5 +quick 623.0 0.5 +footsteps 623.5 0.5 +behind 624.0 0.5 +them. 624.5 0.5 ++ 625.0 0.5 +"""Percy!""" 625.5 0.5 +hissed 626.0 0.5 +Ron, 626.5 0.5 +pulling 627.0 0.5 +Harry 627.5 0.5 +behind 628.0 0.5 +a 628.5 0.5 +large 629.0 0.5 +stone 629.5 0.5 +griffin. 630.0 0.5 ++ 630.5 0.5 +Peering 631.0 0.5 +around 631.5 0.5 +it, 632.0 0.5 +however, 632.5 0.5 +they 633.0 0.5 +saw 633.5 0.5 +not 634.0 0.5 +Percy 634.5 0.5 +but 635.0 0.5 +Snape. 635.5 0.5 +He 636.0 0.5 +crossed 636.5 0.5 +the 637.0 0.5 +corridor 637.5 0.5 +and 638.0 0.5 +disappeared 638.5 0.5 +from 639.0 0.5 +view. 639.5 0.5 ++ 640.0 0.5 +"""What's" 640.5 0.5 +he 641.0 0.5 +"doing?""" 641.5 0.5 +Harry 642.0 0.5 +whispered. 642.5 0.5 +"""Why" 643.0 0.5 +isn't 643.5 0.5 +he 644.0 0.5 +down 644.5 0.5 +in 645.0 0.5 +the 645.5 0.5 +dungeons 646.0 0.5 +with 646.5 0.5 +the 647.0 0.5 +rest 647.5 0.5 +of 648.0 0.5 +the 648.5 0.5 +"teachers?""" 649.0 0.5 ++ 649.5 0.5 +"""Search" 650.0 0.5 +"me.""" 650.5 0.5 ++ 651.0 0.5 +Quietly 651.5 0.5 +as 652.0 0.5 +possible, 652.5 0.5 +they 653.0 0.5 +crept 653.5 0.5 +along 654.0 0.5 +the 654.5 0.5 +next 655.0 0.5 +corridor 655.5 0.5 +after 656.0 0.5 +Snape's 656.5 0.5 +fading 657.0 0.5 +footsteps. 657.5 0.5 ++ 658.0 16 diff --git a/data/language/harrypotter/task-harry_run-7_events.tsv b/data/language/harrypotter/task-harry_run-7_events.tsv new file mode 100644 index 00000000..24ec5fb0 --- /dev/null +++ b/data/language/harrypotter/task-harry_run-7_events.tsv @@ -0,0 +1,1547 @@ +word format onset duration ++ 0 10 +"""He's" 10.0 0.5 +heading 10.5 0.5 +for 11.0 0.5 +the 11.5 0.5 +third 12.0 0.5 +"floor,""" 12.5 0.5 +Harry 13.0 0.5 +said, 13.5 0.5 +but 14.0 0.5 +Ron 14.5 0.5 +held 15.0 0.5 +up 15.5 0.5 +his 16.0 0.5 +hand. 16.5 0.5 ++ 17.0 0.5 +"""Can" 17.5 0.5 +you 18.0 0.5 +smell 18.5 0.5 +"something?""" 19.0 0.5 ++ 19.5 0.5 +Harry 20.0 0.5 +sniffed 20.5 0.5 +and 21.0 0.5 +a 21.5 0.5 +foul 22.0 0.5 +stench 22.5 0.5 +reached 23.0 0.5 +his 23.5 0.5 +nostrils, 24.0 0.5 +a 24.5 0.5 +mixture 25.0 0.5 +of 25.5 0.5 +old 26.0 0.5 +socks 26.5 0.5 +and 27.0 0.5 +the 27.5 0.5 +kind 28.0 0.5 +of 28.5 0.5 +public 29.0 0.5 +toilet 29.5 0.5 +no 30.0 0.5 +one 30.5 0.5 +seems 31.0 0.5 +to 31.5 0.5 +clean. 32.0 0.5 ++ 32.5 0.5 +And 33.0 0.5 +then 33.5 0.5 +they 34.0 0.5 +heard 34.5 0.5 +it 35.0 0.5 +— 35.5 0.5 +a 36.0 0.5 +low 36.5 0.5 +grunting, 37.0 0.5 +and 37.5 0.5 +the 38.0 0.5 +shuffling 38.5 0.5 +footfalls 39.0 0.5 +of 39.5 0.5 +gigantic 40.0 0.5 +feet. 40.5 0.5 +Ron 41.0 0.5 +pointed 41.5 0.5 +— 42.0 0.5 +at 42.5 0.5 +the 43.0 0.5 +end 43.5 0.5 +of 44.0 0.5 +a 44.5 0.5 +passage 45.0 0.5 +to 45.5 0.5 +the 46.0 0.5 +left, 46.5 0.5 +something 47.0 0.5 +huge 47.5 0.5 +was 48.0 0.5 +moving 48.5 0.5 +toward 49.0 0.5 +them. 49.5 0.5 +They 50.0 0.5 +shrank 50.5 0.5 +into 51.0 0.5 +the 51.5 0.5 +shadows 52.0 0.5 +and 52.5 0.5 +watched 53.0 0.5 +as 53.5 0.5 +it 54.0 0.5 +emerged 54.5 0.5 +into 55.0 0.5 +a 55.5 0.5 +patch 56.0 0.5 +of 56.5 0.5 +moonlight. 57.0 0.5 ++ 57.5 0.5 +It 58.0 0.5 +was 58.5 0.5 +a 59.0 0.5 +horrible 59.5 0.5 +sight. 60.0 0.5 +Twelve 60.5 0.5 +feet 61.0 0.5 +tall, 61.5 0.5 +its 62.0 0.5 +skin 62.5 0.5 +was 63.0 0.5 +a 63.5 0.5 +dull, 64.0 0.5 +granite 64.5 0.5 +gray, 65.0 0.5 +its 65.5 0.5 +great 66.0 0.5 +lumpy 66.5 0.5 +body 67.0 0.5 +like 67.5 0.5 +a 68.0 0.5 +boulder 68.5 0.5 +with 69.0 0.5 +its 69.5 0.5 +small 70.0 0.5 +bald 70.5 0.5 +head 71.0 0.5 +perched 71.5 0.5 +on 72.0 0.5 +top 72.5 0.5 +like 73.0 0.5 +a 73.5 0.5 +coconut. 74.0 0.5 +It 74.5 0.5 +had 75.0 0.5 +short 75.5 0.5 +legs 76.0 0.5 +thick 76.5 0.5 +as 77.0 0.5 +tree 77.5 0.5 +trunks 78.0 0.5 +with 78.5 0.5 +flat, 79.0 0.5 +horny 79.5 0.5 +feet. 80.0 0.5 +The 80.5 0.5 +smell 81.0 0.5 +coming 81.5 0.5 +from 82.0 0.5 +it 82.5 0.5 +was 83.0 0.5 +incredible. 83.5 0.5 +It 84.0 0.5 +was 84.5 0.5 +holding 85.0 0.5 +a 85.5 0.5 +huge 86.0 0.5 +wooden 86.5 0.5 +club, 87.0 0.5 +which 87.5 0.5 +dragged 88.0 0.5 +along 88.5 0.5 +the 89.0 0.5 +floor 89.5 0.5 +because 90.0 0.5 +its 90.5 0.5 +arms 91.0 0.5 +were 91.5 0.5 +so 92.0 0.5 +long. 92.5 0.5 ++ 93.0 0.5 +The 93.5 0.5 +troll 94.0 0.5 +stopped 94.5 0.5 +next 95.0 0.5 +to 95.5 0.5 +a 96.0 0.5 +doorway 96.5 0.5 +and 97.0 0.5 +peered 97.5 0.5 +inside. 98.0 0.5 +It 98.5 0.5 +waggled 99.0 0.5 +its 99.5 0.5 +long 100.0 0.5 +ears, 100.5 0.5 +making 101.0 0.5 +up 101.5 0.5 +its 102.0 0.5 +tiny 102.5 0.5 +mind, 103.0 0.5 +then 103.5 0.5 +slouched 104.0 0.5 +slowly 104.5 0.5 +into 105.0 0.5 +the 105.5 0.5 +room. 106.0 0.5 ++ 106.5 0.5 +"""The" 107.0 0.5 +keys 107.5 0.5 +in 108.0 0.5 +the 108.5 0.5 +"lock,""" 109.0 0.5 +Harry 109.5 0.5 +muttered. 110.0 0.5 +"""We" 110.5 0.5 +could 111.0 0.5 +lock 111.5 0.5 +it 112.0 0.5 +"in.""" 112.5 0.5 ++ 113.0 0.5 +"""Good" 113.5 0.5 +"idea,""" 114.0 0.5 +said 114.5 0.5 +Ron 115.0 0.5 +nervously. 115.5 0.5 ++ 116.0 0.5 +They 116.5 0.5 +edged 117.0 0.5 +toward 117.5 0.5 +the 118.0 0.5 +open 118.5 0.5 +door, 119.0 0.5 +mouths 119.5 0.5 +dry, 120.0 0.5 +praying 120.5 0.5 +the 121.0 0.5 +troll 121.5 0.5 +wasn't 122.0 0.5 +about 122.5 0.5 +to 123.0 0.5 +come 123.5 0.5 +out 124.0 0.5 +of 124.5 0.5 +it. 125.0 0.5 +With 125.5 0.5 +one 126.0 0.5 +great 126.5 0.5 +leap, 127.0 0.5 +Harry 127.5 0.5 +managed 128.0 0.5 +to 128.5 0.5 +grab 129.0 0.5 +the 129.5 0.5 +key, 130.0 0.5 +slam 130.5 0.5 +the 131.0 0.5 +door, 131.5 0.5 +and 132.0 0.5 +lock 132.5 0.5 +it. 133.0 0.5 ++ 133.5 0.5 +"""Yes!""" italic 134.0 0.5 ++ 134.5 0.5 +Flushed 135.0 0.5 +with 135.5 0.5 +their 136.0 0.5 +victory, 136.5 0.5 +they 137.0 0.5 +started 137.5 0.5 +to 138.0 0.5 +run 138.5 0.5 +back 139.0 0.5 +up 139.5 0.5 +the 140.0 0.5 +passage, 140.5 0.5 +but 141.0 0.5 +as 141.5 0.5 +they 142.0 0.5 +reached 142.5 0.5 +the 143.0 0.5 +corner 143.5 0.5 +they 144.0 0.5 +heard 144.5 0.5 +something 145.0 0.5 +that 145.5 0.5 +made 146.0 0.5 +their 146.5 0.5 +hearts 147.0 0.5 +stop 147.5 0.5 +— 148.0 0.5 +a 148.5 0.5 +high, 149.0 0.5 +petrified 149.5 0.5 +scream 150.0 0.5 +— 150.5 0.5 +and 151.0 0.5 +it 151.5 0.5 +was 152.0 0.5 +coming 152.5 0.5 +from 153.0 0.5 +the 153.5 0.5 +chamber 154.0 0.5 +they'd 154.5 0.5 +just 155.0 0.5 +chained 155.5 0.5 +up. 156.0 0.5 ++ 156.5 0.5 +"""Oh," 157.0 0.5 +"no,""" 157.5 0.5 +said 158.0 0.5 +Ron, 158.5 0.5 +pale 159.0 0.5 +as 159.5 0.5 +the 160.0 0.5 +Bloody 160.5 0.5 +Baron. 161.0 0.5 ++ 161.5 0.5 +"""It's" 162.0 0.5 +the 162.5 0.5 +girls' 163.0 0.5 +"bathroom!""" 163.5 0.5 +Harry 164.0 0.5 +gasped. 164.5 0.5 ++ 165.0 0.5 +"""Hermione!""" italic 165.5 0.5 +they 166.0 0.5 +said 166.5 0.5 +together. 167.0 0.5 ++ 167.5 0.5 +It 168.0 0.5 +was 168.5 0.5 +the 169.0 0.5 +last 169.5 0.5 +thing 170.0 0.5 +they 170.5 0.5 +wanted 171.0 0.5 +to 171.5 0.5 +do, 172.0 0.5 +but 172.5 0.5 +what 173.0 0.5 +choice 173.5 0.5 +did 174.0 0.5 +they 174.5 0.5 +have? 175.0 0.5 +Wheeling 175.5 0.5 +around, 176.0 0.5 +they 176.5 0.5 +sprinted 177.0 0.5 +back 177.5 0.5 +to 178.0 0.5 +the 178.5 0.5 +door 179.0 0.5 +and 179.5 0.5 +turned 180.0 0.5 +the 180.5 0.5 +key, 181.0 0.5 +fumbling 181.5 0.5 +in 182.0 0.5 +their 182.5 0.5 +panic. 183.0 0.5 +Harry 183.5 0.5 +pulled 184.0 0.5 +the 184.5 0.5 +door 185.0 0.5 +open 185.5 0.5 +and 186.0 0.5 +they 186.5 0.5 +ran 187.0 0.5 +inside. 187.5 0.5 ++ 188.0 0.5 +Hermione 188.5 0.5 +Granger 189.0 0.5 +was 189.5 0.5 +shrinking 190.0 0.5 +against 190.5 0.5 +the 191.0 0.5 +wall 191.5 0.5 +opposite, 192.0 0.5 +looking 192.5 0.5 +as 193.0 0.5 +if 193.5 0.5 +she 194.0 0.5 +was 194.5 0.5 +about 195.0 0.5 +to 195.5 0.5 +faint. 196.0 0.5 +The 196.5 0.5 +troll 197.0 0.5 +was 197.5 0.5 +advancing 198.0 0.5 +on 198.5 0.5 +her, 199.0 0.5 +knocking 199.5 0.5 +the 200.0 0.5 +sinks 200.5 0.5 +off 201.0 0.5 +the 201.5 0.5 +walls 202.0 0.5 +as 202.5 0.5 +it 203.0 0.5 +went. 203.5 0.5 ++ 204.0 0.5 +"""Confuse" 204.5 0.5 +"it!""" 205.0 0.5 +Harry 205.5 0.5 +said 206.0 0.5 +desperately 206.5 0.5 +to 207.0 0.5 +Ron, 207.5 0.5 +and, 208.0 0.5 +seizing 208.5 0.5 +a 209.0 0.5 +tap, 209.5 0.5 +he 210.0 0.5 +threw 210.5 0.5 +it 211.0 0.5 +as 211.5 0.5 +hard 212.0 0.5 +as 212.5 0.5 +he 213.0 0.5 +could 213.5 0.5 +against 214.0 0.5 +the 214.5 0.5 +wall. 215.0 0.5 ++ 215.5 0.5 +The 216.0 0.5 +troll 216.5 0.5 +stopped 217.0 0.5 +a 217.5 0.5 +few 218.0 0.5 +feet 218.5 0.5 +from 219.0 0.5 +Hermione. 219.5 0.5 +It 220.0 0.5 +lumbered 220.5 0.5 +around, 221.0 0.5 +blinking 221.5 0.5 +stupidly, 222.0 0.5 +to 222.5 0.5 +see 223.0 0.5 +what 223.5 0.5 +had 224.0 0.5 +made 224.5 0.5 +the 225.0 0.5 +noise. 225.5 0.5 +Its 226.0 0.5 +mean 226.5 0.5 +little 227.0 0.5 +eyes 227.5 0.5 +saw 228.0 0.5 +Harry. 228.5 0.5 +It 229.0 0.5 +hesitated, 229.5 0.5 +then 230.0 0.5 +made 230.5 0.5 +for 231.0 0.5 +him 231.5 0.5 +instead, 232.0 0.5 +lifting 232.5 0.5 +its 233.0 0.5 +club 233.5 0.5 +as 234.0 0.5 +it 234.5 0.5 +went. 235.0 0.5 ++ 235.5 0.5 +"""Oy," 236.0 0.5 +"pea-brain!""" 236.5 0.5 +yelled 237.0 0.5 +Ron 237.5 0.5 +from 238.0 0.5 +the 238.5 0.5 +other 239.0 0.5 +side 239.5 0.5 +of 240.0 0.5 +the 240.5 0.5 +chamber, 241.0 0.5 +and 241.5 0.5 +he 242.0 0.5 +threw 242.5 0.5 +a 243.0 0.5 +metal 243.5 0.5 +pipe 244.0 0.5 +at 244.5 0.5 +it. 245.0 0.5 +The 245.5 0.5 +troll 246.0 0.5 +didn't 246.5 0.5 +even 247.0 0.5 +seem 247.5 0.5 +to 248.0 0.5 +notice 248.5 0.5 +the 249.0 0.5 +pipe 249.5 0.5 +hitting 250.0 0.5 +its 250.5 0.5 +shoulder, 251.0 0.5 +but 251.5 0.5 +it 252.0 0.5 +heard 252.5 0.5 +the 253.0 0.5 +yell 253.5 0.5 +and 254.0 0.5 +paused 254.5 0.5 +again, 255.0 0.5 +turning 255.5 0.5 +its 256.0 0.5 +ugly 256.5 0.5 +snout 257.0 0.5 +toward 257.5 0.5 +Ron 258.0 0.5 +instead, 258.5 0.5 +giving 259.0 0.5 +Harry 259.5 0.5 +time 260.0 0.5 +to 260.5 0.5 +run 261.0 0.5 +around 261.5 0.5 +it. 262.0 0.5 ++ 262.5 0.5 +"""Come" 263.0 0.5 +on, 263.5 0.5 +run, 264.0 0.5 +"run!""" italic 264.5 0.5 +Harry 265.0 0.5 +yelled 265.5 0.5 +at 266.0 0.5 +Hermione, 266.5 0.5 +trying 267.0 0.5 +to 267.5 0.5 +pull 268.0 0.5 +her 268.5 0.5 +toward 269.0 0.5 +the 269.5 0.5 +door, 270.0 0.5 +but 270.5 0.5 +she 271.0 0.5 +couldn't 271.5 0.5 +move, 272.0 0.5 +she 272.5 0.5 +was 273.0 0.5 +still 273.5 0.5 +flat 274.0 0.5 +against 274.5 0.5 +the 275.0 0.5 +wall, 275.5 0.5 +her 276.0 0.5 +mouth 276.5 0.5 +open 277.0 0.5 +with 277.5 0.5 +terror. 278.0 0.5 ++ 278.5 0.5 +The 279.0 0.5 +shouting 279.5 0.5 +and 280.0 0.5 +the 280.5 0.5 +echoes 281.0 0.5 +seemed 281.5 0.5 +to 282.0 0.5 +be 282.5 0.5 +driving 283.0 0.5 +the 283.5 0.5 +troll 284.0 0.5 +berserk. 284.5 0.5 +It 285.0 0.5 +roared 285.5 0.5 +again 286.0 0.5 +and 286.5 0.5 +started 287.0 0.5 +toward 287.5 0.5 +Ron, 288.0 0.5 +who 288.5 0.5 +was 289.0 0.5 +nearest 289.5 0.5 +and 290.0 0.5 +had 290.5 0.5 +no 291.0 0.5 +way 291.5 0.5 +to 292.0 0.5 +escape. 292.5 0.5 ++ 293.0 0.5 +Harry 293.5 0.5 +then 294.0 0.5 +did 294.5 0.5 +something 295.0 0.5 +that 295.5 0.5 +was 296.0 0.5 +both 296.5 0.5 +very 297.0 0.5 +brave 297.5 0.5 +and 298.0 0.5 +very 298.5 0.5 +stupid: 299.0 0.5 +He 299.5 0.5 +took 300.0 0.5 +a 300.5 0.5 +great 301.0 0.5 +running 301.5 0.5 +jump 302.0 0.5 +and 302.5 0.5 +managed 303.0 0.5 +to 303.5 0.5 +fasten 304.0 0.5 +his 304.5 0.5 +arms 305.0 0.5 +around 305.5 0.5 +the 306.0 0.5 +troll's 306.5 0.5 +neck 307.0 0.5 +from 307.5 0.5 +behind. 308.0 0.5 +The 308.5 0.5 +troll 309.0 0.5 +couldn't 309.5 0.5 +feel 310.0 0.5 +Harry 310.5 0.5 +hanging 311.0 0.5 +there, 311.5 0.5 +but 312.0 0.5 +even 312.5 0.5 +a 313.0 0.5 +troll 313.5 0.5 +will 314.0 0.5 +notice 314.5 0.5 +if 315.0 0.5 +you 315.5 0.5 +stick 316.0 0.5 +a 316.5 0.5 +long 317.0 0.5 +bit 317.5 0.5 +of 318.0 0.5 +wood 318.5 0.5 +up 319.0 0.5 +its 319.5 0.5 +nose, 320.0 0.5 +and 320.5 0.5 +Harry's 321.0 0.5 +wand 321.5 0.5 +had 322.0 0.5 +still 322.5 0.5 +been 323.0 0.5 +in 323.5 0.5 +his 324.0 0.5 +hand 324.5 0.5 +when 325.0 0.5 +he'd 325.5 0.5 +jumped 326.0 0.5 +— 326.5 0.5 +it 327.0 0.5 +had 327.5 0.5 +gone 328.0 0.5 +straight 328.5 0.5 +up 329.0 0.5 +one 329.5 0.5 +of 330.0 0.5 +the 330.5 0.5 +troll's 331.0 0.5 +nostrils. 331.5 0.5 ++ 332.0 0.5 +Howling 332.5 0.5 +with 333.0 0.5 +pain, 333.5 0.5 +the 334.0 0.5 +troll 334.5 0.5 +twisted 335.0 0.5 +and 335.5 0.5 +flailed 336.0 0.5 +its 336.5 0.5 +club, 337.0 0.5 +with 337.5 0.5 +Harry 338.0 0.5 +clinging 338.5 0.5 +on 339.0 0.5 +for 339.5 0.5 +dear 340.0 0.5 +life; 340.5 0.5 +any 341.0 0.5 +second, 341.5 0.5 +the 342.0 0.5 +troll 342.5 0.5 +was 343.0 0.5 +going 343.5 0.5 +to 344.0 0.5 +rip 344.5 0.5 +him 345.0 0.5 +off 345.5 0.5 +or 346.0 0.5 +catch 346.5 0.5 +him 347.0 0.5 +a 347.5 0.5 +terrible 348.0 0.5 +blow 348.5 0.5 +with 349.0 0.5 +the 349.5 0.5 +club. 350.0 0.5 ++ 350.5 0.5 +Hermione 351.0 0.5 +had 351.5 0.5 +sunk 352.0 0.5 +to 352.5 0.5 +the 353.0 0.5 +floor 353.5 0.5 +in 354.0 0.5 +fright; 354.5 0.5 +Ron 355.0 0.5 +pulled 355.5 0.5 +out 356.0 0.5 +his 356.5 0.5 +own 357.0 0.5 +wand 357.5 0.5 +— 358.0 0.5 +not 358.5 0.5 +knowing 359.0 0.5 +what 359.5 0.5 +he 360.0 0.5 +was 360.5 0.5 +going 361.0 0.5 +to 361.5 0.5 +do 362.0 0.5 +he 362.5 0.5 +heard 363.0 0.5 +himself 363.5 0.5 +cry 364.0 0.5 +the 364.5 0.5 +first 365.0 0.5 +spell 365.5 0.5 +that 366.0 0.5 +came 366.5 0.5 +into 367.0 0.5 +his 367.5 0.5 +head: 368.0 0.5 +"""Wingardium" italic 368.5 0.5 +"Leviosa!""" italic 369.0 0.5 ++ 369.5 0.5 +The 370.0 0.5 +club 370.5 0.5 +flew 371.0 0.5 +suddenly 371.5 0.5 +out 372.0 0.5 +of 372.5 0.5 +the 373.0 0.5 +troll's 373.5 0.5 +hand, 374.0 0.5 +rose 374.5 0.5 +high, 375.0 0.5 +high 375.5 0.5 +up 376.0 0.5 +into 376.5 0.5 +the 377.0 0.5 +air, 377.5 0.5 +turned 378.0 0.5 +slowly 378.5 0.5 +over 379.0 0.5 +— 379.5 0.5 +and 380.0 0.5 +dropped, 380.5 0.5 +with 381.0 0.5 +a 381.5 0.5 +sickening 382.0 0.5 +crack, 382.5 0.5 +onto 383.0 0.5 +its 383.5 0.5 +owner's 384.0 0.5 +head. 384.5 0.5 +The 385.0 0.5 +troll 385.5 0.5 +swayed 386.0 0.5 +on 386.5 0.5 +the 387.0 0.5 +spot 387.5 0.5 +and 388.0 0.5 +then 388.5 0.5 +fell 389.0 0.5 +flat 389.5 0.5 +on 390.0 0.5 +its 390.5 0.5 +face, 391.0 0.5 +with 391.5 0.5 +a 392.0 0.5 +thud 392.5 0.5 +that 393.0 0.5 +made 393.5 0.5 +the 394.0 0.5 +whole 394.5 0.5 +room 395.0 0.5 +tremble. 395.5 0.5 ++ 396.0 0.5 +Harry 396.5 0.5 +got 397.0 0.5 +to 397.5 0.5 +his 398.0 0.5 +feet. 398.5 0.5 +He 399.0 0.5 +was 399.5 0.5 +shaking 400.0 0.5 +and 400.5 0.5 +out 401.0 0.5 +of 401.5 0.5 +breath. 402.0 0.5 +Ron 402.5 0.5 +was 403.0 0.5 +standing 403.5 0.5 +there 404.0 0.5 +with 404.5 0.5 +his 405.0 0.5 +wand 405.5 0.5 +still 406.0 0.5 +raised, 406.5 0.5 +staring 407.0 0.5 +at 407.5 0.5 +what 408.0 0.5 +he 408.5 0.5 +had 409.0 0.5 +done. 409.5 0.5 ++ 410.0 0.5 +It 410.5 0.5 +was 411.0 0.5 +Hermione 411.5 0.5 +who 412.0 0.5 +spoke 412.5 0.5 +first. 413.0 0.5 ++ 413.5 0.5 +"""Is" 414.0 0.5 +it 414.5 0.5 +— 415.0 0.5 +"dead?""" 415.5 0.5 ++ 416.0 0.5 +I 416.5 0.5 +don't 417.0 0.5 +think 417.5 0.5 +"so,""" 418.0 0.5 +said 418.5 0.5 +Harry, 419.0 0.5 +I 419.5 0.5 +think 420.0 0.5 +it's 420.5 0.5 +just 421.0 0.5 +been 421.5 0.5 +knocked 422.0 0.5 +"out.""" 422.5 0.5 ++ 423.0 0.5 +He 423.5 0.5 +bent 424.0 0.5 +down 424.5 0.5 +and 425.0 0.5 +pulled 425.5 0.5 +his 426.0 0.5 +wand 426.5 0.5 +out 427.0 0.5 +of 427.5 0.5 +the 428.0 0.5 +troll's 428.5 0.5 +nose. 429.0 0.5 +It 429.5 0.5 +was 430.0 0.5 +covered 430.5 0.5 +in 431.0 0.5 +what 431.5 0.5 +looked 432.0 0.5 +like 432.5 0.5 +lumpy 433.0 0.5 +gray 433.5 0.5 +glue. 434.0 0.5 ++ 434.5 0.5 +"""Urgh" 435.0 0.5 +— 435.5 0.5 +troll 436.0 0.5 +"boogers.""" 436.5 0.5 ++ 437.0 0.5 +He 437.5 0.5 +wiped 438.0 0.5 +it 438.5 0.5 +on 439.0 0.5 +the 439.5 0.5 +troll's 440.0 0.5 +trousers. 440.5 0.5 ++ 441.0 0.5 +A 441.5 0.5 +sudden 442.0 0.5 +slamming 442.5 0.5 +and 443.0 0.5 +loud 443.5 0.5 +footsteps 444.0 0.5 +made 444.5 0.5 +the 445.0 0.5 +three 445.5 0.5 +of 446.0 0.5 +them 446.5 0.5 +look 447.0 0.5 +up. 447.5 0.5 +They 448.0 0.5 +hadn't 448.5 0.5 +realized 449.0 0.5 +what 449.5 0.5 +a 450.0 0.5 +racket 450.5 0.5 +they 451.0 0.5 +had 451.5 0.5 +been 452.0 0.5 +making, 452.5 0.5 +but 453.0 0.5 +of 453.5 0.5 +course, 454.0 0.5 +someone 454.5 0.5 +downstairs 455.0 0.5 +must 455.5 0.5 +have 456.0 0.5 +heard 456.5 0.5 +the 457.0 0.5 +crashes 457.5 0.5 +and 458.0 0.5 +the 458.5 0.5 +troll's 459.0 0.5 +roars. 459.5 0.5 +A 460.0 0.5 +moment 460.5 0.5 +later, 461.0 0.5 +Professor 461.5 0.5 +McGonagall 462.0 0.5 +had 462.5 0.5 +come 463.0 0.5 +bursting 463.5 0.5 +into 464.0 0.5 +the 464.5 0.5 +room, 465.0 0.5 +closely 465.5 0.5 +followed 466.0 0.5 +by 466.5 0.5 +Snape, 467.0 0.5 +with 467.5 0.5 +Quirrell 468.0 0.5 +bringing 468.5 0.5 +up 469.0 0.5 +the 469.5 0.5 +rear. 470.0 0.5 +Quirrell 470.5 0.5 +took 471.0 0.5 +one 471.5 0.5 +look 472.0 0.5 +at 472.5 0.5 +the 473.0 0.5 +troll, 473.5 0.5 +let 474.0 0.5 +out 474.5 0.5 +a 475.0 0.5 +faint 475.5 0.5 +whimper, 476.0 0.5 +and 476.5 0.5 +sat 477.0 0.5 +quickly 477.5 0.5 +down 478.0 0.5 +on 478.5 0.5 +a 479.0 0.5 +toilet, 479.5 0.5 +clutching 480.0 0.5 +his 480.5 0.5 +heart. 481.0 0.5 ++ 481.5 0.5 +Snape 482.0 0.5 +bent 482.5 0.5 +over 483.0 0.5 +the 483.5 0.5 +troll. 484.0 0.5 +Professor 484.5 0.5 +McGonagall 485.0 0.5 +was 485.5 0.5 +looking 486.0 0.5 +at 486.5 0.5 +Ron 487.0 0.5 +and 487.5 0.5 +Harry. 488.0 0.5 +Harry 488.5 0.5 +had 489.0 0.5 +never 489.5 0.5 +seen 490.0 0.5 +her 490.5 0.5 +look 491.0 0.5 +so 491.5 0.5 +angry. 492.0 0.5 +Her 492.5 0.5 +lips 493.0 0.5 +were 493.5 0.5 +white. 494.0 0.5 +Hopes 494.5 0.5 +of 495.0 0.5 +winning 495.5 0.5 +fifty 496.0 0.5 +points 496.5 0.5 +for 497.0 0.5 +Gryffindor 497.5 0.5 +faded 498.0 0.5 +quickly 498.5 0.5 +from 499.0 0.5 +Harry's 499.5 0.5 +mind. 500.0 0.5 ++ 500.5 0.5 +"""What" 501.0 0.5 +on 501.5 0.5 +earth 502.0 0.5 +were 502.5 0.5 +you 503.0 0.5 +thinking 503.5 0.5 +"of?""" 504.0 0.5 +said 504.5 0.5 +Professor 505.0 0.5 +McGonagall, 505.5 0.5 +with 506.0 0.5 +cold 506.5 0.5 +fury 507.0 0.5 +in 507.5 0.5 +her 508.0 0.5 +voice. 508.5 0.5 +Harry 509.0 0.5 +looked 509.5 0.5 +at 510.0 0.5 +Ron, 510.5 0.5 +who 511.0 0.5 +was 511.5 0.5 +still 512.0 0.5 +standing 512.5 0.5 +with 513.0 0.5 +his 513.5 0.5 +wand 514.0 0.5 +in 514.5 0.5 +the 515.0 0.5 +air. 515.5 0.5 +"""You're" 516.0 0.5 +lucky 516.5 0.5 +you 517.0 0.5 +weren't 517.5 0.5 +killed. 518.0 0.5 +Why 518.5 0.5 +aren't 519.0 0.5 +you 519.5 0.5 +in 520.0 0.5 +your 520.5 0.5 +"dormitory?""" 521.0 0.5 ++ 521.5 0.5 +Snape 522.0 0.5 +gave 522.5 0.5 +Harry 523.0 0.5 +a 523.5 0.5 +swift, 524.0 0.5 +piercing 524.5 0.5 +look. 525.0 0.5 +Harry 525.5 0.5 +looked 526.0 0.5 +at 526.5 0.5 +the 527.0 0.5 +floor. 527.5 0.5 +He 528.0 0.5 +wished 528.5 0.5 +Ron 529.0 0.5 +would 529.5 0.5 +put 530.0 0.5 +his 530.5 0.5 +wand 531.0 0.5 +down. 531.5 0.5 ++ 532.0 0.5 +Then 532.5 0.5 +a 533.0 0.5 +small 533.5 0.5 +voice 534.0 0.5 +came 534.5 0.5 +out 535.0 0.5 +of 535.5 0.5 +the 536.0 0.5 +shadows. 536.5 0.5 ++ 537.0 0.5 +"""Please," 537.5 0.5 +Professor 538.0 0.5 +McGonagall 538.5 0.5 +— 539.0 0.5 +they 539.5 0.5 +were 540.0 0.5 +looking 540.5 0.5 +for 541.0 0.5 +"me.""" 541.5 0.5 ++ 542.0 0.5 +"""Miss" 542.5 0.5 +"Granger!""" 543.0 0.5 ++ 543.5 0.5 +Hermione 544.0 0.5 +had 544.5 0.5 +managed 545.0 0.5 +to 545.5 0.5 +get 546.0 0.5 +to 546.5 0.5 +her 547.0 0.5 +feet 547.5 0.5 +at 548.0 0.5 +last. 548.5 0.5 ++ 549.0 0.5 +I 549.5 0.5 +went 550.0 0.5 +looking 550.5 0.5 +for 551.0 0.5 +the 551.5 0.5 +troll 552.0 0.5 +because 552.5 0.5 +I 553.0 0.5 +— 553.5 0.5 +I 554.0 0.5 +thought 554.5 0.5 +I 555.0 0.5 +could 555.5 0.5 +deal 556.0 0.5 +with 556.5 0.5 +it 557.0 0.5 +on 557.5 0.5 +my 558.0 0.5 +own 558.5 0.5 +— 559.0 0.5 +you 559.5 0.5 +know, 560.0 0.5 +because 560.5 0.5 +I've 561.0 0.5 +read 561.5 0.5 +all 562.0 0.5 +about 562.5 0.5 +"them.""" 563.0 0.5 ++ 563.5 0.5 +Ron 564.0 0.5 +dropped 564.5 0.5 +his 565.0 0.5 +wand. 565.5 0.5 +Hermione 566.0 0.5 +Granger, 566.5 0.5 +telling 567.0 0.5 +a 567.5 0.5 +downright 568.0 0.5 +lie 568.5 0.5 +to 569.0 0.5 +a 569.5 0.5 +teacher? 570.0 0.5 ++ 570.5 0.5 +"""If" 571.0 0.5 +they 571.5 0.5 +hadn't 572.0 0.5 +found 572.5 0.5 +me, 573.0 0.5 +I'd 573.5 0.5 +be 574.0 0.5 +dead 574.5 0.5 +now. 575.0 0.5 +Harry 575.5 0.5 +stuck 576.0 0.5 +his 576.5 0.5 +wand 577.0 0.5 +up 577.5 0.5 +its 578.0 0.5 +nose 578.5 0.5 +and 579.0 0.5 +Ron 579.5 0.5 +knocked 580.0 0.5 +it 580.5 0.5 +out 581.0 0.5 +with 581.5 0.5 +its 582.0 0.5 +own 582.5 0.5 +club. 583.0 0.5 +They 583.5 0.5 +didn't 584.0 0.5 +have 584.5 0.5 +time 585.0 0.5 +to 585.5 0.5 +come 586.0 0.5 +and 586.5 0.5 +fetch 587.0 0.5 +anyone. 587.5 0.5 +It 588.0 0.5 +was 588.5 0.5 +about 589.0 0.5 +to 589.5 0.5 +finish 590.0 0.5 +me 590.5 0.5 +off 591.0 0.5 +when 591.5 0.5 +they 592.0 0.5 +"arrived.""" 592.5 0.5 ++ 593.0 0.5 +Harry 593.5 0.5 +and 594.0 0.5 +Ron 594.5 0.5 +tried 595.0 0.5 +to 595.5 0.5 +look 596.0 0.5 +as 596.5 0.5 +though 597.0 0.5 +this 597.5 0.5 +story 598.0 0.5 +wasn't 598.5 0.5 +new 599.0 0.5 +to 599.5 0.5 +them. 600.0 0.5 ++ 600.5 0.5 +"""Well" 601.0 0.5 +— 601.5 0.5 +in 602.0 0.5 +that 602.5 0.5 +"case...""" 603.0 0.5 +said 603.5 0.5 +Professor 604.0 0.5 +McGonagall, 604.5 0.5 +staring 605.0 0.5 +at 605.5 0.5 +the 606.0 0.5 +three 606.5 0.5 +of 607.0 0.5 +them, 607.5 0.5 +"""Miss" 608.0 0.5 +Granger, 608.5 0.5 +you 609.0 0.5 +foolish 609.5 0.5 +girl, 610.0 0.5 +how 610.5 0.5 +could 611.0 0.5 +you 611.5 0.5 +think 612.0 0.5 +of 612.5 0.5 +tackling 613.0 0.5 +a 613.5 0.5 +mountain 614.0 0.5 +troll 614.5 0.5 +on 615.0 0.5 +your 615.5 0.5 +"own?""" 616.0 0.5 ++ 616.5 0.5 +Hermione 617.0 0.5 +hung 617.5 0.5 +her 618.0 0.5 +head. 618.5 0.5 +Harry 619.0 0.5 +was 619.5 0.5 +speechless. 620.0 0.5 +Hermione 620.5 0.5 +was 621.0 0.5 +the 621.5 0.5 +last 622.0 0.5 +person 622.5 0.5 +to 623.0 0.5 +do 623.5 0.5 +anything 624.0 0.5 +against 624.5 0.5 +the 625.0 0.5 +rules, 625.5 0.5 +and 626.0 0.5 +here 626.5 0.5 +she 627.0 0.5 +was, 627.5 0.5 +pretending 628.0 0.5 +she 628.5 0.5 +had, 629.0 0.5 +to 629.5 0.5 +get 630.0 0.5 +them 630.5 0.5 +out 631.0 0.5 +of 631.5 0.5 +trouble. 632.0 0.5 +It 632.5 0.5 +was 633.0 0.5 +as 633.5 0.5 +if 634.0 0.5 +Snape 634.5 0.5 +had 635.0 0.5 +started 635.5 0.5 +handing 636.0 0.5 +out 636.5 0.5 +sweets. 637.0 0.5 ++ 637.5 0.5 +"""Miss" 638.0 0.5 +Granger, 638.5 0.5 +five 639.0 0.5 +points 639.5 0.5 +will 640.0 0.5 +be 640.5 0.5 +taken 641.0 0.5 +from 641.5 0.5 +Gryffindor 642.0 0.5 +for 642.5 0.5 +"this,""" 643.0 0.5 +said 643.5 0.5 +Professor 644.0 0.5 +McGonagall. 644.5 0.5 +"""I'm" 645.0 0.5 +very 645.5 0.5 +disappointed 646.0 0.5 +in 646.5 0.5 +you. 647.0 0.5 +If 647.5 0.5 +you're 648.0 0.5 +not 648.5 0.5 +hurt 649.0 0.5 +at 649.5 0.5 +all, 650.0 0.5 +you'd 650.5 0.5 +better 651.0 0.5 +get 651.5 0.5 +off 652.0 0.5 +to 652.5 0.5 +Gryffindor 653.0 0.5 +tower. 653.5 0.5 +Students 654.0 0.5 +are 654.5 0.5 +finishing 655.0 0.5 +the 655.5 0.5 +feast 656.0 0.5 +in 656.5 0.5 +their 657.0 0.5 +"houses.""" 657.5 0.5 ++ 658.0 0.5 +Hermione 658.5 0.5 +left. 659.0 0.5 ++ 659.5 0.5 +Professor 660.0 0.5 +McGonagall 660.5 0.5 +turned 661.0 0.5 +to 661.5 0.5 +Harry 662.0 0.5 +and 662.5 0.5 +Ron. 663.0 0.5 ++ 663.5 0.5 +"""Well," 664.0 0.5 +I 664.5 0.5 +still 665.0 0.5 +say 665.5 0.5 +you 666.0 0.5 +were 666.5 0.5 +lucky, 667.0 0.5 +but 667.5 0.5 +not 668.0 0.5 +many 668.5 0.5 +first 669.0 0.5 +years 669.5 0.5 +could 670.0 0.5 +have 670.5 0.5 +taken 671.0 0.5 +on 671.5 0.5 +a 672.0 0.5 +full-grown 672.5 0.5 +mountain 673.0 0.5 +troll. 673.5 0.5 +You 674.0 0.5 +each 674.5 0.5 +win 675.0 0.5 +Gryffindor 675.5 0.5 +five 676.0 0.5 +points. 676.5 0.5 +Professor 677.0 0.5 +Dumbledore 677.5 0.5 +will 678.0 0.5 +be 678.5 0.5 +informed 679.0 0.5 +of 679.5 0.5 +this. 680.0 0.5 +You 680.5 0.5 +may 681.0 0.5 +"go.""" 681.5 0.5 ++ 682.0 0.5 +They 682.5 0.5 +hurried 683.0 0.5 +out 683.5 0.5 +of 684.0 0.5 +the 684.5 0.5 +chamber 685.0 0.5 +and 685.5 0.5 +didn't 686.0 0.5 +speak 686.5 0.5 +at 687.0 0.5 +all 687.5 0.5 +until 688.0 0.5 +they 688.5 0.5 +had 689.0 0.5 +climbed 689.5 0.5 +two 690.0 0.5 +floors 690.5 0.5 +up. 691.0 0.5 +It 691.5 0.5 +was 692.0 0.5 +a 692.5 0.5 +relief 693.0 0.5 +to 693.5 0.5 +be 694.0 0.5 +away 694.5 0.5 +from 695.0 0.5 +the 695.5 0.5 +smell 696.0 0.5 +of 696.5 0.5 +the 697.0 0.5 +troll, 697.5 0.5 +quite 698.0 0.5 +apart 698.5 0.5 +from 699.0 0.5 +anything 699.5 0.5 +else. 700.0 0.5 ++ 700.5 0.5 +"""We" 701.0 0.5 +should 701.5 0.5 +have 702.0 0.5 +gotten 702.5 0.5 +more 703.0 0.5 +than 703.5 0.5 +ten 704.0 0.5 +"points,""" 704.5 0.5 +Ron 705.0 0.5 +grumbled. 705.5 0.5 ++ 706.0 0.5 +"""Five," 706.5 0.5 +you 707.0 0.5 +mean, 707.5 0.5 +once 708.0 0.5 +she's 708.5 0.5 +taken 709.0 0.5 +off 709.5 0.5 +"Hermione's.""" 710.0 0.5 ++ 710.5 0.5 +"""Good" 711.0 0.5 +of 711.5 0.5 +her 712.0 0.5 +to 712.5 0.5 +get 713.0 0.5 +us 713.5 0.5 +out 714.0 0.5 +of 714.5 0.5 +trouble 715.0 0.5 +like 715.5 0.5 +"that,""" 716.0 0.5 +Ron 716.5 0.5 +admitted. 717.0 0.5 +"""Mind" 717.5 0.5 +you, 718.0 0.5 +we 718.5 0.5 +did 719.0 0.5 +save 719.5 0.5 +"her.""" 720.0 0.5 ++ 720.5 0.5 +"""She" 721.0 0.5 +might 721.5 0.5 +not 722.0 0.5 +have 722.5 0.5 +needed 723.0 0.5 +saving 723.5 0.5 +if 724.0 0.5 +we 724.5 0.5 +hadn't 725.0 0.5 +locked 725.5 0.5 +the 726.0 0.5 +thing 726.5 0.5 +in 727.0 0.5 +with 727.5 0.5 +"her,""" 728.0 0.5 +Harry 728.5 0.5 +reminded 729.0 0.5 +him. 729.5 0.5 ++ 730.0 0.5 +They 730.5 0.5 +had 731.0 0.5 +reached 731.5 0.5 +the 732.0 0.5 +portrait 732.5 0.5 +of 733.0 0.5 +the 733.5 0.5 +Fat 734.0 0.5 +Lady. 734.5 0.5 ++ 735.0 0.5 +"""Pig" 735.5 0.5 +"snout,""" 736.0 0.5 +they 736.5 0.5 +said 737.0 0.5 +and 737.5 0.5 +entered. 738.0 0.5 ++ 738.5 0.5 +The 739.0 0.5 +common 739.5 0.5 +room 740.0 0.5 +was 740.5 0.5 +packed 741.0 0.5 +and 741.5 0.5 +noisy. 742.0 0.5 +Everyone 742.5 0.5 +was 743.0 0.5 +eating 743.5 0.5 +the 744.0 0.5 +food 744.5 0.5 +that 745.0 0.5 +had 745.5 0.5 +been 746.0 0.5 +sent 746.5 0.5 +up. 747.0 0.5 +Hermione, 747.5 0.5 +however, 748.0 0.5 +stood 748.5 0.5 +alone 749.0 0.5 +by 749.5 0.5 +the 750.0 0.5 +door, 750.5 0.5 +waiting 751.0 0.5 +for 751.5 0.5 +them. 752.0 0.5 +There 752.5 0.5 +was 753.0 0.5 +a 753.5 0.5 +very 754.0 0.5 +embarrassed 754.5 0.5 +pause. 755.0 0.5 +Then, 755.5 0.5 +none 756.0 0.5 +of 756.5 0.5 +them 757.0 0.5 +looking 757.5 0.5 +at 758.0 0.5 +each 758.5 0.5 +other, 759.0 0.5 +they 759.5 0.5 +all 760.0 0.5 +said 760.5 0.5 +"""Thanks,""" 761.0 0.5 +and 761.5 0.5 +hurried 762.0 0.5 +off 762.5 0.5 +to 763.0 0.5 +get 763.5 0.5 +plates. 764.0 0.5 ++ 764.5 0.5 +But 765.0 0.5 +from 765.5 0.5 +that 766.0 0.5 +moment 766.5 0.5 +on, 767.0 0.5 +Hermione 767.5 0.5 +Granger 768.0 0.5 +became 768.5 0.5 +their 769.0 0.5 +friend. 769.5 0.5 +There 770.0 0.5 +are 770.5 0.5 +some 771.0 0.5 +things 771.5 0.5 +you 772.0 0.5 +can't 772.5 0.5 +share 773.0 0.5 +without 773.5 0.5 +ending 774.0 0.5 +up 774.5 0.5 +liking 775.0 0.5 +each 775.5 0.5 +other, 776.0 0.5 +and 776.5 0.5 +knocking 777.0 0.5 +out 777.5 0.5 +a 778.0 0.5 +twelve-foot 778.5 0.5 +mountain 779.0 0.5 +troll 779.5 0.5 +is 780.0 0.5 +one 780.5 0.5 +of 781.0 0.5 +them. 781.5 0.5 ++ 782.0 16 diff --git a/data/things/images b/data/things/images new file mode 160000 index 00000000..f5dea7c5 --- /dev/null +++ b/data/things/images @@ -0,0 +1 @@ +Subproject commit f5dea7c5264bd81fb58c60e1a6afb0474db511fb diff --git a/data/things/memory_designs/.gitkeep b/data/things/memory_designs/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/data/things/pngs/fixation_cross.png b/data/things/pngs/fixation_cross.png new file mode 120000 index 00000000..e499b9aa --- /dev/null +++ b/data/things/pngs/fixation_cross.png @@ -0,0 +1 @@ +../../../.git/annex/objects/6p/MF/MD5E-s3730--af10957c81bd6f4ec6fda46976b2f92c.png/MD5E-s3730--af10957c81bd6f4ec6fda46976b2f92c.png \ No newline at end of file diff --git a/data/things/pngs/fixation_cross_old.png b/data/things/pngs/fixation_cross_old.png new file mode 120000 index 00000000..dcc0cae0 --- /dev/null +++ b/data/things/pngs/fixation_cross_old.png @@ -0,0 +1 @@ +../../../.git/annex/objects/wq/2f/MD5E-s15446--ffafa1de6e0d71a131bcf618f812f550.png/MD5E-s15446--ffafa1de6e0d71a131bcf618f812f550.png \ No newline at end of file diff --git a/data/things/pngs/response_mapping.png b/data/things/pngs/response_mapping.png new file mode 120000 index 00000000..cd78247a --- /dev/null +++ b/data/things/pngs/response_mapping.png @@ -0,0 +1 @@ +../../../.git/annex/objects/vj/6k/MD5E-s1162--ecc582b653454b9f8efb57c8cd9ca019.png/MD5E-s1162--ecc582b653454b9f8efb57c8cd9ca019.png \ No newline at end of file diff --git a/data/things/pngs/response_mapping.svg b/data/things/pngs/response_mapping.svg new file mode 120000 index 00000000..dfa2ccc1 --- /dev/null +++ b/data/things/pngs/response_mapping.svg @@ -0,0 +1 @@ +../../../.git/annex/objects/ww/7P/MD5E-s4331--b225b41a96c216781817484b5d9d1e4f.svg/MD5E-s4331--b225b41a96c216781817484b5d9d1e4f.svg \ No newline at end of file diff --git a/data/things/pngs/response_mapping2.png b/data/things/pngs/response_mapping2.png new file mode 120000 index 00000000..54521960 --- /dev/null +++ b/data/things/pngs/response_mapping2.png @@ -0,0 +1 @@ +../../../.git/annex/objects/jF/65/MD5E-s11203--2f5f3b8566ac2e511c9d2b25ca864c21.png/MD5E-s11203--2f5f3b8566ac2e511c9d2b25ca864c21.png \ No newline at end of file diff --git a/data/things/pngs/response_mapping2.svg b/data/things/pngs/response_mapping2.svg new file mode 120000 index 00000000..211139db --- /dev/null +++ b/data/things/pngs/response_mapping2.svg @@ -0,0 +1 @@ +../../../.git/annex/objects/P5/JJ/MD5E-s9961--7bb7949c37aeb8dc26bb5c67658080fa.svg/MD5E-s9961--7bb7949c37aeb8dc26bb5c67658080fa.svg \ No newline at end of file diff --git a/data/things/pngs/response_mapping3.png b/data/things/pngs/response_mapping3.png new file mode 120000 index 00000000..3c303c33 --- /dev/null +++ b/data/things/pngs/response_mapping3.png @@ -0,0 +1 @@ +../../../.git/annex/objects/Mx/Mx/MD5E-s4934--406900a974a48fdccb5f314a200b4723.png/MD5E-s4934--406900a974a48fdccb5f314a200b4723.png \ No newline at end of file diff --git a/data/things/snes_for_things_mem.gamecontroller.amgp b/data/things/snes_for_things_mem.gamecontroller.amgp new file mode 100644 index 00000000..bff7c69a --- /dev/null +++ b/data/things/snes_for_things_mem.gamecontroller.amgp @@ -0,0 +1,101 @@ + + + + Retrolink SNES Controller + + 0300000079000000110000001001000012117 + + + + + + + + + + L Stick + R Stick + + + + + + + + 0x1000012 + keyboard + + + + + + + 0x1000014 + keyboard + + + + + + + 0x1000013 + keyboard + + + + + + + 0x1000015 + keyboard + + + + + + 0 + -32767 + 32767 + positivehalf + + + 0 + -32767 + 32767 + positivehalf + + + + + + + + diff --git a/data/things/stimuli b/data/things/stimuli new file mode 160000 index 00000000..d0150953 --- /dev/null +++ b/data/things/stimuli @@ -0,0 +1 @@ +Subproject commit d0150953b62f327b8e2a782ffeb82daec740a7b5 diff --git a/data/videogames/mario b/data/videogames/mario new file mode 160000 index 00000000..cf2701d0 --- /dev/null +++ b/data/videogames/mario @@ -0,0 +1 @@ +Subproject commit cf2701d004d3fcf9525cd9f1a56d558c729a4595 diff --git a/data/videogames/shinobi b/data/videogames/shinobi new file mode 160000 index 00000000..ed682779 --- /dev/null +++ b/data/videogames/shinobi @@ -0,0 +1 @@ +Subproject commit ed6827793896100e9c3f303d34ec74ea347dc387 diff --git a/env.sh b/env.sh index f21b5714..2f4e24a7 100644 --- a/env.sh +++ b/env.sh @@ -1,2 +1,2 @@ -export PUPIL_PATH=$HOME/data/src/pupil +export PUPIL_PATH=$HOME/git/pupil diff --git a/main.py b/main.py index ab8a7c9f..2b33ab7a 100755 --- a/main.py +++ b/main.py @@ -1,24 +1,61 @@ #!/usr/bin/python3 +from subprocess import Popen + import os, sys, importlib -from psychopy import visual, logging # need to import visual first things to avoid pyglet related crash +import itertools +from collections.abc import Iterable, Iterator -# threading and processing -from multiprocessing import ( - Process, - Value, - active_children, - set_start_method, - freeze_support, -) +from src.shared import parser, config, screen +from src.shared.didyoumean import suggest_session_tasks -from src.shared import cli -if __name__ == "__main__": - parsed = cli.parse_args() - print(parsed) +def run(parsed): + # initializing the screen need to be done before loading any psychopy + if not parsed.no_force_resolution: + screen.init_exp_screen() try: - ses_mod = importlib.import_module('src.sessions.ses-%s'%parsed.session) - tasks = ses_mod.TASKS + ses_mod = importlib.import_module('src.sessions.ses-%s'%parsed.tasks) + tasks = ses_mod.get_tasks(parsed) if hasattr(ses_mod, 'get_tasks') else ses_mod.TASKS except ImportError: - raise(ValueError('session tasks file cannot be found for %s'%parsed.session)) - cli.main_loop(tasks[parsed.skip_n_tasks:], parsed.subject, parsed.session, parsed.eyetracking, parsed.fmri, parsed.meg) + suggestion = suggest_session_tasks(parsed.tasks) + raise(ValueError('session tasks file cannot be found for %s. Did you mean %s ?'%(parsed.tasks, suggestion))) + from src.shared import cli + if parsed.skip_n_tasks: + if isinstance(tasks, Iterator): + tasks = itertools.islice(tasks, parsed.skip_n_tasks, None) + else: + tasks = tasks[parsed.skip_n_tasks:] + try: + cli.main_loop( + tasks, + parsed.subject, + parsed.session, + parsed.output, + parsed.eyetracking, + parsed.fmri, + parsed.meg, + parsed.ctl_win, + parsed.run_on_battery, + parsed.ptt, + parsed.record_movie, + ) + finally: + if not parsed.no_force_resolution: + screen.reset_exp_screen() + +def run_profiled(parsed): + import cProfile + from main import run + cProfile.runctx( + "run(parsed)", + {'parsed':parsed}, + locals(), + "task_stimuli.pstats" + ) + +if __name__ == "__main__": + parsed = parser.parse_args() + if parsed.profile: + run_profiled(parsed) + else: + run(parsed) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..95e37f5a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +PsychoPy>=2020.2.4.post1 +gym-retro>=0.8.0 +pandas>=1.1.1 +python-dotenv +tqdm>=4.60.0 +textdistance diff --git a/src/sessions/ses-bourne_supremacy1.py b/src/sessions/ses-bourne_supremacy1.py index a2a7f1f5..c3e772f6 100644 --- a/src/sessions/ses-bourne_supremacy1.py +++ b/src/sessions/ses-bourne_supremacy1.py @@ -1,10 +1,11 @@ from ..tasks import images, video, memory, task_base -TASKS = [ -] -for seg_idx in range(1,6): +TASKS = [] +for seg_idx in range(1, 6): TASKS.append( video.SingleVideo( - 'data/videos/bourne_supremacy/bourne_supremacy_seg%02d.mkv'%seg_idx, - aspect_ratio = 372/157, - name='bourne_supremacy_seg-%d'%seg_idx)) + "data/videos/bourne_supremacy/bourne_supremacy_seg%02d.mkv" % seg_idx, + aspect_ratio=372 / 157, + name="bourne_supremacy_seg-%d" % seg_idx, + ) + ) diff --git a/src/sessions/ses-bourne_supremacy2.py b/src/sessions/ses-bourne_supremacy2.py index b71af830..34ea7a2a 100644 --- a/src/sessions/ses-bourne_supremacy2.py +++ b/src/sessions/ses-bourne_supremacy2.py @@ -1,9 +1,11 @@ from ..tasks import images, video, memory, task_base TASKS = [] -for seg_idx in range(6,11): +for seg_idx in range(6, 11): TASKS.append( video.SingleVideo( - 'data/videos/bourne_supremacy/bourne_supremacy_seg%02d.mkv'%seg_idx, - aspect_ratio = 372/157, - name='bourne_supremacy_seg-%d'%seg_idx)) + "data/videos/bourne_supremacy/bourne_supremacy_seg%02d.mkv" % seg_idx, + aspect_ratio=372 / 157, + name="bourne_supremacy_seg-%d" % seg_idx, + ) + ) diff --git a/src/sessions/ses-friends-s1.py b/src/sessions/ses-friends-s1.py index 9091a9b5..5ae6dd41 100644 --- a/src/sessions/ses-friends-s1.py +++ b/src/sessions/ses-friends-s1.py @@ -2,10 +2,12 @@ TASKS = [] -for episode in range(1,25): - for segment in 'ab': +for episode in range(1, 25): + for segment in "ab": TASKS.append( video.SingleVideo( - 'data/videos/friends/s1/friends_s1e%02d%s.mkv'%(episode, segment), - aspect_ratio = 4/3., - name='task-friends-s1e%d%s'%(episode, segment))) + "data/videos/friends/s1/friends_s1e%02d%s.mkv" % (episode, segment), + aspect_ratio=4 / 3.0, + name="task-friends-s1e%d%s" % (episode, segment), + ) + ) diff --git a/src/sessions/ses-friends-s2.py b/src/sessions/ses-friends-s2.py new file mode 100644 index 00000000..7245021b --- /dev/null +++ b/src/sessions/ses-friends-s2.py @@ -0,0 +1,13 @@ +from ..tasks import video + +TASKS = [] + +for episode in range(1, 25): + for segment in "ab": + TASKS.append( + video.SingleVideo( + "data/videos/friends/s2/friends_s2e%02d%s.mkv" % (episode, segment), + aspect_ratio=4 / 3.0, + name="task-friends-s2e%d%s" % (episode, segment), + ) + ) diff --git a/src/sessions/ses-friends-s3.py b/src/sessions/ses-friends-s3.py new file mode 100644 index 00000000..d783c79a --- /dev/null +++ b/src/sessions/ses-friends-s3.py @@ -0,0 +1,13 @@ +from ..tasks import video + +TASKS = [] + +for episode in range(1, 26): + for segment in "ab": + TASKS.append( + video.SingleVideo( + "data/videos/friends/s3/friends_s3e%02d%s.mkv" % (episode, segment), + aspect_ratio=4 / 3.0, + name="task-friends-s3e%d%s" % (episode, segment), + ) + ) diff --git a/src/sessions/ses-friends-s4.py b/src/sessions/ses-friends-s4.py new file mode 100644 index 00000000..68b204d8 --- /dev/null +++ b/src/sessions/ses-friends-s4.py @@ -0,0 +1,21 @@ +from ..tasks import video + +TASKS = [] + +for episode in range(1, 23): + for segment in "ab": + TASKS.append( + video.SingleVideo( + "data/videos/friends/s4/friends_s4e%02d%s.mkv" % (episode, segment), + aspect_ratio=4 / 3.0, + name="task-friends-s4e%d%s" % (episode, segment), + ) + ) +""" +for segment in 'abcd': + TASKS.append( + video.SingleVideo( + 'data/videos/friends/s4/friends_s4e%02d%s.mkv'%(23, segment), + aspect_ratio = 4/3., + name='task-friends-s4e%d%s'%(23, segment))) +""" diff --git a/src/sessions/ses-friends-s5.py b/src/sessions/ses-friends-s5.py new file mode 100644 index 00000000..cc7bc8b1 --- /dev/null +++ b/src/sessions/ses-friends-s5.py @@ -0,0 +1,21 @@ +from ..tasks import video + +TASKS = [] + +for episode in range(1, 23): + for segment in "ab": + TASKS.append( + video.SingleVideo( + "data/videos/friends/s5/friends_s05e%02d%s.mkv" % (episode, segment), + aspect_ratio=4 / 3.0, + name="task-friends-s5e%d%s" % (episode, segment), + ) + ) + +for segment in 'abcd': + TASKS.append( + video.SingleVideo( + 'data/videos/friends/s5/friends_s05e%02d%s.mkv'%(23, segment), + aspect_ratio = 4/3., + name='task-friends-s5e%d%s'%(23, segment))) + diff --git a/src/sessions/ses-harrypotter.py b/src/sessions/ses-harrypotter.py new file mode 100644 index 00000000..7745e216 --- /dev/null +++ b/src/sessions/ses-harrypotter.py @@ -0,0 +1,12 @@ +from ..tasks import language + +TASKS = [] +for run in range(1, 8): + TASKS.append( + language.Reading( + f"data/language/harrypotter/task-harry_run-{run}_events.tsv", + name=f"harrypotter_run-{run}", + cross_duration=2, + txt_size=124, + ) + ) diff --git a/src/sessions/ses-hiddenfigures1.py b/src/sessions/ses-hiddenfigures1.py index f5b76d13..ecc5a082 100644 --- a/src/sessions/ses-hiddenfigures1.py +++ b/src/sessions/ses-hiddenfigures1.py @@ -1,11 +1,13 @@ from ..tasks import images, video, memory, task_base TASKS = [] -for seg_idx in range(1,7): +for seg_idx in range(1, 7): TASKS.append( video.SingleVideo( - 'data/videos/hidden_figures/hidden_figures_seg%02d.mkv'%seg_idx, - aspect_ratio = 12/5, - name='hidden_figures_seg-%d'%seg_idx)) + "data/videos/hidden_figures/hidden_figures_seg%02d.mkv" % seg_idx, + aspect_ratio=12 / 5, + name="hidden_figures_seg-%d" % seg_idx, + ) + ) # 410 volumes each diff --git a/src/sessions/ses-hiddenfigures2.py b/src/sessions/ses-hiddenfigures2.py index 12420fc4..2886063e 100644 --- a/src/sessions/ses-hiddenfigures2.py +++ b/src/sessions/ses-hiddenfigures2.py @@ -1,11 +1,13 @@ from ..tasks import images, video, memory, task_base TASKS = [] -for seg_idx in range(7,13): +for seg_idx in range(7, 13): TASKS.append( video.SingleVideo( - 'data/videos/hidden_figures/hidden_figures_seg%02d.mkv'%seg_idx, - aspect_ratio = 12/5, - name='hidden_figures_seg-%d'%seg_idx)) + "data/videos/hidden_figures/hidden_figures_seg%02d.mkv" % seg_idx, + aspect_ratio=12 / 5, + name="hidden_figures_seg-%d" % seg_idx, + ) + ) # 410 volumes each, last run is 375 volumes long diff --git a/src/sessions/ses-life1.py b/src/sessions/ses-life1.py index ba8f2df8..b3a5f4f6 100644 --- a/src/sessions/ses-life1.py +++ b/src/sessions/ses-life1.py @@ -1,11 +1,13 @@ from ..tasks import images, video, memory, task_base TASKS = [] -for seg_idx in range(1,6): +for seg_idx in range(1, 6): TASKS.append( video.SingleVideo( - 'data/videos/life1/life1_seg%02d.mkv'%seg_idx, - aspect_ratio = 12/5, - name='life1_seg-%d'%seg_idx)) + "data/videos/life1/life1_seg%02d.mkv" % seg_idx, + aspect_ratio=12 / 5, + name="life1_seg-%d" % seg_idx, + ) + ) # 410 volumes each, last run is 392 volumes long diff --git a/src/sessions/ses-liris.py b/src/sessions/ses-liris.py new file mode 100644 index 00000000..3044398e --- /dev/null +++ b/src/sessions/ses-liris.py @@ -0,0 +1,33 @@ +from ..tasks import video, task_base +import numpy as np + + +def get_videos(subject, session): + video_idx = np.loadtxt( + "data/liris/order_fmri_neuromod.csv", delimiter=",", skiprows=1, dtype=np.int + ) + selected_idx = video_idx[video_idx[:, 0] == session, subject + 1] + return selected_idx + + +def get_tasks(parsed): + + tasks = [] + + video_indices = get_videos(int(parsed.subject), int(parsed.session)) + + for idx in video_indices: + tasks.append( + video.SingleVideo( + f"data/liris/videos/{idx:03d}.mp4", name=f"task-liris{idx:03d}" + ) + ) + continue + tasks.append( + task_base.Pause( + """The video is finished. +The scanner might run for a few seconds to acquire more images. +Please remain still.""" + ) + ) + return tasks diff --git a/src/sessions/ses-mario.py b/src/sessions/ses-mario.py new file mode 100644 index 00000000..02b7053b --- /dev/null +++ b/src/sessions/ses-mario.py @@ -0,0 +1,100 @@ +import os +import random +import retro +import json + +# point to a copy of the whole gym-retro with custom states and scenarii +retro.data.Integrations.add_custom_path( + os.path.join(os.getcwd(), "data", "videogames", "mario") +) + +from psychopy import logging +from ..tasks import images, videogame, memory, task_base + +flow_ratings = [ + "I feel just the right amount of challenge.", + "My thoughts/activities run fluidly and smoothly.", + "I don’t notice time passing.", + "I have no difficulty concentrating.", + "My mind is completely clear.", + "I am totally absorbed in what I am doing.", + "The right thoughts/movements occur of their own accord.", + "I know what I have to do each step of the way.", + "I feel that I have everything under control.", + "I am completely lost in thought.", +] + +levels_scenario = [ + ("Level1-1", "scenario"), + ("Level1-2", "scenario"), + ("Level1-3", "scenario")] +#random.shuffle(levels_scenario) # randomize order + +scenario = "scenario" + + +# code adaptive design for learning phase + +def get_tasks(parsed): + bids_sub = "sub-%s" % parsed.subject + savestate_path = os.path.abspath(os.path.join(parsed.output, "sourcedata", bids_sub, f"{bids_sub}_task-mario_savestate.json")) + + # check for a "savestate" + if os.path.exists(savestate_path): + with open(savestate_path) as f: + savestate = json.load(f) + else: + savestate = {"world": 1, "level":1} #TODO: determine format + + for run in range(5): + current_level = f"Level{savestate['world']}-{savestate['level']}" + task = videogame.VideoGameMultiLevel( + game_name='SuperMarioBros-Nes', + state_names=[current_level], + scenarii=[scenario], + repeat_scenario=True, + max_duration=10 * 60, # if when level completed or dead we exceed that time in secs, stop the task + name=f"task-mario_run-{run+1:02d}", + instruction="playing Super Mario Bros {state_name} \n\n Let's-a go!", + post_level_ratings = [(k, q, 7) for k, q in enumerate(flow_ratings)] + ) + yield task + + if task._completed: + logging.exp(f"{current_level} successfuly completed at least once.") + savestate['level'] += 1 + if savestate['level'] > 3: + savestate['world'] +=1 + savestate['level'] = 1 + with open(savestate_path, 'w') as f: + json.dump(savestate, f) + else: + logging.exp(f"{current_level} not completed.") + + #yield task_base.Pause() + + + return tasks + +""" +TASKS = sum( + [ + [ + videogame.VideoGameMultiLevel( + game_name='SuperMarioBros-Nes', + state_names=[l for l,s in levels_scenario], + scenarii=[s for l,s in levels_scenario] + , # this scenario repeats the same level + repeat_scenario=True, + max_duration=10 + * 60, # if when level completed or dead we exceed that time in secs, stop the task + name=f"task-mario_run-{run+1:02d}", + instruction="playing Super Mario Bros {state_name} \n\n Let's-a go!", + # post_level_ratings = [(q, 7) for q in flow_ratings], + ), + task_base.Pause(), + ] + for run in range(5) + ], + [], +)""" diff --git a/src/sessions/ses-megmotion.py b/src/sessions/ses-megmotion.py index b32c50fb..5c809c8e 100644 --- a/src/sessions/ses-megmotion.py +++ b/src/sessions/ses-megmotion.py @@ -1,7 +1,6 @@ from ..tasks import images, video, memory, task_base, eye_movements TASKS = [ - eye_movements.EyetrackerTask(name='eye_mvts'), task_base.Pause(), task_base.Fixation(duration=7*60, name='resting_state'), @@ -14,7 +13,7 @@ 'data/videos/movies_for_montreal/Oceans_10m_fs.mp4', name='Oceans_1'), task_base.Pause(), - task_base.Fixation(duration=7*60, name='resting_state'), + task_base.Fixation(duration=7 * 60, name="resting_state"), task_base.Pause(), video.SingleVideo( 'data/videos/movies_for_montreal/03_Inscaped_NoScannerSound_h264.mov', diff --git a/src/sessions/ses-motion.py b/src/sessions/ses-motion.py index a4e501a5..b47c9090 100644 --- a/src/sessions/ses-motion.py +++ b/src/sessions/ses-motion.py @@ -1,18 +1,19 @@ from ..tasks import images, video, memory, task_base TASKS = [ - task_base.Pause("""Hi! We are about to start the MRI session. + task_base.Pause( + """Hi! We are about to start the MRI session. Make yourself comfortable. Ensure that you can see the full screen and that the image is sharp. -Please keep your eyes open."""), - speech.Speech('data/speech/motion_study_speech_words.csv', name='Speech'), - video.SingleVideo( - 'data/videos/Inscapes-67962604.mp4', name='Inscapes'), +Please keep your eyes open.""" + ), + speech.Speech("data/speech/motion_study_speech_words.csv", name="Speech"), + video.SingleVideo("data/videos/Inscapes-67962604.mp4", name="Inscapes"), # source: https://drive.google.com/file/d/1prOM1QuPEAcqe_D-3rLYNu937A8VzbsB/view - video.SingleVideo( - 'data/videos/tammy/Oceans_1.mp4', - name='Oceans_chunk1'), - videogame.VideoGame(state_name='Level1',name='ShinobiIIIReturnOfTheNinjaMaster-test'), - ] + video.SingleVideo("data/videos/tammy/Oceans_1.mp4", name="Oceans_chunk1"), + videogame.VideoGame( + state_name="Level1", name="ShinobiIIIReturnOfTheNinjaMaster-test" + ), +] - # TODO: randomize the order of the tasks? +# TODO: randomize the order of the tasks? diff --git a/src/sessions/ses-retino.py b/src/sessions/ses-retino.py new file mode 100644 index 00000000..b305e037 --- /dev/null +++ b/src/sessions/ses-retino.py @@ -0,0 +1,13 @@ +from ..tasks import retinotopy +import random + +conditions = ['RETBAR','RETRINGS','RETWEDGES',] +#conditions = random.shuffle(conditions) + +TASKS = [ + retinotopy.Retinotopy( + condition = condition, + ncycles=4, + name=f"task-retinotopy{condition}", + ) for condition in conditions #'RETCCW','RETCW','RETEXP','RETCON'] +] diff --git a/src/sessions/ses-shinobi.py b/src/sessions/ses-shinobi.py index ce5722e9..a97d9a65 100644 --- a/src/sessions/ses-shinobi.py +++ b/src/sessions/ses-shinobi.py @@ -1,13 +1,12 @@ - from ..tasks import images, videogame, memory, task_base TASKS = [ - videogame.VideoGame( - state_name='Level1', - scenario='scenario_repeat1', # this scenario repeats the same level - max_duration=10*60, # if when level completed or dead we exceed that time in secs, stop the task - name='ShinobiIIIReturnOfTheNinjaMaster-level1'), - #videogame.VideoGameReplay('output/sub-test/ses-test_refactor/sub-test_ses-test_refactor_20190109_154007_ShinobiIIIReturnOfTheNinjaMaster-Genesis_Level4_000.bk2',name='ShinobiIIIReturnOfTheNinjaMaster-replay',), - -] * 3 ###!!!!!!!!!!! REPEAT !!!!!!!!!!!### + state_name="Level1", + scenario="scenario_repeat1", # this scenario repeats the same level + max_duration=10 + * 60, # if when level completed or dead we exceed that time in secs, stop the task + name="ShinobiIIIReturnOfTheNinjaMaster-level1", + ), + # videogame.VideoGameReplay('output/sub-test/ses-test_refactor/sub-test_ses-test_refactor_20190109_154007_ShinobiIIIReturnOfTheNinjaMaster-Genesis_Level4_000.bk2',name='ShinobiIIIReturnOfTheNinjaMaster-replay',), +] * 3 ###!!!!!!!!!!! REPEAT !!!!!!!!!!!### diff --git a/src/sessions/ses-shinobi3levels.py b/src/sessions/ses-shinobi3levels.py new file mode 100644 index 00000000..e6aa3095 --- /dev/null +++ b/src/sessions/ses-shinobi3levels.py @@ -0,0 +1,50 @@ +import os +import random +import retro + +# point to a copy of the whole gym-retro with custom states and scenarii +retro.data.Integrations.add_custom_path( + os.path.join(os.getcwd(), "data", "videogames", "shinobi") +) + +from ..tasks import images, videogame, memory, task_base + +flow_ratings = [ + "I feel just the right amount of challenge.", + "My thoughts/activities run fluidly and smoothly.", + "I don’t notice time passing.", + "I have no difficulty concentrating.", + "My mind is completely clear.", + "I am totally absorbed in what I am doing.", + "The right thoughts/movements occur of their own accord.", + "I know what I have to do each step of the way.", + "I feel that I have everything under control.", + "I am completely lost in thought.", +] + +levels_scenario = [ + ("Level1-0", "scenario_Level1"), + ("Level4-1", "scenario_Level4-1"), + ("Level5-0", "scenario_Level5-0")] +#random.shuffle(levels_scenario) # randomize order + +TASKS = sum( + [ + [ + videogame.VideoGameMultiLevel( + state_names=[l for l,s in levels_scenario], + scenarii=[s for l,s in levels_scenario] + , # this scenario repeats the same level + repeat_scenario=True, + max_duration=10 + * 60, # if when level completed or dead we exceed that time in secs, stop the task + name=f"task-shinobi_run-{run+1:02d}", + instruction="Let's play Shinobi III: {state_name}\nHave fun!", + # post_level_ratings = [(q, 7) for q in flow_ratings] + ), + task_base.Pause(), + ] + for run in range(5) + ], + [], +) diff --git a/src/sessions/ses-shinobi_3levels.py b/src/sessions/ses-shinobi_3levels.py index d6960b22..b674f3b6 100644 --- a/src/sessions/ses-shinobi_3levels.py +++ b/src/sessions/ses-shinobi_3levels.py @@ -1,25 +1,26 @@ - from ..tasks import images, videogame, memory, task_base TASKS = [ - videogame.VideoGame( - state_name='Level1', - scenario='scenario_repeat1', # this scenario repeats the same level - max_duration=10*60, # if when level completed or dead we exceed that time in secs, stop the task - name='ShinobiIIIReturnOfTheNinjaMaster-level1'), - #videogame.VideoGameReplay('output/sub-test/ses-test_refactor/sub-test_ses-test_refactor_20190109_154007_ShinobiIIIReturnOfTheNinjaMaster-Genesis_Level4_000.bk2',name='ShinobiIIIReturnOfTheNinjaMaster-replay',), - + state_name="Level1", + scenario="scenario_repeat1", # this scenario repeats the same level + max_duration=10 + * 60, # if when level completed or dead we exceed that time in secs, stop the task + name="ShinobiIIIReturnOfTheNinjaMaster-level1", + ), + # videogame.VideoGameReplay('output/sub-test/ses-test_refactor/sub-test_ses-test_refactor_20190109_154007_ShinobiIIIReturnOfTheNinjaMaster-Genesis_Level4_000.bk2',name='ShinobiIIIReturnOfTheNinjaMaster-replay',), videogame.VideoGame( - state_name='Level4-1', - scenario='scenario_Level4-1', # this scenario repeats the same level - max_duration=10*60, # if when level completed or dead we exceed that time in secs, stop the task - name='ShinobiIIIReturnOfTheNinjaMaster-level4-1'), - + state_name="Level4-1", + scenario="scenario_Level4-1", # this scenario repeats the same level + max_duration=10 + * 60, # if when level completed or dead we exceed that time in secs, stop the task + name="ShinobiIIIReturnOfTheNinjaMaster-level4-1", + ), videogame.VideoGame( - state_name='Level5-0', - scenario='scenario_Level5-0', # this scenario repeats the same level - max_duration=10*60, # if when level completed or dead we exceed that time in secs, stop the task - name='ShinobiIIIReturnOfTheNinjaMaster-level5-0'), - -] * 3 ###!!!!!!!!!!! REPEAT !!!!!!!!!!!### + state_name="Level5-0", + scenario="scenario_Level5-0", # this scenario repeats the same level + max_duration=10 + * 60, # if when level completed or dead we exceed that time in secs, stop the task + name="ShinobiIIIReturnOfTheNinjaMaster-level5-0", + ), +] * 3 ###!!!!!!!!!!! REPEAT !!!!!!!!!!!### diff --git a/src/sessions/ses-shinobimeg.py b/src/sessions/ses-shinobimeg.py new file mode 100644 index 00000000..897b24b8 --- /dev/null +++ b/src/sessions/ses-shinobimeg.py @@ -0,0 +1,49 @@ +import os +import random +import retro + +# point to a copy of the whole gym-retro with custom states and scenarii +retro.data.Integrations.add_custom_path( + os.path.join(os.getcwd(), "data", "videogames", "shinobi") +) + +from ..tasks import images, videogame, memory, task_base + +flow_ratings = [ + ("challenge", "I feel just the right amount of challenge."), + ("fluidity", "My thoughts/activities run fluidly and smoothly."), + ("time","I don’t notice time passing."), + ("focus", "I have no difficulty concentrating."), + ("insight","My mind is completely clear."), + ("absorption","I am totally absorbed in what I am doing."), + ("spontaneous","The right thoughts/movements occur of their own accord."), + ("planning", "I know what I have to do each step of the way."), + ("control", "I feel that I have everything under control."), + ("wandering", "I am completely lost in thought."), +] + +levels_scenario = [ + ("Level1-0", "scenario_Level1"), + ("Level4-1", "scenario_Level4-1"), + ("Level5-0", "scenario_Level5-0")] +#random.shuffle(levels_scenario) # randomize order + +TASKS = sum( + [ + [ + videogame.VideoGameMultiLevel( + state_names=[l for l,s in levels_scenario], + scenarii=[s for l,s in levels_scenario] + , # this scenario repeats the same level + repeat_scenario=True, + max_duration=9 + * 60, # if when level completed or dead we exceed that time in secs, stop the task + name=f"task-shinobi_run-{run+1:02d}", + post_level_ratings = [(k, q, 7) for k, q in flow_ratings] + ), + task_base.Pause(), + ] + for run in range(5) + ], + [], +) diff --git a/src/sessions/ses-synctest.py b/src/sessions/ses-synctest.py new file mode 100644 index 00000000..644fa0c1 --- /dev/null +++ b/src/sessions/ses-synctest.py @@ -0,0 +1,5 @@ +from ..tasks import video + +TASKS = [ + video.SingleVideo("data/videos/Sync-Footage-V1-H264.mp4", name="task-synctest") +] * 10 diff --git a/src/sessions/ses-test64.py b/src/sessions/ses-test64.py index e8d0344f..a1b0ce3a 100644 --- a/src/sessions/ses-test64.py +++ b/src/sessions/ses-test64.py @@ -1,12 +1,11 @@ from ..tasks import images, video, memory, task_base TASKS = [ - video.SingleVideo( - 'data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4', name='Inscapes'), - + "data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4", name="Inscapes" + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_1_filt.mp4', - name='Oceans_fs_10m_1'), - + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_1_filt.mp4", + name="Oceans_fs_10m_1", + ), ] diff --git a/src/sessions/ses-things.py b/src/sessions/ses-things.py new file mode 100644 index 00000000..eb65cdc3 --- /dev/null +++ b/src/sessions/ses-things.py @@ -0,0 +1,117 @@ +import os + +THINGS_DATA_PATH = os.path.join("data", "things") +IMAGE_PATH = os.path.join(THINGS_DATA_PATH, "images") + + +def get_tasks(parsed): + from ..tasks.things import Things + + session_design_filename = os.path.join( + THINGS_DATA_PATH, + "designs", + f"sub-{parsed.subject}_ses-{parsed.session}_design.tsv", + ) + tasks = [ + Things(session_design_filename, IMAGE_PATH, run, name=f"task-things_run-{run}") + for run in range(1, n_runs + 1) + ] + return tasks + + +# experiment + +n_sessions = 12 # number of sessions +n_runs = 10 # number of runs +n_trials_exp = 72 # number of trials for each run +n_trials_catch = 10 # catch trials where response is required +n_trials_test = 10 # for test data, separate images +n_trials_total = n_trials_exp + n_trials_test + n_trials_catch +final_wait = 9 # time to wait after last trial +initial_wait = 3 # time until first trial starts + +# trial +trial_duration = 4.5 # mean trial duration +jitters = 0 # chosen to be a range that minimizes phase synchrony and that can be presented exactly on most screens +image_duration = 0.5 # duration of image presentation +rm_duration = 4.0 # duration of response mapping screen +max_rt = 4.0 # from stimulus onset + +# constraints +min_catch_spacing = 3 +max_catch_spacing = 20 + + +def generate_design_file(subject, session): + import pandas + import numpy as np + import random + import hashlib + + images_list = pandas.read_csv( + os.path.join(THINGS_DATA_PATH, "image_paths_fmri.csv") + ) + + images_exp = images_list[ + images_list.condition.eq("exp") & images_list.exemplar_nr.eq(int(session)) + ].sample(frac=1) + images_catch = images_list[images_list.condition.eq("catch")].sample(frac=1) + images_test = images_list[images_list.condition.eq("test")].sample(frac=1) + + design = pandas.DataFrame() + + print("%s-%s" % (subject, session)) + seed = int( + hashlib.sha1(("%s-%s" % (subject, session)).encode("utf-8")).hexdigest(), 16 + ) % (2 ** 32 - 1) + print("seed", seed) + np.random.seed(seed) + + all_run_trials = pandas.DataFrame() + + for run in range(n_runs): + niter = 0 + while True: + niter += 1 + randorder = np.random.permutation(n_trials_total) + n_noncatch_trial = n_trials_exp + n_trials_test + catch_indices = np.where(randorder >= n_noncatch_trial)[0] + catch_indices_bounds = np.hstack([[0], catch_indices, [n_trials_total]]) + catch_spacings = np.diff(catch_indices_bounds) + if np.all(catch_spacings > min_catch_spacing) and np.all( + catch_spacings < max_catch_spacing + ): + break + print(subject, session, run, niter) + run_trials = pandas.concat( + [ + images_exp[run * n_trials_exp : (run + 1) * n_trials_exp], + images_test[run * n_trials_test : (run + 1) * n_trials_test], + images_catch[run * n_trials_catch : (run + 1) * n_trials_catch], + ] + ) + run_trials = run_trials.iloc[randorder] + run_trials["run"] = run + 1 + run_trials["onset"] = initial_wait + np.arange(n_trials_total) * trial_duration + run_trials["duration"] = image_duration + all_run_trials = pandas.concat([all_run_trials, run_trials]) + out_fname = os.path.join( + THINGS_DATA_PATH, + "designs", + f"sub-{parsed.subject}_ses-{parsed.session}_design.tsv", + ) + all_run_trials.to_csv(out_fname, sep="\t") + + +if __name__ == "__main__": + import argparse + import sys + + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + description="generate design files for participant / session", + ) + parser.add_argument("subject", type=str, help="participant id") + parser.add_argument("session", type=str, help="session id") + parsed = parser.parse_args() + generate_design_file(parsed.subject, parsed.session) diff --git a/src/sessions/ses-thingsmem.py b/src/sessions/ses-thingsmem.py new file mode 100644 index 00000000..4bb0017a --- /dev/null +++ b/src/sessions/ses-thingsmem.py @@ -0,0 +1,281 @@ +import os + +THINGS_DATA_PATH = os.path.join("data", "things") +IMAGE_PATH = os.path.join(THINGS_DATA_PATH, "images") + + +def get_tasks(parsed): + from ..tasks.things import ThingsMemory + + session_design_filename = os.path.join( + THINGS_DATA_PATH, + "memory_designs", + f"sub-{parsed.subject}_ses-{parsed.session}_design.tsv", + ) + n_runs_session = n_runs if int(parsed.session) > 1 else n_runs//2 + tasks = [ + ThingsMemory(session_design_filename, IMAGE_PATH, run, name=f"task-thingsmemory_run-{run}") + for run in range(1, n_runs_session + 1) + ] + return tasks + + +# experiment + +n_sessions = 36 # number of sessions +n_runs = 6 # number of runs +n_trials = 60 # number of trials for each run +splits = n_trials * 1 +new_samples_nsplits = 720//splits//2 + +final_wait = 9 # time to wait after last trial +initial_wait = 3 # time until first trial starts + +# trial +tr = 1.49 +trial_duration = 3*tr # mean trial duration +jitters = 0 # chosen to be a range that minimizes phase synchrony and that can be presented exactly on most screens +image_duration = 2*tr # duration of image presentation +rm_duration = 4. # duration of response mapping screen +max_rt = 4.0 # from stimulus onset + +# constraints +max_seen_spacing = 8 + + + + +def generate_design_file(subject): + import pandas + import numpy as np + import random + import hashlib + + + # proportions for intra-session pseudo-randomize + props = pandas.DataFrame() + props['unseen_between'] = [5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15] + props['unseen_within'] = [15, 14, 13, 12, 11, 10, 10, 9, 8, 7, 6, 5] + props['seen_within'] = props['unseen_between'] + props['seen_between_within'] = props['unseen_within'] + props['seen_between_within2'] = props['unseen_between'] + props['seen_within_between'] = props['unseen_within'] + + #props_session1 = pandas.DataFrame() + #props_session1['unseen_between'] = [12, 16, 18, 22, 24, 28] + #props_session1['unseen_within'] = [28, 24 , 22, 18, 16, 12] + #props_session1['seen_within'] = [20] * (n_runs//2) + + props = pandas.DataFrame() + props['unseen_between'] = [6, 8, 10, 10, 12, 14] + props['unseen_within'] = [14, 12, 10, 10, 8, 6] + props['seen_within'] = props['unseen_between'] + props['seen_between_within'] = props['unseen_within'] + props['seen_between_within2'] = props['unseen_between'] + props['seen_within_between'] = props['unseen_within'] + + props_session1 = pandas.DataFrame() + props_session1['unseen_between'] = [12, 20, 28] + props_session1['unseen_within'] = [28, 20, 12] + props_session1['seen_within'] = [20] * (n_runs//2) + + + images_list = pandas.read_csv( + os.path.join(THINGS_DATA_PATH, "images", "image_paths_fmri.csv") + ) + + # we select only the exp trial from Martin's BIG experiment + # and only the first (or second for pilot) half of the 12 exemplar of each category + images_exp = images_list.loc[ + images_list.condition.eq("exp") & + (images_list.exemplar_nr < 7 ) # > 6 for pilot < 7 for study + ] + + # randomize examplar order across participants + exemplar_sorting = np.random.permutation(np.arange(1,7)) + if subject in ["%02d" % sn for sn in [2, 6]]: + print("already seen examplars 1 and 2") + exemplar_sorting = np.hstack([ + np.random.permutation(np.arange(3,7)), + np.random.permutation(np.arange(1,3)) + ]) + elif subject in ["%02d" % sn for sn in [1, 3 , 4]]: + print("already seen examplars 1") + exemplar_sorting = np.hstack([ + np.random.permutation(np.arange(2,7)), + [1] + ]) + + # seed numpy with subject id to have reproducible design generation + seed = int( + hashlib.sha1(("%s" % (subject)).encode("utf-8")).hexdigest(), 16 + ) % (2 ** 32 - 1) + print("seed", seed) + np.random.seed(seed) + + all_run_trials = pandas.DataFrame() + + # permute categories per participant + categories = np.random.permutation(720)+1 + + #empty roll-over subconditions for first session + img_within_between = pandas.DataFrame() + img_between_within = pandas.DataFrame() + + for session in range(n_sessions): + # select the examplar + exemplar = exemplar_sorting[session//6] + #exemplar = session//3+1 # + 6 here for pilot, remove for study!! + + # subselect 240 categories for new stimuli + # loop through the 3 sets of 240 across sessions and thus avoid + # having distractors from the same category within-session + # but there will still be between session distractors + new_stimuli_categories = categories[splits*2*(session%new_samples_nsplits):splits*2*(session%new_samples_nsplits+1)] + #randomize categories to be used as within and between repeated new stimuli to avoid systematic bias + new_stimuli_categories = np.random.permutation(new_stimuli_categories) + cat_unseen_within = new_stimuli_categories[:splits] + cat_unseen_between = new_stimuli_categories[splits:] + + # get all the new stimuli that will be repeated within first + img_unseen_within = images_exp[ + images_exp.category_nr.isin(cat_unseen_within) & + images_exp.exemplar_nr.eq(exemplar)] + img_unseen_within.condition = "unseen" + img_unseen_within['subcondition'] = "unseen-within" + img_unseen_within['repetition'] = 1 + + # get all the new stimuli that will be repeated between first + img_unseen_between = images_exp[ + images_exp.category_nr.isin(cat_unseen_between) & + images_exp.exemplar_nr.eq(exemplar)] + img_unseen_between.condition = "unseen" + img_unseen_between['subcondition'] = "unseen-between" + img_unseen_between['repetition'] = 1 + + n_runs_session = n_runs + if session == 0: + session_props = props_session1 + n_runs_session = n_runs//2 + else: + session_props = props + + img_unseen_between = img_unseen_between.sample(frac=1).reset_index(drop=True) #randomize + img_unseen_between["run"] = np.hstack([r+1]*prop for r,prop in enumerate(session_props.unseen_between)) + img_unseen_within = img_unseen_within.sample(frac=1).reset_index(drop=True) #randomize + img_unseen_within["run"] = np.hstack([r+1]*prop for r,prop in enumerate(session_props.unseen_within)) + + if session > 0: + img_within_between = img_within_between.sample(frac=1).reset_index(drop=True) #randomize + img_within_between["run"] = np.hstack([r+1]*prop for r,prop in enumerate(session_props.seen_within_between)) + img_between_within = img_between_within.sample(frac=1).reset_index(drop=True) #randomize + img_between_within["run"] = np.hstack([r+1]*prop for r,prop in enumerate(session_props.seen_between_within)) + + + # here it is more complex due to temporal dependencies of within session repetitions + + all_unseen_within = pandas.DataFrame() + img_seen_within = pandas.DataFrame() + for run in range(n_runs_session): + img_unseen_within_run = img_unseen_within[img_unseen_within.run == run+1] + # aggregate all the unused repetitions + all_unseen_within = all_unseen_within.append(img_unseen_within_run) + # randomely sample the unused repetitions + img_seen_within_run = all_unseen_within.sample(n=session_props.seen_within[run]) + all_unseen_within = all_unseen_within.drop(img_seen_within_run.index) # without replacement + img_seen_within_run['run'] = run+1 + img_seen_within = img_seen_within.append(img_seen_within_run) + +# img_seen_within.set_index( +# img_seen_within.index+np.random.randint(1, n_trials/2, img_seen_within.shape[0]), +# inplace=True) + #img_seen_within['repetition'] = 2 + #img_seen_within['condition'] = 'seen' + #img_seen_within['subcondition'] = 'seen-within' + #img_seen_within = img_seen_within.reset_index(drop=True) + + img_between_within2 = pandas.DataFrame() + if session > 0: + all_between_within = pandas.DataFrame() + for run in range(n_runs_session): + img_between_within_run = img_between_within[img_between_within.run == run+1] + # aggregate all the unused repetitions + all_between_within = all_between_within.append(img_between_within_run) + # randomely sample the unused repetitions + img_between_within_run = all_between_within.sample(n=session_props.seen_between_within2[run]) + all_between_within = all_between_within.drop(img_between_within_run.index) # without replacement + img_between_within_run['run'] = run+1 + img_between_within2 = img_between_within2.append(img_between_within_run) + #img_between_within2['repetition'] = 3 + #img_between_within2 = img_between_within2.reset_index(drop=True) +# img_between_within2.set_index( +# img_between_within2.index + np.random.randint(1, n_trials/2, img_between_within2.shape[0]), +# inplace=True) + + + all_run_trials = pandas.concat([ + img_unseen_within, + img_unseen_between, + img_seen_within, + img_between_within, + img_between_within2, + img_within_between + ], ignore_index=True) + + #all_run_trials = all_run_trials.reset_index().sort_values(['run','index','image_nr','repetition']) + + # pass new "within"/"between" stimuli to the next session + img_between_within = img_unseen_between + img_between_within.condition = 'seen' + img_between_within.subcondition = "seen-between" + img_between_within.repetition = 2 + + img_within_between = img_unseen_within + img_within_between.condition = 'seen' + img_within_between.subcondition = "seen-within-between" + img_within_between.repetition = 3 + + + all_run_trials = all_run_trials.sample(frac=1).sort_values('run') + repeated_within = all_run_trials['image_nr'].duplicated() + + # set seen condition for second viewing of new set of images + all_run_trials.loc[repeated_within, 'condition'] = 'seen' + seen_within_subset = repeated_within & \ + all_run_trials.subcondition.eq('unseen-within') & \ + all_run_trials.repetition.eq(1) + all_run_trials.loc[seen_within_subset, 'repetition'] = 2 + all_run_trials.loc[seen_within_subset, 'subcondition'] = 'seen-within' + + #all_run_trials.loc[repeated_within & all_run_trials.subcondition.eq('unseen-within'), 'repetition'] = 2 + seen_between_within = repeated_within & all_run_trials.subcondition.eq('seen-between') + all_run_trials.loc[seen_between_within, 'repetition'] = 3 + all_run_trials.loc[seen_between_within, 'subcondition'] = 'seen-between-within' + + # set timing + all_run_trials["onset"] = np.tile(initial_wait + np.arange(n_trials) * trial_duration, n_runs_session) + all_run_trials["duration"] = image_duration + # set equal number of flipped and unflipped response mapping + all_run_trials["response_mapping_flip_h"] = np.hstack([np.random.permutation(np.arange(2,dtype=np.bool).repeat(n_trials/2)) for i in range(n_runs_session)]) + all_run_trials["response_mapping_flip_v"] = np.hstack([np.random.permutation(np.arange(2,dtype=np.bool).repeat(n_trials/2)) for i in range(n_runs_session)]) + + # save a file for the whole session (will be split in runs in the task) + out_fname = os.path.join( + THINGS_DATA_PATH, + "memory_designs", + f"sub-{parsed.subject}_ses-{session+1:03d}_design.tsv", + ) + all_run_trials.to_csv(out_fname, sep="\t", index=False) + + +if __name__ == "__main__": + import argparse + import sys + + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + description="generate design files for participant / session", + ) + parser.add_argument("subject", type=str, help="participant id") + parsed = parser.parse_args() + generate_design_file(parsed.subject) diff --git a/src/sessions/ses-triplet_test.py b/src/sessions/ses-triplet_test.py index 7a437f86..05132333 100644 --- a/src/sessions/ses-triplet_test.py +++ b/src/sessions/ses-triplet_test.py @@ -2,21 +2,11 @@ TASKS = [ language.Triplet( - 'data/language/triplet/first1000triples.csv', - wait_key=True, - name='triplet_test'), - - task_base.Pause( - text="Take a break. Press any key to continue...", - wait_key=None), - + "data/language/triplet/first1000triples.csv", wait_key=True, name="triplet_test" + ), + task_base.Pause(text="Take a break. Press any key to continue...", wait_key=None), language.Triplet( - 'data/language/triplet/first1000triples.csv', - wait_key=True, - name='triplet_test'), - - task_base.Pause( - text="Take a break. Press to continue...", - wait_key=['a']), - - ] + "data/language/triplet/first1000triples.csv", wait_key=True, name="triplet_test" + ), + task_base.Pause(text="Take a break. Press to continue...", wait_key=["a"]), +] diff --git a/src/sessions/ses-video1.py b/src/sessions/ses-video1.py index 904d3101..d465aaf6 100644 --- a/src/sessions/ses-video1.py +++ b/src/sessions/ses-video1.py @@ -1,23 +1,23 @@ from ..tasks import images, video, memory, task_base TASKS = [ - video.SingleVideo( - 'data/videos/Oceans_fs_10m/Inscapes_sound_normed.mp4', name='Inscapes'), + "data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4", name="Inscapes" + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Inscapes_sound_normed.mp4', name='Inscapes'), - + "data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4", name="Inscapes" + ), task_base.Pause(), - video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_1.mp4', - name='Oceans_fs_10m_1'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_1_filt.mp4", + name="Oceans_fs_10m_1", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_2.mp4', - name='Oceans_fs_10m_2'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_2_filt.mp4", + name="Oceans_fs_10m_2", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_3.mp4', - name='Oceans_fs_10m_3'), - - + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_3_filt.mp4", + name="Oceans_fs_10m_3", + ), ] diff --git a/src/sessions/ses-video2.py b/src/sessions/ses-video2.py index 2e192e14..556c7a72 100644 --- a/src/sessions/ses-video2.py +++ b/src/sessions/ses-video2.py @@ -1,25 +1,27 @@ from ..tasks import images, video, memory, task_base TASKS = [ - video.SingleVideo( - 'data/videos/Oceans_fs_10m/Inscapes_sound_normed.mp4', name='Inscapes'), + "data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4", name="Inscapes" + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Inscapes_sound_normed.mp4', name='Inscapes'), - + "data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4", name="Inscapes" + ), task_base.Pause(), - video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_4.mp4', - name='Oceans_fs_10m_4'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_4_filt.mp4", + name="Oceans_fs_10m_4", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_5.mp4', - name='Oceans_fs_10m_5'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_5_filt.mp4", + name="Oceans_fs_10m_5", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_6.mp4', - name='Oceans_fs_10m_6'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_6_filt.mp4", + name="Oceans_fs_10m_6", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_7.mp4', - name='Oceans_fs_10m_7'), - + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_7_filt.mp4", + name="Oceans_fs_10m_7", + ), ] diff --git a/src/sessions/ses-video3.py b/src/sessions/ses-video3.py index 4858f04b..c88b8a40 100644 --- a/src/sessions/ses-video3.py +++ b/src/sessions/ses-video3.py @@ -1,25 +1,27 @@ from ..tasks import images, video, memory, task_base TASKS = [ - video.SingleVideo( - 'data/videos/Oceans_fs_10m/Inscapes_sound_normed.mp4', name='Inscapes'), + "data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4", name="Inscapes" + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Inscapes_sound_normed.mp4', name='Inscapes'), - + "data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4", name="Inscapes" + ), task_base.Pause(), - video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_8.mp4', - name='Oceans_fs_10m_8'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_8_filt.mp4", + name="Oceans_fs_10m_8", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_9.mp4', - name='Oceans_fs_10m_9'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_9_filt.mp4", + name="Oceans_fs_10m_9", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_10.mp4', - name='Oceans_fs_10m_10'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_10_filt.mp4", + name="Oceans_fs_10m_10", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m/Oceans_fs_10m_11.mp4', - name='Oceans_fs_10m_11'), - + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_11_filt.mp4", + name="Oceans_fs_10m_11", + ), ] diff --git a/src/sessions/ses-video3b.py b/src/sessions/ses-video3b.py index 0a75028a..8052865f 100644 --- a/src/sessions/ses-video3b.py +++ b/src/sessions/ses-video3b.py @@ -1,25 +1,27 @@ from ..tasks import images, video, memory, task_base TASKS = [ - video.SingleVideo( - 'data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4', name='Inscapes'), + "data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4", name="Inscapes" + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4', name='Inscapes'), - + "data/videos/Oceans_fs_10m_filt/Inscapes_sound_normed_filt.mp4", name="Inscapes" + ), task_base.Pause(), - video.SingleVideo( - 'data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_7_filt.mp4', - name='Oceans_fs_10m_7'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_7_filt.mp4", + name="Oceans_fs_10m_7", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_8_filt.mp4', - name='Oceans_fs_10m_8'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_8_filt.mp4", + name="Oceans_fs_10m_8", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_9_filt.mp4', - name='Oceans_fs_10m_9'), + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_9_filt.mp4", + name="Oceans_fs_10m_9", + ), video.SingleVideo( - 'data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_10_filt.mp4', - name='Oceans_fs_10m_10'), - + "data/videos/Oceans_fs_10m_filt/Oceans_fs_10m_10_filt.mp4", + name="Oceans_fs_10m_10", + ), ] diff --git a/src/sessions/ses-video_images_pilot.py b/src/sessions/ses-video_images_pilot.py index 5c288b1c..80ae4065 100644 --- a/src/sessions/ses-video_images_pilot.py +++ b/src/sessions/ses-video_images_pilot.py @@ -1,18 +1,25 @@ from ..tasks import images, video, memory, task_base TASKS = [ - #memory.ImagePosition('data/memory/stimuli.csv', use_fmri=parsed.fmri, use_eyetracking=True), + # memory.ImagePosition('data/memory/stimuli.csv', use_fmri=parsed.fmri, use_eyetracking=True), video.SingleVideo( - 'data/videos/Inscapes-67962604.mp4', name='Inscapes', - use_fmri=parsed.fmri, use_eyetracking=True), + "data/videos/Inscapes-67962604.mp4", + name="Inscapes", + use_fmri=parsed.fmri, + use_eyetracking=True, + ), task_base.Pause(), video.SingleVideo( - 'data/videos/skateboard_fails.mp4', - name='skateboard_fails', - use_fmri=parsed.fmri, use_eyetracking=True), + "data/videos/skateboard_fails.mp4", + name="skateboard_fails", + use_fmri=parsed.fmri, + use_eyetracking=True, + ), images.Images( - 'data/images/test_conditions.csv', - '/home/basile/data/projects/task_stimuli/data/images/bold5000/Scene_Stimuli/Presented_Stimuli/ImageNet', - name='bold5000', - use_fmri=parsed.fmri, use_eyetracking=True) - ] + "data/images/test_conditions.csv", + "/home/basile/data/projects/task_stimuli/data/images/bold5000/Scene_Stimuli/Presented_Stimuli/ImageNet", + name="bold5000", + use_fmri=parsed.fmri, + use_eyetracking=True, + ), +] diff --git a/src/sessions/ses-video_pilot.py b/src/sessions/ses-video_pilot.py index b1e51cb3..3267f2bb 100644 --- a/src/sessions/ses-video_pilot.py +++ b/src/sessions/ses-video_pilot.py @@ -1,10 +1,9 @@ from ..tasks import images, video, memory, task_base TASKS = [ - - video.SingleVideo( - 'data/videos/movies_for_montreal/03_Inscaped_NoScannerSound_h264.mov', name='Inscapes'), video.SingleVideo( - 'data/videos/tammy/Oceans_1.mp4', - name='Oceans_chunk1'), - ] + "data/videos/movies_for_montreal/03_Inscaped_NoScannerSound_h264.mov", + name="Inscapes", + ), + video.SingleVideo("data/videos/tammy/Oceans_1.mp4", name="Oceans_chunk1"), +] diff --git a/src/sessions/ses-videogame_test.py b/src/sessions/ses-videogame_test.py index b0fa8b9d..8528b4d6 100644 --- a/src/sessions/ses-videogame_test.py +++ b/src/sessions/ses-videogame_test.py @@ -1,13 +1,12 @@ - from ..tasks import images, videogame, memory, task_base TASKS = [ - videogame.VideoGame( - state_name='Level1', - scenario='scenario_repeat1', # this scenario repeats the same level - max_duration=10*60, # if when level completed or dead we exceed that time in secs, stop the task - name='ShinobiIIIReturnOfTheNinjaMaster-test'), - #videogame.VideoGameReplay('output/sub-test/ses-test_refactor/sub-test_ses-test_refactor_20190109_154007_ShinobiIIIReturnOfTheNinjaMaster-Genesis_Level4_000.bk2',name='ShinobiIIIReturnOfTheNinjaMaster-replay',), - -] * 3 ###!!!!!!!!!!! REPEAT !!!!!!!!!!!### + state_name="Level1", + scenario="scenario_repeat1", # this scenario repeats the same level + max_duration=10 + * 60, # if when level completed or dead we exceed that time in secs, stop the task + name="ShinobiIIIReturnOfTheNinjaMaster-test", + ), + # videogame.VideoGameReplay('output/sub-test/ses-test_refactor/sub-test_ses-test_refactor_20190109_154007_ShinobiIIIReturnOfTheNinjaMaster-Genesis_Level4_000.bk2',name='ShinobiIIIReturnOfTheNinjaMaster-replay',), +] * 3 ###!!!!!!!!!!! REPEAT !!!!!!!!!!!### diff --git a/src/sessions/ses-videoshorttest.py b/src/sessions/ses-videoshorttest.py index 63f8614c..25cf7e13 100644 --- a/src/sessions/ses-videoshorttest.py +++ b/src/sessions/ses-videoshorttest.py @@ -1,7 +1,5 @@ -from ..tasks import video, task_base +from ..tasks import video, task_base TASKS = [ - - video.SingleVideo( - 'data/videos/bourne_test.mkv', name='testshort'), + video.SingleVideo("data/videos/bourne_test.mkv", name="testshort"), ] diff --git a/src/sessions/ses-wolfwallstreet1.py b/src/sessions/ses-wolfwallstreet1.py index 58bf28c8..df37dfeb 100644 --- a/src/sessions/ses-wolfwallstreet1.py +++ b/src/sessions/ses-wolfwallstreet1.py @@ -1,9 +1,12 @@ from ..tasks import images, video, memory, task_base TASKS = [] -for seg_idx in range(1,7): +for seg_idx in range(1, 7): TASKS.append( video.SingleVideo( - 'data/videos/the_wolf_of_wall_street/the_wolf_of_wall_street_seg%02d.mkv'%seg_idx, - aspect_ratio = 12/5, - name='the_wolf_of_wall_street_seg-%d'%seg_idx)) + "data/videos/the_wolf_of_wall_street/the_wolf_of_wall_street_seg%02d.mkv" + % seg_idx, + aspect_ratio=12 / 5, + name="the_wolf_of_wall_street_seg-%d" % seg_idx, + ) + ) diff --git a/src/sessions/ses-wolfwallstreet2.py b/src/sessions/ses-wolfwallstreet2.py index 5e77d7fc..9ff5c2eb 100644 --- a/src/sessions/ses-wolfwallstreet2.py +++ b/src/sessions/ses-wolfwallstreet2.py @@ -2,9 +2,12 @@ TASKS = [] -for seg_idx in range(7,13): +for seg_idx in range(7, 13): TASKS.append( video.SingleVideo( - 'data/videos/the_wolf_of_wall_street/the_wolf_of_wall_street_seg%02d.mkv'%seg_idx, - aspect_ratio = 12/5, - name='the_wolf_of_wall_street_seg-%d'%seg_idx)) + "data/videos/the_wolf_of_wall_street/the_wolf_of_wall_street_seg%02d.mkv" + % seg_idx, + aspect_ratio=12 / 5, + name="the_wolf_of_wall_street_seg-%d" % seg_idx, + ) + ) diff --git a/src/sessions/ses-wolfwallstreet3.py b/src/sessions/ses-wolfwallstreet3.py index d8fe9b09..be12b191 100644 --- a/src/sessions/ses-wolfwallstreet3.py +++ b/src/sessions/ses-wolfwallstreet3.py @@ -1,9 +1,12 @@ from ..tasks import images, video, memory, task_base TASKS = [] -for seg_idx in range(13,18): +for seg_idx in range(13, 18): TASKS.append( video.SingleVideo( - 'data/videos/the_wolf_of_wall_street/the_wolf_of_wall_street_seg%02d.mkv'%seg_idx, - aspect_ratio = 12/5, - name='the_wolf_of_wall_street_seg-%d'%seg_idx)) + "data/videos/the_wolf_of_wall_street/the_wolf_of_wall_street_seg%02d.mkv" + % seg_idx, + aspect_ratio=12 / 5, + name="the_wolf_of_wall_street_seg-%d" % seg_idx, + ) + ) diff --git a/src/shared/__init__.py b/src/shared/__init__.py index 97cc74af..0b47e1ef 100644 --- a/src/shared/__init__.py +++ b/src/shared/__init__.py @@ -1,2 +1,3 @@ import sys, os -#sys.path.append(os.environ['PUPIL_MODULES_PATH']) + +# sys.path.append(os.environ['PUPIL_MODULES_PATH']) diff --git a/src/shared/cli.py b/src/shared/cli.py index 7e4550d6..019df831 100644 --- a/src/shared/cli.py +++ b/src/shared/cli.py @@ -1,7 +1,11 @@ # CLI: command line interface options and main loop -import os, datetime, traceback, glob +import os, datetime, traceback, glob, time +from collections.abc import Iterable, Iterator from psychopy import core, visual, logging, event +import itertools + +visual.window.reportNDroppedFrames = 10e10 TIMEOUT = 5 DELAY_BETWEEN_TASK = 5 @@ -9,166 +13,281 @@ globalClock = core.MonotonicClock(0) logging.setDefaultClock(globalClock) -from . import config, fmri, eyetracking +from . import config # import first separately +from . import fmri, eyetracking, utils, meg, config from ..tasks import task_base, video -def main_loop(all_tasks, subject, session, enable_eyetracker=False, use_fmri=False, use_meg=False, show_ctl_win = False): - log_path = os.path.abspath(os.path.join(config.OUTPUT_DIR, 'sub-%s'%subject,'ses-%s'%session)) +def listen_shortcuts(): + if any([k[1] & event.MOD_CTRL for k in event._keyBuffer]): + allKeys = event.getKeys(["n", "c", "q"], modifiers=True) + ctrl_pressed = any([k[1]["ctrl"] for k in allKeys]) + all_keys_only = [k[0] for k in allKeys] + if len(allKeys) and ctrl_pressed: + return all_keys_only[0] + return False + + +def run_task_loop(loop, eyetracker=None, gaze_drawer=None, record_movie=False): + for frameN, _ in enumerate(loop): + if gaze_drawer: + gaze = eyetracker.get_gaze() + if not gaze is None: + gaze_drawer.draw_gazepoint(gaze) + if record_movie and frameN % 6 == 0: + record_movie.getMovieFrame(buffer="back") + # check for global event keys + shortcut_evt = listen_shortcuts() + if shortcut_evt: + return shortcut_evt + + +def run_task( + task, exp_win, ctl_win=None, eyetracker=None, gaze_drawer=None, record_movie=False +): + print("Next task: %s" % str(task)) + + # show instruction + shortcut_evt = run_task_loop( + task.instructions(exp_win, ctl_win), + eyetracker, + gaze_drawer, + record_movie=exp_win if record_movie else False, + ) + + if task.use_fmri and not shortcut_evt: + for _ in fmri.wait_for_ttl(): + shortcut_evt = listen_shortcuts() + if shortcut_evt: + return shortcut_evt + + logging.info("GO") + if eyetracker and not shortcut_evt: + eyetracker.start_recording(task.name) + # send start trigger/marker to MEG + Biopac (or anything else on parallel port) + if task.use_meg and not shortcut_evt: + meg.send_signal(meg.MEG_settings["TASK_START_CODE"]) + + if not shortcut_evt: + shortcut_evt = run_task_loop( + task.run(exp_win, ctl_win), + eyetracker, + gaze_drawer, + record_movie=exp_win if record_movie else False, + ) + + # send stop trigger/marker to MEG + Biopac (or anything else on parallel port) + if task.use_meg and not shortcut_evt: + meg.send_signal(meg.MEG_settings["TASK_STOP_CODE"]) + + if eyetracker: + eyetracker.stop_recording() + + run_task_loop( + task.stop(exp_win, ctl_win), + eyetracker, + gaze_drawer, + record_movie=exp_win if record_movie else False, + ) + + # now that time is less sensitive: save files + task.save() + + return shortcut_evt + + +def main_loop( + all_tasks, + subject, + session, + output_ds, + enable_eyetracker=False, + use_fmri=False, + use_meg=False, + show_ctl_win=False, + allow_run_on_battery=False, + enable_ptt=False, + record_movie=False, +): + + # force screen resolution to solve issues with video splitter at scanner + """xrandr = Popen([ + 'xrandr', + '--output', 'eDP-1', + '--mode', '%dx%d'%config.EXP_WINDOW['size'], + '--rate', str(config.FRAME_RATE)]) + time.sleep(5)""" + + if not utils.check_power_plugged(): + print("*" * 25 + "WARNING: the power cord is not connected" + "*" * 25) + if not allow_run_on_battery: + return + + bids_sub_ses = ("sub-%s" % subject, "ses-%s" % session) + log_path = os.path.abspath(os.path.join(output_ds, "sourcedata", *bids_sub_ses)) if not os.path.exists(log_path): os.makedirs(log_path, exist_ok=True) - log_name_prefix = 'sub-%s_ses-%s_%s'%(subject,session, datetime.datetime.now().strftime('%Y%m%d_%H%M%S')) - logfile_path = os.path.join(log_path, log_name_prefix+'.log') - log_file = logging.LogFile( - logfile_path, - level=logging.INFO, filemode='w') + log_name_prefix = "sub-%s_ses-%s_%s" % ( + subject, + session, + datetime.datetime.now().strftime("%Y%m%d-%H%M%S"), + ) + logfile_path = os.path.join(log_path, log_name_prefix + ".log") + log_file = logging.LogFile(logfile_path, level=logging.INFO, filemode="w") + + exp_win = visual.Window(**config.EXP_WINDOW, monitor=config.EXP_MONITOR) + exp_win.mouseVisible = False if show_ctl_win: ctl_win = visual.Window(**config.CTL_WINDOW) - ctl_win.winHandle.set_caption('Stimuli') + ctl_win.name = "Stimuli" else: ctl_win = None - exp_win = visual.Window(**config.EXP_WINDOW) - exp_win.mouseVisible = False + ptt = None + if enable_ptt: + from .ptt import PushToTalk + + ptt = PushToTalk() + + eyetracker_client = None + gaze_drawer = None if enable_eyetracker: - print('creating et client') + print("creating et client") eyetracker_client = eyetracking.EyeTrackerClient( output_path=log_path, - output_fname_base=log_name_prefix - ) - print('starting et client') + output_fname_base=log_name_prefix, + profile=False, + debug=False, + ) + print("starting et client") eyetracker_client.start() - print('done') - #all_tasks.insert(0, eyetracking.EyetrackerCalibration(eyetracker_client,name='EyeTracker-Calibration')) - gaze_drawer = eyetracking.GazeDrawer(ctl_win) - if use_fmri: - setup_video_path = glob.glob(os.path.join('data','videos','subject_setup_videos','sub-%s_*'%subject)) - if not len(setup_video_path): - setup_video_path = [os.path.join('data','videos','subject_setup_videos','sub-default_setup_video.mp4')] + print("done") + all_tasks = sum(([ + eyetracking.EyetrackerCalibration( + eyetracker_client, name="EyeTracker-Calibration" + ), t] for t in all_tasks), []) - all_tasks.insert(0, video.VideoAudioCheckLoop(setup_video_path[0], name='setup_soundcheck_video')) - all_tasks.insert(1, task_base.Pause("""We are completing the setup and initializing the scanner. + if show_ctl_win: + gaze_drawer = eyetracking.GazeDrawer(ctl_win) + if use_fmri: + all_tasks = itertools.chain( + [task_base.Pause( + """We are completing the setup and initializing the scanner. We will start the tasks in a few minutes. -Please remain still.""")) - all_tasks.append(task_base.Pause("""We are done for today. +Please remain still.""" + )], + all_tasks, + [task_base.Pause( + """We are done for today. The scanner might run for a few seconds to acquire reference images. Please remain still. -We are coming to get you out of the scanner shortly.""")) +We are coming to get you out of the scanner shortly.""" + )], + ) + + if not skip_soundcheck: + setup_video_path = utils.get_subject_soundcheck_video(subject) + all_tasks = itertools.chain([ + video.VideoAudioCheckLoop(setup_video_path, name="setup_soundcheck_video",)], + all_tasks, + ) + + else: - all_tasks.append(task_base.Pause("""We are done with the tasks for today. -Thanks for your participation!""")) - # list of tasks to be ran in a session + all_tasks = itertools.chain( + all_tasks, + [task_base.Pause( + """We are done with the tasks for today. +Thanks for your participation!""" + )], + ) + + if not isinstance(all_tasks, Iterator): + + # list of tasks to be ran in a session - print('Here are the stimuli planned for today\n' + '_'*50) - for task in all_tasks: - print('- ' + task.name) - print('_'*50) + print("Here are the stimuli planned for today\n" + "_" * 50) + for task in all_tasks: + print(f"- {task.name} {getattr(task,'duration','')}" ) + print("_" * 50) try: for task in all_tasks: - #clear events buffer in case the user pressed a lot of buttoons + # clear events buffer in case the user pressed a lot of buttoons event.clearEvents() - # ensure to clear the screen if task aborted - exp_win.flip() - if show_ctl_win: - ctl_win.flip() use_eyetracking = False if enable_eyetracker and task.use_eyetracking: use_eyetracking = True - #setup task files (eg. video) - task.setup(exp_win, log_path, log_name_prefix, + # setup task files (eg. video) + task.setup( + exp_win, + log_path, + log_name_prefix, use_fmri=use_fmri, use_eyetracking=use_eyetracking, - use_meg=use_meg) - print('READY') - - allKeys = [] - ctrl_pressed = False + use_meg=use_meg, + ) + print("READY") while True: - #force focus on the task window to ensure getting keys, TTL, ... + # force focus on the task window to ensure getting keys, TTL, ... exp_win.winHandle.activate() + # record frame intervals for debug - for draw in task.run(exp_win, ctl_win): - - if use_eyetracking: - gaze = eyetracker_client.get_gaze() - if not gaze is None: - gaze_drawer.draw_gazepoint(gaze) - # check for global event keys - exp_win.flip() - if show_ctl_win: - ctl_win.flip() - - if any([k[1]&event.MOD_CTRL for k in event._keyBuffer]): - allKeys = event.getKeys(['n','c','q'], modifiers=True) - ctrl_pressed = any([k[1]['ctrl'] for k in allKeys]) - all_keys_only = [k[0] for k in allKeys] - if len(allKeys) and ctrl_pressed: - break - else: # task completed - task.save() + shortcut_evt = run_task( + task, + exp_win, + ctl_win, + eyetracker_client, + gaze_drawer, + record_movie=record_movie, + ) + + if shortcut_evt == "n": + # restart the task + logging.exp(msg="task - %s: restart" % str(task)) + task.restart() + continue + elif shortcut_evt: + # abort/skip or quit + logging.exp(msg="task - %s: abort" % str(task)) + break + else: # task completed + logging.exp(msg="task - %s: complete" % str(task)) + # send stop trigger/marker to MEG + Biopac (or anything else on parallel port) break - task.save() logging.flush() - task.stop() - - # ensure last frame or task clear is draw - exp_win.flip() - if show_ctl_win: - ctl_win.flip() - - if ctrl_pressed and ('c' in all_keys_only or 'q' in all_keys_only): - break - logging.exp(msg="task - %s: restart"%str(task)) - task.restart() + if record_movie: + out_fname = os.path.join( + task.output_path, "%s_%s.mp4" % (task.output_fname_base, task.name) + ) + print(f"saving movie as {out_fname}") + exp_win.saveMovieFrames(out_fname, fps=10) task.unload() - if ctrl_pressed: - if 'q' in all_keys_only: - print('quit') - break - else: - print('skip') - else: + if shortcut_evt == "q": + print("quit") + break + elif shortcut_evt is None: # add a delay between tasks to avoid remaining TTL to start next task - for i in range(DELAY_BETWEEN_TASK*config.FRAME_RATE): - exp_win.flip() + # do that only if the task was not aborted to save time + # there is anyway the duration of the instruction before listening to TTL + for i in range(DELAY_BETWEEN_TASK * config.FRAME_RATE): + exp_win.flip(clearBuffer=False) + + exp_win.saveFrameIntervals("exp_win_frame_intervals.txt") + if ctl_win: + ctl_win.saveFrameIntervals("ctl_win_frame_intervals.txt") except KeyboardInterrupt as ki: print(traceback.format_exc()) logging.exp(msg="user killing the program") - print('you killing me!') + print("you killing me!") finally: if enable_eyetracker: eyetracker_client.join(TIMEOUT) - -def parse_args(): - import argparse - parser = argparse.ArgumentParser( - prog='main.py', - description=('Run all tasks in a session'), - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('--subject', '-s', - help='Subject ID') - parser.add_argument('--session', '-ss', - help='Session ID') - parser.add_argument('--fmri', '-f', - help='Wait for fmri TTL to start each task', - action='store_true') - parser.add_argument('--meg', '-m', - help='Send signal to parallel port to start trigger to MEG and Biopac.', - action='store_true') - parser.add_argument('--eyetracking', '-e', - help='Enable eyetracking', - action='store_true') - parser.add_argument('--skip_n_tasks', - help='skip n of the tasks', - default=0, - type=int) - return parser.parse_args() diff --git a/src/shared/config.py b/src/shared/config.py index 09cd831e..8ae82cfe 100644 --- a/src/shared/config.py +++ b/src/shared/config.py @@ -1,35 +1,54 @@ +from dotenv import load_dotenv +load_dotenv() + from psychopy import prefs +from psychopy.monitors import Monitor # avoids delay in movie3 audio seek -prefs.general['audioLib'] = ['sounddevice'] +prefs.hardware["audioLib"] = ["sounddevice"] +# prefs.hardware['general'] = ['glfw'] + +TR = 1.49 #seconds + +OUTPUT_DIR = "output" +EYETRACKING_ROI = (60, 30, 660, 450) -OUTPUT_DIR = 'output' +EXP_SCREEN_XRANDR_NAME = "eDP-1" -EYETRACKING_ROI = (60,30,660,450) +EXP_MONITOR = Monitor( + name='__blank__', + width=55, + distance=180, + ) EXP_WINDOW = dict( -# size = (800,600), - #size = (1024,768), - size = (1920,1080), + size=(1280, 1024), screen=1, fullscr=True, - gammaErrorPolicy='warn', + gammaErrorPolicy="warn", + #waitBlanking=False, ) +EXP_MONITOR.setSizePix(EXP_WINDOW['size']) + CTL_WINDOW = dict( - size = (1920,1080), - pos = (100,0), + size=(1280, 1024), + pos=(100, 0), screen=0, - gammaErrorPolicy='warn', + gammaErrorPolicy="warn", + # swapInterval=0., + waitBlanking=False, # avoid ctrl window to block the script in case of differing refresh rate. ) -FRAME_RATE=60 +FRAME_RATE = 60 # task parameters INSTRUCTION_DURATION = 6 -WRAP_WIDTH = 1 + +WRAP_WIDTH = 2 + # port for meg setup -PARALLEL_PORT_ADDRESS = '/dev/parport0' +PARALLEL_PORT_ADDRESS = "/dev/parport0" diff --git a/src/shared/didyoumean.py b/src/shared/didyoumean.py new file mode 100644 index 00000000..d8367fc1 --- /dev/null +++ b/src/shared/didyoumean.py @@ -0,0 +1,10 @@ +import os +from importlib.util import find_spec +from textdistance import jaro + + +def suggest_session_tasks(query): + ses_dir_path = find_spec('src.sessions').submodule_search_locations._path[0] + avail_sess = [s.replace("ses-","").replace(".py","") for s in os.listdir(ses_dir_path)] + best_match = max(avail_sess, key=lambda x: jaro(x, query)) + return best_match diff --git a/src/shared/ellipse.py b/src/shared/ellipse.py index 69b5649e..e6520430 100644 --- a/src/shared/ellipse.py +++ b/src/shared/ellipse.py @@ -11,13 +11,14 @@ import numpy + class Ellipse(Polygon): """Creates a Circle with a given radius as a special case of a :class:`~psychopy.visual.ShapeStim` (New in version 1.72.00) """ - def __init__(self, win, radius=.5, radius2=.5, edges=32, **kwargs): + def __init__(self, win, radius=0.5, radius2=0.5, edges=32, **kwargs): """ Circle accepts all input parameters that `~psychopy.visual.ShapeStim` accept, @@ -26,15 +27,15 @@ def __init__(self, win, radius=.5, radius2=.5, edges=32, **kwargs): # what local vars are defined (these are the init params) for use by # __repr__ self._initParams = dir() - self._initParams.remove('self') + self._initParams.remove("self") # kwargs isn't a parameter, but a list of params - self._initParams.remove('kwargs') + self._initParams.remove("kwargs") self._initParams.extend(kwargs) # initialise parent class - kwargs['edges'] = edges - kwargs['radius'] = radius - self.__dict__['radius2'] = numpy.asarray(radius2) + kwargs["edges"] = edges + kwargs["radius"] = radius + self.__dict__["radius2"] = numpy.asarray(radius2) super(Ellipse, self).__init__(win, **kwargs) @attributeSetter @@ -46,12 +47,17 @@ def radius2(self, radius2): Usually there's a setAttribute(value, log=False) method for each attribute. Use this if you want to disable logging. """ - self.__dict__['radius2'] = numpy.array(radius2) + self.__dict__["radius2"] = numpy.array(radius2) self._calcVertices() self.setVertices(self.vertices, log=False) def _calcVertices(self): d = numpy.pi * 2 / self.edges self.vertices = numpy.asarray( - [numpy.asarray((numpy.sin(e * d) * self.radius, numpy.cos(e * d) * self.radius2)) - for e in range(int(round(self.edges)))]) + [ + numpy.asarray( + (numpy.sin(e * d) * self.radius, numpy.cos(e * d) * self.radius2) + ) + for e in range(int(round(self.edges))) + ] + ) diff --git a/src/shared/eyetracking.py b/src/shared/eyetracking.py index c0766b91..41c34fb1 100644 --- a/src/shared/eyetracking.py +++ b/src/shared/eyetracking.py @@ -13,103 +13,193 @@ INSTRUCTION_DURATION = 5 -CALIBRATE_HOTKEY = 'c' +CALIBRATE_HOTKEY = "c" INSTRUCTION_DURATION = 5 MARKER_SIZE = 50 -MARKER_FILL_COLOR = (.0,1,.0) -MARKER_DURATION_FRAMES = 120 # at 60fps -MARKER_POSITIONS = np.asarray([(0, .5), (1,0.5)]*10 + [(0.5, 0), (0.5,1)]*10) +MARKER_FILL_COLOR = (0.8, 0, 0.5) +MARKER_DURATION_FRAMES = 240 +MARKER_POSITIONS = np.asarray( + [ + (0.25, 0.5), + (0, 0.5), + (0.0, 1.0), + (0.5, 1.0), + (1.0, 1.0), + (1.0, 0.5), + (1.0, 0.0), + (0.5, 0.0), + (0.0, 0.0), + (0.75, 0.5), + ] +) + # number of frames to eliminate at start and end of marker CALIBRATION_LEAD_IN = 20 CALIBRATION_LEAD_OUT = 20 -# remove pupil samples with low confidence -PUPIL_CONFIDENCE_THRESHOLD = .4 -class EyetrackerCalibration(Task): +# Pupil settings +PUPIL_REMOTE_PORT = 50123 +CAPTURE_SETTINGS = { + "frame_size": [640, 480], + "frame_rate": 250, + "exposure_time": 4000, + "global_gain": 1, + "gev_packet_size": 1400, + "uid": "Aravis-Fake-GV01", # for test purposes + # "uid": "MRC Systems GmbH-GVRD-MRC HighSpeed-MR_CAM_HS_0014", +} + - def __init__(self,eyetracker, order='random', marker_fill_color=MARKER_FILL_COLOR, **kwargs): +class EyetrackerCalibration(Task): + def __init__( + self, + eyetracker, + markers_order="random", + marker_fill_color=MARKER_FILL_COLOR, + markers=MARKER_POSITIONS, + **kwargs, + ): self.use_eyetracking = True - self.order = order + self.markers_order = markers_order + self.markers = markers self.marker_fill_color = marker_fill_color super().__init__(**kwargs) self.eyetracker = eyetracker - def instructions(self, exp_win, ctl_win): + def _instructions(self, exp_win, ctl_win): instruction_text = """We're going to calibrate the eyetracker. -Please look at the markers that appear on the screen.""" +Please look at the markers that appear on the screen. + +While awaiting for the calibration to start please roll your eyes in one direction then in the other.""" screen_text = visual.TextStim( - exp_win, text=instruction_text, - alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) + exp_win, + text=instruction_text, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) for frameN in range(config.FRAME_RATE * INSTRUCTION_DURATION): screen_text.draw(exp_win) screen_text.draw(ctl_win) - yield() + yield True def _setup(self, exp_win): self.use_fmri = False - def _run(self, exp_win, ctl_win): - while True: - allKeys = event.getKeys([CALIBRATE_HOTKEY]) - start_calibration = False - for key in allKeys: - if key == CALIBRATE_HOTKEY: - start_calibration = True - if start_calibration: - break - yield - print('calibration started') - - window_size_frame = exp_win.size-MARKER_SIZE*2 - print(window_size_frame) - circle_marker = visual.Circle( - exp_win, edges=64, units='pixels', - lineColor=None,fillColor=self.marker_fill_color, - autoLog=False) - - random_order = np.random.permutation(np.arange(len(MARKER_POSITIONS))) - - all_refs_per_flip = [] - all_pupils = [] - - radius_anim = np.hstack([np.linspace(MARKER_SIZE,0,MARKER_DURATION_FRAMES/2), - np.linspace(0,MARKER_SIZE,MARKER_DURATION_FRAMES/2)]) - - pupil = None - while pupil is None: # wait until we get at least a pupil - pupil = self.eyetracker.get_pupil() - yield - - exp_win.logOnFlip(level=logging.EXP,msg='eyetracker_calibration: starting at %f'%time.time()) - for site_id in random_order: - marker_pos = MARKER_POSITIONS[site_id] - pos = (marker_pos-.5)*window_size_frame - circle_marker.pos = pos - exp_win.logOnFlip(level=logging.EXP, - msg="calibrate_position,%d,%d,%d,%d"%(marker_pos[0],marker_pos[1], pos[0],pos[1])) - for f,r in enumerate(radius_anim): - circle_marker.radius = r - circle_marker.draw(exp_win) - circle_marker.draw(ctl_win) - - pupil = self.eyetracker.get_pupil() + def _pupil_cb(self, pupil): + if pupil["timestamp"] > self.task_stop: + self.eyetracker.unset_pupil_cb() + return + if pupil["timestamp"] > self.task_start: + self._pupils_list.append(pupil) - exp_win.logOnFlip(level=logging.EXP, - msg="pupil: pos=(%f,%f), diameter=%d"%tuple(pupil['norm_pos']+[pupil['diameter']])) - if f > CALIBRATION_LEAD_IN and f < len(radius_anim)-CALIBRATION_LEAD_OUT: - if pupil and pupil['confidence'] > PUPIL_CONFIDENCE_THRESHOLD: - pos_decenter = (pos/exp_win.size*2).tolist() + def _run(self, exp_win, ctl_win): + calibration_success = False + while not calibration_success: + while True: + allKeys = event.getKeys([CALIBRATE_HOTKEY]) + start_calibration = False + for key in allKeys: + if key == CALIBRATE_HOTKEY: + start_calibration = True + if start_calibration: + break + yield False + logging.info("calibration started") + print("calibration started") + + window_size_frame = exp_win.size - MARKER_SIZE * 2 + circle_marker = visual.Circle( + exp_win, + edges=64, + units="pixels", + lineColor=None, + fillColor=self.marker_fill_color, + autoLog=False, + ) + + markers_order = np.arange(len(self.markers)) + if self.markers_order == "random": + markers_order = np.random.permutation(markers_order) + + self.all_refs_per_flip = [] + self._pupils_list = [] + + radius_anim = np.hstack( + [ + np.linspace(MARKER_SIZE, 0, MARKER_DURATION_FRAMES // 2), + np.linspace(0, MARKER_SIZE, MARKER_DURATION_FRAMES // 2), + ] + ) + + self.task_start = time.monotonic() + self.task_stop = np.inf + self.eyetracker.set_pupil_cb(self._pupil_cb) + + while not len(self._pupils_list): # wait until we get at least a pupil + yield False + + exp_win.logOnFlip( + level=logging.EXP, + msg="eyetracker_calibration: starting at %f" % time.time(), + ) + for site_id in markers_order: + marker_pos = self.markers[site_id] + pos = (marker_pos - 0.5) * window_size_frame + circle_marker.pos = pos + exp_win.logOnFlip( + level=logging.EXP, + msg="calibrate_position,%d,%d,%d,%d" + % (marker_pos[0], marker_pos[1], pos[0], pos[1]), + ) + exp_win.callOnFlip( + self._log_event, {"marker_x": pos[0], "marker_y": pos[1]} + ) + for f, r in enumerate(radius_anim): + circle_marker.radius = r + circle_marker.draw(exp_win) + circle_marker.draw(ctl_win) + + if ( + f > CALIBRATION_LEAD_IN + and f < len(radius_anim) - CALIBRATION_LEAD_OUT + ): + screen_pos = pos + exp_win.size / 2 + norm_pos = screen_pos / exp_win.size ref = { - 'norm_pos': pos_decenter, - 'screen_pos': pos_decenter, - 'timestamp': pupil['timestamp']} - all_refs_per_flip.append(ref) - all_pupils.append(pupil) - yield - self.eyetracker.calibrate(all_pupils, all_refs_per_flip, exp_win.size) + "norm_pos": norm_pos.tolist(), + "screen_pos": screen_pos.tolist(), + "timestamp": time.monotonic(), # =pupil frame timestamp on same computer + } + self.all_refs_per_flip.append(ref) # accumulate all refs + yield True + yield True + self.task_stop = time.monotonic() + logging.info( + f"calibrating on {len(self._pupils_list)} pupils and {len(self.all_refs_per_flip)} markers" + ) + self.eyetracker.calibrate(self._pupils_list, self.all_refs_per_flip) + while True: + notes = getattr(self.eyetracker, '_last_calibration_notification',None) + if notes: + calibration_success = notes['topic'].startswith("notify.calibration.successful") + if not calibration_success: + print('#### CALIBRATION FAILED: restart with ####') + break + + + def stop(self, exp_win, ctl_win): + self.eyetracker.unset_pupil_cb() + yield + + def _save(self): + if hasattr(self, "_pupils_list"): + fname = self._generate_unique_filename("calib-data", "npz") + np.savez(fname, pupils=self._pupils_list, markers=self.all_refs_per_flip) + class EyetrackerTask(Task): @@ -119,8 +209,7 @@ def __init__(self, order='random', marker_fill_color=MARKER_FILL_COLOR, **kwargs super().__init__(**kwargs) def instructions(self, exp_win, ctl_win): - instruction_text = """We're going to calibrate the eyetracker. -Please look at the markers that appear on the screen.""" + instruction_text = """Please look at the markers that appear on the screen.""" screen_text = visual.TextStim( exp_win, text=instruction_text, alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) @@ -189,144 +278,250 @@ def _run(self, exp_win, ctl_win): from subprocess import Popen +from contextlib import contextmanager + + +@contextmanager +def nonblocking(lock): + locked = lock.acquire(False) + try: + yield locked + finally: + if locked: + lock.release() + + class EyeTrackerClient(threading.Thread): - def __init__(self, output_path, output_fname_base): + EYE = "eye0" + + def __init__(self, output_path, output_fname_base, profile=False, debug=False): super(EyeTrackerClient, self).__init__() self.stoprequest = threading.Event() self.lock = threading.Lock() self.pupil = None self.gaze = None + self.unset_pupil_cb() self.output_path = output_path self.output_fname_base = output_fname_base - self.record_dir = os.path.join(self.output_path, self.output_fname_base + '.pupil') - os.makedirs(self.record_dir) - - self._pupil_process = Popen([ - 'python3', - os.path.join(os.environ['PUPIL_PATH'],'pupil_src','main.py'), - 'capture']) + self.record_dir = os.path.join( + self.output_path, self.output_fname_base + ".pupil" + ) + os.makedirs(self.record_dir, exist_ok=True) + + dev_opts = [] + if debug: + dev_opts.append("--debug") + if profile: + dev_opts.append("--profile") + + self._pupil_process = Popen( + [ + "python3", + os.path.join(os.environ["PUPIL_PATH"], "pupil_src", "main.py"), + "capture", + "--port", + str(PUPIL_REMOTE_PORT), + ] + + dev_opts + ) self._ctx = zmq.Context() self._req_socket = self._ctx.socket(zmq.REQ) - self._req_socket.connect('tcp://localhost:50020') + self._req_socket.connect(f"tcp://localhost:{PUPIL_REMOTE_PORT}") + + # stop eye1 if started: monocular eyetracking in the MRI + notif = self.send_recv_notification( + {"subject": "eye_process.should_stop.1", "eye_id": 1, "args": {}} + ) # start eye0 if not started yet (from pupil saved config) - self.send_recv_notification({ - 'subject':'eye_process.should_start.0', - 'eye_id':0, 'args':{}}) - # setup recorder output path - # quit existing plugin - self.send_recv_notification({ - 'subject':'stop_plugin', - 'name':'Recorder',}) - self.send_recv_notification({ - 'subject':'stop_plugin', - 'name':'Accuracy_Visualizer','args':{}}) - - #restart with new params - self.send_recv_notification({ - 'subject':'start_plugin', - 'name':'Fixed_Screen_Marker_Calibration', - 'args':{'fullscreen':True, 'marker_scale':.8, 'sample_duration':120, 'monitor_idx':1}}) - self.send_recv_notification({ - 'subject':'start_plugin', - 'name':'Recorder','args':{'rec_path':self.record_dir,'rec_root_dir':self.record_dir,'raw_jpeg':False}}) - self.send_recv_notification({ - 'subject':'start_plugin', - 'name':'Pupil_Remote','args':{}}) - - self.send_recv_notification({'subject':'recording.should_start',}) - # wait for the whole schmilblick to boot - time.sleep(4) + notif = self.send_recv_notification( + {"subject": "eye_process.should_start.0", "eye_id": 0, "args": {}} + ) + + # wait for eye process to start before starting plugins + time.sleep(1) + + # quit existing recorder plugin + self.send_recv_notification( + { + "subject": "stop_plugin", + "name": "Recorder", + } + ) + # restart recorder plugin with custom output settings + self.send_recv_notification( + { + "subject": "start_plugin", + "name": "Recorder", + "args": { + "rec_root_dir": self.record_dir, + "session_name": self.output_fname_base + ".pupil", + "raw_jpeg": False, + "record_eye": True, + }, + } + ) + + # restart 2d detector plugin with custom output settings + self.send_recv_notification( + { + "subject": "start_eye_plugin", + "name": "Detector2DPlugin", + "target": self.EYE, + "args": { + "properties": { + "intensity_range": 4, + } + }, + } + ) + + # stop a bunch of eye plugins for performance + for plugin in ["NDSI_Manager"]: + self.send_recv_notification( + { + "subject": "stop_eye_plugin", + "target": self.EYE, + "name": plugin, + } + ) + + self.send_recv_notification( + { + "subject": "start_eye_plugin", + "name": "Aravis_Source", + "target": self.EYE, + "args": CAPTURE_SETTINGS, + } + ) def send_recv_notification(self, n): - # REQ REP requirese lock step communication with multipart msg (topic,msgpack_encoded dict) - self._req_socket.send_multipart((bytes('notify.%s'%n['subject'],'utf-8'), msgpack.dumps(n))) + # REQ REP requires lock step communication with multipart msg (topic,msgpack_encoded dict) + self._req_socket.send_multipart( + (bytes("notify.%s" % n["subject"], "utf-8"), msgpack.dumps(n)) + ) return self._req_socket.recv() def get_pupil_timestamp(self): - self._req_socket.send('t') #see Pupil Remote Plugin for details + self._req_socket.send("t") # see Pupil Remote Plugin for details return float(self._req_socket.recv()) + def start_recording(self, recording_name): + logging.info("starting eyetracking recording") + return self.send_recv_notification( + {"subject": "recording.should_start", "session_name": recording_name} + ) + + def stop_recording(self): + logging.info("stopping eyetracking recording") + return self.send_recv_notification({"subject": "recording.should_stop"}) + def join(self, timeout=None): self.stoprequest.set() # stop recording - self.send_recv_notification({'subject':'recording.should_stop',}) + self.send_recv_notification( + { + "subject": "recording.should_stop", + } + ) # stop world and children process - self.send_recv_notification({'subject':'world_process.should_stop'}) + self.send_recv_notification({"subject": "world_process.should_stop"}) + self.send_recv_notification({"subject": "launcher_process.should_stop"}) self._pupil_process.wait(timeout) - + self._pupil_process.terminate() + time.sleep(1 / 60.0) super(EyeTrackerClient, self).join(timeout) def run(self): - self._req_socket.send_string('SUB_PORT') + self._req_socket.send_string("SUB_PORT") ipc_sub_port = int(self._req_socket.recv()) - self.pupil_monitor = Msg_Receiver(self._ctx,'tcp://localhost:%d'%ipc_sub_port,topics=('gaze','pupil')) + logging.info(f"ipc_sub_port: {ipc_sub_port}") + self.pupil_monitor = Msg_Receiver( + self._ctx, f"tcp://localhost:{ipc_sub_port}", + topics=("gaze", "pupil", "notify.calibration.successful", "notify.calibration.failed") + ) while not self.stoprequest.isSet(): msg = self.pupil_monitor.recv() if not msg is None: topic, tmp = msg with self.lock: - if topic.startswith('pupil'): + if topic.startswith("pupil"): self.pupil = tmp - if topic.startswith('gaze'): + if self._pupil_cb: + self._pupil_cb(tmp) + elif topic.startswith("gaze"): self.gaze = tmp + elif topic.startswith("notify.calibration"): + self._last_calibration_notification = tmp + time.sleep(1e-3) + logging.info("eyetracker listener: stopping") - print('eyetracker listener: stopping') + def set_pupil_cb(self, pupil_cb): + self._pupil_cb = pupil_cb + def unset_pupil_cb(self): + self._pupil_cb = None def get_pupil(self): - with self.lock: - return self.pupil + with nonblocking(self.lock) as locked: + if locked: + return self.pupil def get_gaze(self): - with self.lock: - return self.gaze + with nonblocking(self.lock) as locked: + if locked: + return self.gaze - def calibrate(self, pupil_list, ref_list, frame_size): + def calibrate(self, pupil_list, ref_list): if len(pupil_list) < 100: - # TODO: log - return - - self.send_recv_notification({ - 'subject':'start_plugin', - 'name':'Mock_Calibration', - 'args':{'frame_size': frame_size.tolist()}}) + logging.error("Calibration: not enough pupil captured for calibration") + # return - self.send_recv_notification({ - 'subject':'calibrate.from_external_data', - 'pupil_list':pupil_list, - 'ref_list':ref_list}) + calib_data = {"ref_list": ref_list, "pupil_list": pupil_list} + logging.info("sending calibration data to pupil") + calib_res = self.send_recv_notification( + { + "subject": "start_plugin", + "name": "Gazer2D", + "args": {"calib_data": calib_data}, + "raise_calibration_error": False, + } + ) -class GazeDrawer(): +class GazeDrawer: def __init__(self, win): self.win = win self._gazepoint_stim = visual.Circle( self.win, radius=30, - units='pixels', - lineColor=(1,0,0),fillColor=None, lineWidth=2, - autoLog=False) + units="pixels", + lineColor=(1, 0, 0), + fillColor=None, + lineWidth=2, + autoLog=False, + ) def draw_gazepoint(self, gaze): - pos = gaze['norm_pos'] - self._gazepoint_stim.pos = (int(pos[0]/2*self.win.size[0]), - int(pos[1]/2*self.win.size[1])) - #self._gazepoint_stim.radius = self.pupils['diameter']/2 - #print(self._gazepoint_stim.pos, self._gazepoint_stim.radius) + pos = gaze["norm_pos"] + self._gazepoint_stim.pos = ( + int(pos[0] / 2 * self.win.size[0]), + int(pos[1] / 2 * self.win.size[1]), + ) + # self._gazepoint_stim.radius = self.pupils['diameter']/2 + # print(self._gazepoint_stim.pos, self._gazepoint_stim.radius) self._gazepoint_stim.draw(self.win) - def read_pl_data(fname): with open(fname, "rb") as fh: for data in msgpack.Unpacker(fh, raw=False, use_list=False): - yield(data) + yield (data) diff --git a/src/shared/fmri.py b/src/shared/fmri.py index 879f3ad4..e3eb997d 100644 --- a/src/shared/fmri.py +++ b/src/shared/fmri.py @@ -1,17 +1,34 @@ from psychopy import core, event, logging from psychopy.hardware.emulator import launchScan +import time MR_settings = { - 'TR': 2.000, # duration (sec) per whole-brain volume - 'sync': '5', # character to use as the sync timing event; assumed to come at start of a volume - 'skip': 0, # number of volumes lacking a sync pulse at start of scan (for T1 stabilization) - } + "TR": 2.000, # duration (sec) per whole-brain volume + "sync": "5", # character to use as the sync timing event; assumed to come at start of a volume + "skip": 0, # number of volumes lacking a sync pulse at start of scan (for T1 stabilization) +} globalClock = core.Clock() + def get_ttl(): - allKeys = event.getKeys([MR_settings['sync']]) + allKeys = event.getKeys([MR_settings["sync"]]) for key in allKeys: - if key.lower() == MR_settings['sync']: + if key.lower() == MR_settings["sync"]: return True return False + + +# blocking function (iterator) +def wait_for_ttl(): + get_ttl() # flush any remaining TTL keys + ttl_index = 0 + logging.exp(msg="waiting for fMRI TTL") + while True: + if get_ttl(): + # TODO: log real timing of TTL? + logging.exp(msg="fMRI TTL %d" % ttl_index) + ttl_index += 1 + return + time.sleep(0.0005) # just to avoid looping to fast + yield diff --git a/src/shared/meg.py b/src/shared/meg.py index b64732ed..054135ca 100644 --- a/src/shared/meg.py +++ b/src/shared/meg.py @@ -4,8 +4,8 @@ # general triggers, common to every MEG protocoles MEG_settings = { - 'TASK_START_CODE': int("00000010", 2), - 'TASK_STOP_CODE': int("00000100", 2), + "TASK_START_CODE": int("00000010", 2), + "TASK_START_STOP": int("00000100", 2), } # triggers for the eye-movements task @@ -20,8 +20,9 @@ 'SP_down': int('00010001', 2) } + def send_signal(data): port = parallel.ParallelPort(address=config.PARALLEL_PORT_ADDRESS) port.setData(data) time.sleep(0.001) - port.setData(0) #reset + port.setData(0) # reset diff --git a/src/shared/parser.py b/src/shared/parser.py new file mode 100644 index 00000000..00a0e5be --- /dev/null +++ b/src/shared/parser.py @@ -0,0 +1,47 @@ +import argparse + + +def parse_args(): + parser = argparse.ArgumentParser( + prog="main.py", + description=("Run all tasks in a session"), + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument("--subject", "-s", required=True, help="Subject ID") + parser.add_argument("--session", "-ss", required=True, help="Session") + parser.add_argument("--tasks", "-t", required=True, help="tasks set") + parser.add_argument("--output", "-o", required=True, help="output dataset") + parser.add_argument( + "--fmri", "-f", help="Wait for fmri TTL to start each task", action="store_true" + ) + parser.add_argument( + "--meg", + "-m", + help="Send signal to parallel port to start trigger to MEG and Biopac.", + action="store_true", + ) + parser.add_argument( + "--eyetracking", "-e", help="Enable eyetracking", action="store_true" + ) + parser.add_argument( + "--skip_n_tasks", help="skip n of the tasks", default=0, type=int + ) + parser.add_argument("--ctl_win", help="show control window", action="store_true") + parser.add_argument( + "--no-force-resolution", + help="do not run xrandr to force screen resolution", + action="store_true") + + parser.add_argument( + "--run_on_battery", + help="allow the script to run on battery", + action="store_true", + ) + parser.add_argument( + "--ptt", help="enable Push-To-Talk function", action="store_true" + ) + parser.add_argument("--profile", help="enable profiling", action="store_true") + parser.add_argument( + "--record-movie", help="record a movie of each task", action="store_true" + ) + return parser.parse_args() diff --git a/src/shared/ptt.py b/src/shared/ptt.py new file mode 100644 index 00000000..8551dbf2 --- /dev/null +++ b/src/shared/ptt.py @@ -0,0 +1,30 @@ +# Push-To-Talk functions +import pulsectl + +MIC_SOURCE_NAME = "alsa_input.pci-0000_00_1f.3.analog-stereo" +DSP_SINK_NAME = "dsp" +DSP_SINK_NAME = ( + "alsa_output.usb-Lenovo_ThinkPad_USB-C_Dock_Audio_000000000000-00.analog-stereo" +) + + +class PushToTalk: + def __init__(self): + self._pa_client = pulsectl.Pulse("ptt_loopback") + self._source_idx = self._pa_client.get_source_by_name(MIC_SOURCE_NAME) + self._sink_idx = self._pa_client.get_sink_by_name(DSP_SINK_NAME) + + def _init_loopback(self): + self._loopback_mod_idx = self._pa_client.module_load( + "module-loopback", + "latency_msec=5 source=%d sink=%d" % (self._source_idx, self._sink_idx), + ) + + def _destroy_loopback(self): + self._pa_client.unload_module(self._loopback_mod_idx) + + def mute(self): + self._pa_client.source_mute(self._source_idx, 1) + + def unmute(self): + self._pa_client.source_mute(self._source_idx, 0) diff --git a/src/shared/screen.py b/src/shared/screen.py new file mode 100644 index 00000000..4a2c8f53 --- /dev/null +++ b/src/shared/screen.py @@ -0,0 +1,27 @@ +from . import config +from subprocess import Popen + + +def init_exp_screen(): + xrandr = Popen( + [ + "xrandr", + "--output", + config.EXP_SCREEN_XRANDR_NAME, + "--mode", + "%dx%d" % config.EXP_WINDOW["size"], + "--rate", + str(config.FRAME_RATE), + ] + ) + + +def reset_exp_screen(): + xrandr = Popen( + [ + "xrandr", + "--output", + config.EXP_SCREEN_XRANDR_NAME, + "--preferred", + ] + ) diff --git a/src/shared/utils.py b/src/shared/utils.py new file mode 100644 index 00000000..170b486a --- /dev/null +++ b/src/shared/utils.py @@ -0,0 +1,53 @@ +import psutil +import time +from psychopy import core +import os, glob + +def check_power_plugged(): + battery = psutil.sensors_battery() + if battery: + return battery.power_plugged + else: + return True + +def wait_until(clock, deadline, hogCPUperiod=0.1, keyboard_accuracy=.0005): + sleep_until = deadline - hogCPUperiod + poll_windows() + current_time = clock.getTime() + while current_time < deadline: + if current_time < sleep_until: + time.sleep(keyboard_accuracy) + poll_windows() + current_time = clock.getTime() + +def poll_windows(): + for winWeakRef in core.openWindows: + win = winWeakRef() + if (win.winType == "pyglet" and + hasattr(win.winHandle, "dispatch_events")): + win.winHandle.dispatch_events() # pump events + +def wait_until_yield(clock, deadline, hogCPUperiod=0.1, keyboard_accuracy=.0005): + sleep_until = deadline - hogCPUperiod + poll_windows() + current_time = clock.getTime() + while current_time < deadline: + if current_time < sleep_until: + time.sleep(keyboard_accuracy) + yield False + + poll_windows() + current_time = clock.getTime() + +def get_subject_soundcheck_video(subject): + setup_video_path = glob.glob( + os.path.join("data", "videos", "subject_setup_videos", "sub-%s_*" % subject) + ) + if not len(setup_video_path): + return os.path.join( + "data", + "videos", + "subject_setup_videos", + "sub-default_setup_video.mp4", + ) + return setup_video_path[0] diff --git a/src/shared/zmq_tools.py b/src/shared/zmq_tools.py index a8799271..ce6ee18b 100644 --- a/src/shared/zmq_tools.py +++ b/src/shared/zmq_tools.py @@ -1,7 +1,7 @@ """ (*)~--------------------------------------------------------------------------- Pupil - eye tracking platform -Copyright (C) 2012-2018 Pupil Labs +Copyright (C) 2012-2020 Pupil Labs Distributed under the terms of the GNU Lesser General Public License (LGPL v3.0). @@ -25,6 +25,9 @@ # import ujson as serializer # uncomment for json serialization assert zmq.__version__ > "15.1" +assert ( + serializer.version[0] == 1 +), "msgpack out of date, please upgrade to version (1, 0, 0)" class ZMQ_handler(logging.Handler): @@ -42,6 +45,8 @@ def emit(self, record): try: self.socket.send(record_dict) except TypeError: + # stringify message in case it is not a string yet + record_dict["msg"] = str(record_dict["msg"]) # stringify `exc_info` since it includes unserializable objects if record_dict["exc_info"]: # do not convert if it is None record_dict["exc_info"] = str(record_dict["exc_info"]) @@ -105,23 +110,20 @@ def recv(self): Any addional message frames will be added as a list in the payload dict with key: '__raw_data__' . """ - try: - topic = self.recv_topic() - remaining_frames = self.recv_remaining_frames() - payload = self.deserialize_payload(*remaining_frames) - return topic, payload - except zmq.ZMQError: - return None + topic = self.recv_topic() + remaining_frames = self.recv_remaining_frames() + payload = self.deserialize_payload(*remaining_frames) + return topic, payload def recv_topic(self): - return self.socket.recv_string(zmq.NOBLOCK) + return self.socket.recv_string() def recv_remaining_frames(self): while self.socket.get(zmq.RCVMORE): yield self.socket.recv() def deserialize_payload(self, payload_serialized, *extra_frames): - payload = serializer.loads(payload_serialized, encoding="utf-8") + payload = serializer.loads(payload_serialized) if extra_frames: payload["__raw_data__"] = extra_frames return payload @@ -137,13 +139,16 @@ class Msg_Streamer(ZMQ_Socket): Not threadsave. Make a new one for each thread """ - def __init__(self, ctx, url): + def __init__(self, ctx, url, hwm=None): self.socket = zmq.Socket(ctx, zmq.PUB) + if hwm is not None: + self.socket.set_hwm(hwm) + self.socket.connect(url) def send(self, payload, deprecated=()): """Send a message with topic, payload -` + Topic is a unicode string. It will be sent as utf-8 encoded byte array. Payload is a python dict. It will be sent as a msgpack serialized dict. @@ -153,7 +158,7 @@ def send(self, payload, deprecated=()): the contents of the iterable in '__raw_data__' require exposing the pyhton memoryview interface. """ - assert deprecated is (), "Depracted use of send()" + assert deprecated == (), "Depracted use of send()" assert "topic" in payload, "`topic` field required in {}".format(payload) if "__raw_data__" not in payload: diff --git a/src/tasks/images.py b/src/tasks/images.py index 0f830186..c4ea5046 100644 --- a/src/tasks/images.py +++ b/src/tasks/images.py @@ -4,63 +4,74 @@ from ..shared import config -STIMULI_DURATION=3 -BASELINE_BEGIN=5 -BASELINE_END=5 -ISI=4 +STIMULI_DURATION = 3 +BASELINE_BEGIN = 5 +BASELINE_END = 5 +ISI = 4 + class Images(Task): DEFAULT_INSTRUCTION = """Please keep your eyes open an focused on the screen all the time. You will see pictures of scenes and objects.""" - def __init__(self, images_list, images_path, *args,**kwargs): + def __init__(self, images_list, images_path, *args, **kwargs): super().__init__(**kwargs) - #TODO: image lists as params, subjects .... + # TODO: image lists as params, subjects .... self.image_names = data.importConditions(images_list) - if os.path.exists(images_path) and os.path.exists(os.path.join(images_path, self.images_names[0]['images_path'])): + if os.path.exists(images_path) and os.path.exists( + os.path.join(images_path, self.images_names[0]["images_path"]) + ): self.images_path = images_path else: - raise ValueError('Cannot find the listed images in %s '%images_path) + raise ValueError("Cannot find the listed images in %s " % images_path) - def instructions(self, exp_win, ctl_win): + def _instructions(self, exp_win, ctl_win): screen_text = visual.TextStim( - exp_win, text=self.instruction, - alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) + exp_win, + text=self.instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): screen_text.draw(exp_win) if ctl_win: screen_text.draw(ctl_win) - yield() + yield () def _run(self, exp_win, ctl_win): - self.trials = data.TrialHandler(self.image_names, 1, method='sequential') - img = visual.ImageStim(exp_win,size=(1,1),units='height') - exp_win.logOnFlip(level=logging.EXP,msg='image: task starting at %f'%time.time()) + self.trials = data.TrialHandler(self.image_names, 1, method="sequential") + img = visual.ImageStim(exp_win, size=(1, 1), units="height") + exp_win.logOnFlip( + level=logging.EXP, msg="image: task starting at %f" % time.time() + ) for frameN in range(config.FRAME_RATE * BASELINE_BEGIN): - yield() + yield () for trial in self.trials: - image_path = os.path.join(self.images_path, trial['image_path']) + image_path = os.path.join(self.images_path, trial["image_path"]) img.image = image_path - exp_win.logOnFlip(level=logging.EXP,msg='image: display %s'%image_path) - trial['onset'] = self.task_timer.getTime() + exp_win.logOnFlip(level=logging.EXP, msg="image: display %s" % image_path) + trial["onset"] = self.task_timer.getTime() for frameN in range(config.FRAME_RATE * STIMULI_DURATION): img.draw(exp_win) if ctl_win: img.draw(ctl_win) - yield() - trial['offset'] = self.task_timer.getTime() - trial['duration'] = trial['offset']-trial['onset'] - exp_win.logOnFlip(level=logging.EXP,msg='image: rest') + yield () + trial["offset"] = self.task_timer.getTime() + trial["duration"] = trial["offset"] - trial["onset"] + exp_win.logOnFlip(level=logging.EXP, msg="image: rest") for frameN in range(config.FRAME_RATE * ISI): - yield() + yield () for frameN in range(config.FRAME_RATE * BASELINE_END): - yield() + yield () + + def _save(self): + self.trials.saveAsWideText(self._generate_unique_filename("events", "tsv")) + return False - def save(self): - self.trials.saveAsWideText(self._generate_tsv_filename()) class BOLD5000Images(Images): pass diff --git a/src/tasks/language.py b/src/tasks/language.py index 891b87e0..ffeb0bb1 100644 --- a/src/tasks/language.py +++ b/src/tasks/language.py @@ -1,15 +1,17 @@ import os, sys, time from psychopy import visual, core, data, logging, event +from pandas import read_csv from .task_base import Task -from ..shared import config +from ..shared import config, utils + +STIMULI_DURATION = 4 +BASELINE_BEGIN = 5 +BASELINE_END = 5 +TRIPLET_RIGHT_KEY = "l" +TRIPLET_LEFT_KEY = "d" +ISI = 2 -STIMULI_DURATION=4 -BASELINE_BEGIN=5 -BASELINE_END=5 -TRIPLET_RIGHT_KEY='l' -TRIPLET_LEFT_KEY='d' -ISI=2 class Triplet(Task): @@ -20,10 +22,12 @@ class Triplet(Task): You have to select the response (left or right) that is closest to the target.""" - INSTRUCTION_WAIT_KEY = DEFAULT_INSTRUCTION + "\nWhen you're ready press <%s>" % TRIPLET_LEFT_KEY + INSTRUCTION_WAIT_KEY = ( + DEFAULT_INSTRUCTION + "\nWhen you're ready press <%s>" % TRIPLET_LEFT_KEY + ) - def __init__(self, words_file,*args,**kwargs): - self.wait_key = kwargs.pop('wait_key', False) + def __init__(self, words_file, *args, **kwargs): + self.wait_key = kwargs.pop("wait_key", False) super().__init__(**kwargs) if self.wait_key: self.instruction = Triplet.INSTRUCTION_WAIT_KEY @@ -31,18 +35,23 @@ def __init__(self, words_file,*args,**kwargs): self.words_file = words_file self.words_list = data.importConditions(self.words_file) else: - raise ValueError('File %s does not exists'%words_file) + raise ValueError("File %s does not exists" % words_file) - def instructions(self, exp_win, ctl_win): + def _instructions(self, exp_win, ctl_win): screen_text = visual.TextStim( - exp_win, text=self.instruction, - alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) + exp_win, + text=self.instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) def _draw_instr(): screen_text.draw(exp_win) if ctl_win: screen_text.draw(ctl_win) + if self.wait_key: while True: if len(event.getKeys([TRIPLET_LEFT_KEY])): @@ -56,59 +65,159 @@ def _draw_instr(): def _run(self, exp_win, ctl_win): - self.trials = data.TrialHandler(self.words_list, 1, method='random') + self.trials = data.TrialHandler(self.words_list, 1, method="random") target_stim = visual.TextStim( - exp_win, text='', - pos = (0,.25), - alignHoriz="center", color = 'white') + exp_win, text="", pos=(0, 0.25), alignText="center", color="white" + ) r1_stim = visual.TextStim( - exp_win, text='', - pos = (-.5,-.25), - alignHoriz="center", color = 'white') + exp_win, text="", pos=(-0.5, -0.25), alignText="center", color="white" + ) r2_stim = visual.TextStim( - exp_win, text='', - pos = (.5,-.25), - alignHoriz="center", color = 'white') + exp_win, text="", pos=(0.5, -0.25), alignText="center", color="white" + ) - exp_win.logOnFlip(level=logging.EXP,msg='triplet: task starting at %f'%time.time()) + exp_win.logOnFlip( + level=logging.EXP, msg="triplet: task starting at %f" % time.time() + ) for frameN in range(config.FRAME_RATE * BASELINE_BEGIN): - yield() + yield () for trial_idx, trial in enumerate(self.trials): - target_stim.text = trial['target'] - r1_stim.text = trial['response1'] - r2_stim.text = trial['response2'] + target_stim.text = trial["target"] + r1_stim.text = trial["response1"] + r2_stim.text = trial["response2"] - exp_win.logOnFlip(level=logging.EXP,msg='triplet: %d'%trial_idx) + exp_win.logOnFlip(level=logging.EXP, msg="triplet: %d" % trial_idx) onset = self.task_timer.getTime() # flush keys pressed before - event.getKeys([TRIPLET_LEFT_KEY,TRIPLET_RIGHT_KEY]) + event.getKeys([TRIPLET_LEFT_KEY, TRIPLET_RIGHT_KEY]) for frameN in range(config.FRAME_RATE * STIMULI_DURATION): - triplet_answer_keys = event.getKeys([TRIPLET_LEFT_KEY,TRIPLET_RIGHT_KEY]) - if(len(triplet_answer_keys)): - self.trials.addData('answer', triplet_answer_keys[0]) + triplet_answer_keys = event.getKeys( + [TRIPLET_LEFT_KEY, TRIPLET_RIGHT_KEY] + ) + if len(triplet_answer_keys): + self.trials.addData("answer", triplet_answer_keys[0]) for frameNN in range(frameN, config.FRAME_RATE * STIMULI_DURATION): - yield() + yield () break for stim in [target_stim, r1_stim, r2_stim]: stim.draw(exp_win) if ctl_win: stim.draw(ctl_win) - yield() + yield () else: - self.trials.addData('answer', '') # no answer, too slow or asleep + self.trials.addData("answer", "") # no answer, too slow or asleep offset = self.task_timer.getTime() - self.trials.addData('onset', onset) - self.trials.addData('offset', offset) - self.trials.addData('duration', offset-onset) # RT or max stim duration - exp_win.logOnFlip(level=logging.EXP,msg='triplet: rest') + self.trials.addData("onset", onset) + self.trials.addData("offset", offset) + self.trials.addData("duration", offset - onset) # RT or max stim duration + exp_win.logOnFlip(level=logging.EXP, msg="triplet: rest") for frameN in range(config.FRAME_RATE * ISI): - yield() + yield () for frameN in range(config.FRAME_RATE * BASELINE_END): - yield() + yield () + + def _save(self): + self.trials.saveAsWideText(self._generate_unique_filename("events", "tsv")) + return False + + +class Reading(Task): + + DEFAULT_INSTRUCTION = """You will be presented a text to read word by word.""" + + def __init__(self, words_file, word_duration=0.5, cross_duration=20, + txt_color="black", txt_font="Palatino Linotype", txt_size=42, + bg_color=(.5, .5, .5), *args, **kwargs): + super().__init__(**kwargs) + if os.path.exists(words_file): + self.words_file = words_file + self.word_duration = word_duration + self.cross_duration = cross_duration + self.txt_color = txt_color + self.txt_font = txt_font + self.txt_size = txt_size + self.bg_color = bg_color + import pandas + self.words_list = pandas.read_csv(words_file, sep="\t") + self.duration = len(self.words_list) + else: + raise ValueError("File %s does not exists" % words_file) + + def _instructions(self, exp_win, ctl_win): + screen_text = visual.TextStim( + exp_win, + text=self.instruction, + alignText="center", + color="black", + wrapWidth=1.2, + ) + exp_win.setColor(self.bg_color, "rgb") + if ctl_win: + ctl_win.setColor(self.bg_color, "rgb") + + for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): + screen_text.draw(exp_win) + if ctl_win: + screen_text.draw(ctl_win) + yield () + + def _setup(self, exp_win): + + self.txt_stim = visual.TextStim( + exp_win, + text="+", + font=self.txt_font, + height=self.txt_size, + units='pixels', + alignText="center", + color=self.txt_color, + ) + self._progress_bar_refresh_rate = 1 # 1 flip / trial + + def _run(self, exp_win, ctl_win): - def save(self): - self.trials.saveAsWideText(self._generate_tsv_filename()) + # Display each word + for trial_n, trial in self.words_list.iterrows(): + self.txt_stim.text = trial["word"] + self.txt_stim._pygletTextObj.set_style('italic', trial["format"] == "italic") + self.txt_stim.draw(exp_win) + self.progress_bar.set_description(f"Trial {trial_n}:: {trial['word']} {trial['format']}") + utils.wait_until( + self.task_timer, + trial["onset"] - 1 / config.FRAME_RATE, + hogCPUperiod=0.2) + yield True # flip + self.words_list.at[trial_n, "onset_flip"] = ( + self._exp_win_last_flip_time - self._exp_win_first_flip_time + ) + if trial_n > 0: + self.words_list.at[trial_n - 1, "offset_flip"] = self.words_list.at[trial_n, "onset_flip"] + self.words_list.at[trial_n -1, "duration_flip"] = ( + self.words_list.at[trial_n - 1, "offset_flip"] + - self.words_list.at[trial_n - 1, "onset_flip"] + ) + # wait for last event duration + utils.wait_until( + self.task_timer, + trial["onset"]+trial["duration"] - 1 / config.FRAME_RATE + ) + yield + + def _stop(self, exp_win, ctl_win): + exp_win.setColor((0,0,0), "rgb") + for _ in range(2): + yield True + + def _save(self): + #self.words_list.saveAsWideText(self._generate_unique_filename("events", "tsv") + self.words_list.to_csv( + self._generate_unique_filename("events", "tsv"), + sep = '\t', + index = False, + + ) + return False diff --git a/src/tasks/memory.py b/src/tasks/memory.py index fc019aea..1eff9f42 100644 --- a/src/tasks/memory.py +++ b/src/tasks/memory.py @@ -4,62 +4,67 @@ from ..shared import config -STIMULI_DURATION=4 -BASELINE_BEGIN=5 -BASELINE_END=5 -ISI=1 -IMAGES_FOLDER = '/home/basile/data/projects/task_stimuli/BOLD5000_Stimuli/Scene_Stimuli/Presented_Stimuli/ImageNet' +STIMULI_DURATION = 4 +BASELINE_BEGIN = 5 +BASELINE_END = 5 +ISI = 1 +IMAGES_FOLDER = "/home/basile/data/projects/task_stimuli/BOLD5000_Stimuli/Scene_Stimuli/Presented_Stimuli/ImageNet" -STIMULI_SIZE = (400,400) +STIMULI_SIZE = (400, 400) + +quadrant_id_to_pos = [(-200, 100), (200, 100), (-200, -100), (200, -100)] -quadrant_id_to_pos = [ - (-200,100), - (200,100), - (-200,-100), - (200,-100) -] class ImagePosition(Task): DEFAULT_INSTRUCTION = """You will be presented a set of items in different quadrant of the screen. Try to remember the items and their location on the screen.""" - def __init__(self, items_list,*args,**kwargs): + def __init__(self, items_list, *args, **kwargs): super().__init__(**kwargs) - #TODO: image lists as params, subjects .... + # TODO: image lists as params, subjects .... self.item_list = data.importConditions(items_list) - def instructions(self, exp_win, ctl_win): + def _instructions(self, exp_win, ctl_win): screen_text = visual.TextStim( - exp_win, text=self.instruction, - alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) + exp_win, + text=self.instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): screen_text.draw(exp_win) if ctl_win: screen_text.draw(ctl_win) - yield() + yield () def _run(self, exp_win, ctl_win): - - trials = data.TrialHandler(self.item_list, 1, method='sequential') - img = visual.ImageStim(exp_win,size=STIMULI_SIZE, units='pixels') - exp_win.logOnFlip(level=logging.EXP,msg='memory: task starting at %f'%time.time()) + trials = data.TrialHandler(self.item_list, 1, method="sequential") + img = visual.ImageStim(exp_win, size=STIMULI_SIZE, units="pixels") + exp_win.logOnFlip( + level=logging.EXP, msg="memory: task starting at %f" % time.time() + ) for frameN in range(config.FRAME_RATE * BASELINE_BEGIN): - yield() + yield () for trial in trials: - image_path = trial['image_path'] + image_path = trial["image_path"] img.image = image_path - img.pos = quadrant_id_to_pos[trial['quadrant']] - exp_win.logOnFlip(level=logging.EXP,msg='memory: display %s in quadrant %d'%(image_path,trial['quadrant'])) + img.pos = quadrant_id_to_pos[trial["quadrant"]] + exp_win.logOnFlip( + level=logging.EXP, + msg="memory: display %s in quadrant %d" + % (image_path, trial["quadrant"]), + ) for frameN in range(config.FRAME_RATE * STIMULI_DURATION): img.draw(exp_win) if ctl_win: img.draw(ctl_win) - yield() - exp_win.logOnFlip(level=logging.EXP,msg='memory: rest') + yield () + exp_win.logOnFlip(level=logging.EXP, msg="memory: rest") for frameN in range(config.FRAME_RATE * ISI): - yield() + yield () for frameN in range(config.FRAME_RATE * BASELINE_END): - yield() + yield () diff --git a/src/tasks/retinotopy.py b/src/tasks/retinotopy.py new file mode 100644 index 00000000..df9ee973 --- /dev/null +++ b/src/tasks/retinotopy.py @@ -0,0 +1,270 @@ +import os, sys, time, random +from psychopy import visual, core, data, logging, event +from .task_base import Task +import numpy as np +from colorama import Fore +import pandas + +from ..shared import config, utils + + +def generate_wedge(): + pass + +class Retinotopy(Task): + + DEFAULT_INSTRUCTION = """You will see a dot in the center of the screen. + Fixate that dot, and press the button when the color changes. + Moving patterns will be shown in the meantime, but you need to pay attention to the dot!""" + + + DOT_COLORS = [(237, 96, 31), (66, 135, 245)] + DOT_MIN_DURATION = 3 + RESPONSE_KEY = 'a' + PROGRESS_BAR_FORMAT = "{l_bar}{bar}| {n:.02f}/{total:.02f} [{elapsed}<{remaining}, {rate_fmt}{postfix}]" + + def __init__(self, condition, ncycles=8, *args, **kwargs): + super().__init__(**kwargs) + if condition not in ['RETCCW', 'RETCW', 'RETWEDGES', 'RETRINGS', 'RETEXP', 'RETCON', 'RETBAR']: + raise ValueError("Condition {condition} does not exists") + self.condition = condition + self.ncycles = ncycles + + + + def _setup(self, exp_win): + self.fixation_dot = visual.Circle( + exp_win, + size=.15, + units='deg', + color=self.DOT_COLORS[0], + colorSpace='rgb255' + ) + + + grid = np.load("data/retinotopy/grid.npz")['grid'] + self.grid = visual.ImageStim( + exp_win, + image=np.ones((1,1,3)), + mask=grid/128.-1, + size=10, + units='deg' + ) + + self.img = visual.ImageStim( + exp_win, + size=10, + units='deg', + flipVert=True) + + self._images = np.load('data/retinotopy/images.npz')['images'].astype(np.float32)/255. + + if self.condition in ['RETCW', 'RETCCW', 'RETWEDGES']: + aperture_file = 'apertures_wedge_newtr.npz' + elif self.condition in ['RETEXP', 'RETCON', 'RETRINGS']: + aperture_file = '/apertures_ring.npz' + elif self.condition == 'RETBAR': + self.ncycles = 8 + aperture_file = 'apertures_bars.npz' + self._apertures = np.load(f"data/retinotopy/{aperture_file}")['apertures'].astype(np.float32)/128.-1 + + self.cycle_length = 21*config.TR # a bit less than 32s for TR=1.49 + self.initial_wait = 16 if self.condition == 'RETBAR' else 22 + self.middle_blank = 12 if self.condition in ['RETRINGS', 'RETWEDGES', 'RETBAR'] else 0 + self.duration = ( + 32 * self.ncycles * (1 + (self.condition in ['RETRINGS', 'RETWEDGES'])) + + self.initial_wait * 2 + + self.middle_blank) + + # draw random order with different successive stimuli + self._images_random = np.random.randint(0, 99, size=(8*32*15)) #max nframe in CW conditions + while any(np.ediff1d(self._images_random, to_begin=[-1])==0): + self._images_random[np.ediff1d(self._images_random, to_begin=[-1])==0] += 1 + self._images_random[self._images_random==100] = 0 + + self._progress_bar_refresh_rate = False + + self.events = pandas.DataFrame() + super()._setup(exp_win) + + def _instructions(self, exp_win, ctl_win): + screen_text = visual.TextStim( + exp_win, + text=self.instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) + + for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): + screen_text.draw(exp_win) + if ctl_win: + screen_text.draw(ctl_win) + yield frameN < 2 + yield True + + def _run(self, exp_win, ctl_win): + event.getKeys() # flush all keypresses + + color_state = 0 + dot_next_change = 0 + responded = False + dot_change_idx = 0 + rt_sum = 0 + n_keypresses = 0 + for do_yield in self._run_condition(exp_win, ctl_win): + #TODO: log responses + keypresses = event.getKeys(self.RESPONSE_KEY, timeStamped=self.task_timer) + for k in keypresses: + rt = k[1] - dot_last_change + rt_sum += rt + n_keypresses += 1 + mean_rt = rt_sum / n_keypresses + self._log_event({ + 'trial_type':'response', + 'trial_number': dot_change_idx-1, + 'response_time': rt + }) + if not responded: + self.progress_bar.set_description( + f"({dot_change_idx-1}/{n_keypresses}), mean RT: {mean_rt}" + ) + responded = True + + if self.task_timer.getTime() > dot_next_change: + responded = False + color_state = (color_state+1)%2 + self.fixation_dot.setColor( + self.DOT_COLORS[color_state], + colorSpace='rgb255') + dot_last_change = dot_next_change + dot_next_change += self.DOT_MIN_DURATION + random.random()*5 + exp_win.callOnFlip( + self._log_event, + {'trial_type':'dot_color', + 'trial_number': dot_change_idx, + 'color': color_state}, + clock='flip' + ) + dot_change_idx += 1 + do_yield = True + + if do_yield: + self.grid.draw(exp_win) + self.fixation_dot.draw(exp_win) + if ctl_win: + self.grid.draw(ctl_win) + self.fixation_dot.draw(ctl_win) + previous_flip_time = self._exp_win_last_flip_time + yield True + #print(self._exp_win_last_flip_time, previous_flip_time) + self.progress_bar.update(self._exp_win_last_flip_time - previous_flip_time) + + def _run_condition(self, exp_win, ctl_win): + + frame_duration = 1/15. + + self._cycle_start = None + + yield True + # wait until it's almost time to render first frame + yield from utils.wait_until_yield( + self.task_timer, + self.initial_wait - .2, + keyboard_accuracy=.001, + hogCPUperiod=2/config.FRAME_RATE) + + if 'BAR' in self.condition: + conds = np.asarray([0,1,0,1,2,3,2,3]) + for ci, start_idx in enumerate(conds*28*15): + order = 1-(ci%4>1)*2 + for fi, frame in enumerate(range(28*15)[::order]): + flip_time = (self.initial_wait + (ci>3) * self.middle_blank + + (ci*self.cycle_length*15+fi) * frame_duration + - 1/config.FRAME_RATE) + + exp_win.timeOnFlip(self, '_cycle_start') + + #flipVert = 1 - 2*(ci in [3,6,7]) + #flipHoriz = 1 - 2*(ci in [2]) + image_idx = self._images_random[ci*32*15+fi] + self.img.image = self._images[..., image_idx] + self.img.mask = self._apertures[..., start_idx+frame] + + yield from utils.wait_until_yield( + self.task_timer, + flip_time, + keyboard_accuracy=.001, + hogCPUperiod=2/config.FRAME_RATE) + + self.img.draw(exp_win) + if ctl_win: + self.img.draw(ctl_win) + + exp_win.callOnFlip( + self._log_event, + {'condition': self.condition, 'image_idx': image_idx, 'aperture': frame}, + clock='flip' + ) + yield True + self._events.append({ + 'condition': self.condition, + 'trial_type': 'cycle', + 'onset': self._cycle_start, + 'duration': self._exp_win_last_flip_time - self._cycle_start + }) + + yield True + else: + orders = [-1 if self.condition in ['RETCW', 'RETCON'] else 1] * self.ncycles + if self.condition in ['RETWEDGES', 'RETRINGS']: + orders = [1] * self.ncycles + [-1] * self.ncycles + middle_blank = 12 + for ci, order in enumerate(orders): + display_length = (self.cycle_length + if self.condition in ['RETCW', 'RETCCW', 'RETWEDGES'] + else 28) # shorten next loop, adds 4s blank + for fi, frame in enumerate(range(int(display_length*15))[::order]): # 32/28 sec at 15Hz + flip_time = (self.initial_wait + + (ci*self.cycle_length*15+fi) * frame_duration + + (ci>self.ncycles//2) * self.middle_blank - 1/config.FRAME_RATE) + image_idx = self._images_random[ci*32*15+fi] + self.img.image = self._images[..., image_idx] + self.img.mask = self._apertures[..., frame] + + exp_win.timeOnFlip(self, '_cycle_start') + + yield from utils.wait_until_yield( + self.task_timer, + flip_time, + keyboard_accuracy=.001, + hogCPUperiod=2/config.FRAME_RATE) + + self.img.draw(exp_win) + if ctl_win: + self.img.draw(ctl_win) + + exp_win.callOnFlip( + self._log_event, + {'condition': self.condition, 'image_idx': image_idx, 'aperture': frame}, + clock='flip' + ) + yield True + self._events.append({ + 'condition': self.condition, + 'trial_type': 'cycle', + 'onset': self._cycle_start, + 'duration': self._exp_win_last_flip_time - self._cycle_start + }) + if 'CW' not in self.condition: + yield True # blank + yield True + + yield from utils.wait_until_yield( + self.task_timer, + self.duration, + keyboard_accuracy=.001) + + def unload(self): + del self._apertures, self._images + del self.img, self._images_random, self.fixation_dot diff --git a/src/tasks/speech.py b/src/tasks/speech.py index e124ee15..4f74c56f 100644 --- a/src/tasks/speech.py +++ b/src/tasks/speech.py @@ -4,62 +4,68 @@ from ..shared import config -STIMULI_DURATION=4 -BASELINE_BEGIN=5 -BASELINE_END=5 -ISI=4 +STIMULI_DURATION = 4 +BASELINE_BEGIN = 5 +BASELINE_END = 5 +ISI = 4 + class Speech(Task): DEFAULT_INSTRUCTION = """You will be presented text that you need to read out loud right when you see it.""" - def __init__(self, words_file,*args,**kwargs): + def __init__(self, words_file, *args, **kwargs): super().__init__(**kwargs) if os.path.exists(words_file): self.words_file = words_file self.words_list = data.importConditions(self.words_file) else: - raise ValueError('File %s does not exists'%words_file) + raise ValueError("File %s does not exists" % words_file) - def instructions(self, exp_win, ctl_win): + def _instructions(self, exp_win, ctl_win): screen_text = visual.TextStim( - exp_win, text=self.instruction, - alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) + exp_win, + text=self.instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): screen_text.draw(exp_win) if ctl_win: screen_text.draw(ctl_win) - yield() + yield () def _run(self, exp_win, ctl_win): - self.trials = data.TrialHandler(self.words_list, 1, method='random') + self.trials = data.TrialHandler(self.words_list, 1, method="random") - text = visual.TextStim( - exp_win, text='', - alignHoriz="center", color = 'white') + text = visual.TextStim(exp_win, text="", alignText="center", color="white") - exp_win.logOnFlip(level=logging.EXP,msg='speech: task starting at %f'%time.time()) + exp_win.logOnFlip( + level=logging.EXP, msg="speech: task starting at %f" % time.time() + ) for frameN in range(config.FRAME_RATE * BASELINE_BEGIN): - yield() + yield () for trial in self.trials: - text.text = trial['text'] - exp_win.logOnFlip(level=logging.EXP,msg='speech: %s'%text.text) - trial['onset'] = self.task_timer.getTime() + text.text = trial["text"] + exp_win.logOnFlip(level=logging.EXP, msg="speech: %s" % text.text) + trial["onset"] = self.task_timer.getTime() for frameN in range(config.FRAME_RATE * STIMULI_DURATION): text.draw(exp_win) if ctl_win: text.draw(ctl_win) - yield() - trial['offset'] = self.task_timer.getTime() - trial['duration'] = trial['offset']-trial['onset'] - exp_win.logOnFlip(level=logging.EXP,msg='speech: rest') + yield () + trial["offset"] = self.task_timer.getTime() + trial["duration"] = trial["offset"] - trial["onset"] + exp_win.logOnFlip(level=logging.EXP, msg="speech: rest") for frameN in range(config.FRAME_RATE * ISI): - yield() + yield () for frameN in range(config.FRAME_RATE * BASELINE_END): - yield() + yield () - def save(self): - self.trials.saveAsWideText(self._generate_tsv_filename()) + def _save(self): + self.trials.saveAsWideText(self._generate_unique_filename("events", "tsv")) + return False diff --git a/src/tasks/task_base.py b/src/tasks/task_base.py index e62fb933..ba6c92be 100644 --- a/src/tasks/task_base.py +++ b/src/tasks/task_base.py @@ -1,12 +1,16 @@ import os import tqdm +import time +import pandas from psychopy import logging, visual, core, event from ..shared import fmri, meg, config + class Task(object): - DEFAULT_INSTRUCTION='' + DEFAULT_INSTRUCTION = "" + PROGRESS_BAR_FORMAT = '{l_bar}{bar}{r_bar}' def __init__(self, name, instruction=None): self.name = name @@ -17,111 +21,156 @@ def __init__(self, name, instruction=None): self.instruction = instruction # setup large files for accurate start with other recordings (scanner, biopac...) - def setup(self, exp_win, output_path, output_fname_base, use_fmri=False, use_eyetracking=False, use_meg=False): + def setup( + self, + exp_win, + output_path, + output_fname_base, + use_fmri=False, + use_eyetracking=False, + use_meg=False, + ): self.output_path = output_path self.output_fname_base = output_fname_base self.use_fmri = use_fmri self.use_meg = use_meg self.use_eyetracking = use_eyetracking + self._events = [] + + self._exp_win_first_flip_time = None + self._exp_win_last_flip_time = None + self._ctl_win_last_flip_time = None + self._setup(exp_win) + # initialize a progress bar if we know the duration of the task + self.progress_bar = ( + tqdm.tqdm(total=self.duration, + bar_format=self.PROGRESS_BAR_FORMAT, + ) if hasattr(self, "duration") else False + ) + if not hasattr(self, "_progress_bar_refresh_rate"): + self._progress_bar_refresh_rate = config.FRAME_RATE def _setup(self, exp_win): pass - def _generate_tsv_filename(self): - for fi in range(1000): - fname = os.path.join(self.output_path, '%s_%s_%03d.tsv'%(self.output_fname_base, self.name,fi)) - if not os.path.exists(fname): - break + def _generate_unique_filename(self, suffix, ext="tsv"): + fname = os.path.join( + self.output_path, f"{self.output_fname_base}_{self.name}_{suffix}.{ext}" + ) + fi = 1 + while os.path.exists(fname): + fname = os.path.join( + self.output_path, + f"{self.output_fname_base}_{self.name}_{suffix}-{fi:03d}.{ext}", + ) + fi += 1 return fname def unload(self): pass def __str__(self): - return '%s : %s'%(self.__class__, self.name) + return "%s : %s" % (self.__class__, self.name) - def run(self, exp_win, ctl_win): - print('Next task: %s'%str(self)) - # show instruction - if hasattr(self, 'instructions'): - for _ in self.instructions(exp_win, ctl_win): - yield True - - # wait for TTL - fmri.get_ttl() # flush any remaining TTL keys - if self.use_fmri: - ttl_index = 0 - logging.exp(msg="waiting for fMRI TTL") - while True: - if fmri.get_ttl(): - #TODO: log real timing of TTL? - logging.exp(msg="fMRI TTL %d"%ttl_index) - ttl_index += 1 - break - yield False # no need to draw - logging.info('GO') + def _flip_all_windows(self, exp_win, ctl_win=None, clearBuffer=True): + if not ctl_win is None: + ctl_win.timeOnFlip(self, '_ctl_win_last_flip_time') + ctl_win.flip(clearBuffer=clearBuffer) - # send start trigger/marker to MEG + Biopac (or anything else on parallel port) - if self.use_meg: - meg.send_signal(meg.MEG_settings['TASK_START_CODE']) - self.task_timer = core.Clock() + exp_win.flip(clearBuffer=clearBuffer) + # set callback for next flip, to be the first callback for other callbacks to use + exp_win.timeOnFlip(self, '_exp_win_last_flip_time') - # initialize a progress bar if we know the duration of the task - progress_bar = False - if hasattr(self, 'duration'): - progress_bar = tqdm.tqdm(total=self.duration) - frame_idx = 0 - - for _ in self._run(exp_win, ctl_win): - if self.use_fmri: - if fmri.get_ttl(): - logging.exp(msg="fMRI TTL %d"%ttl_index) - ttl_index += 1 - - # increment the progress bar every second - if progress_bar: - frame_idx += 1 - if not frame_idx%config.FRAME_RATE: - progress_bar.update(1) + def instructions(self, exp_win, ctl_win): + if hasattr(self, "_instructions"): + for clearBuffer in self._instructions(exp_win, ctl_win): + yield + self._flip_all_windows(exp_win, ctl_win, clearBuffer) + # last/only flip to clear screen + yield + self._flip_all_windows(exp_win, ctl_win, True) - yield True + def run(self, exp_win, ctl_win): + # needs to be the 1rst callbacks + exp_win.timeOnFlip(self, '_exp_win_first_flip_time') - # send stop trigger/marker to MEG + Biopac (or anything else on parallel port) - if self.use_meg: - meg.send_signal(meg.MEG_settings['TASK_STOP_CODE']) + self.task_timer = core.Clock() - if progress_bar: - progress_bar.clear() - progress_bar.close() + if self.progress_bar: + self.progress_bar.reset() + flip_idx = 0 - def stop(self): - pass + for clearBuffer in self._run(exp_win, ctl_win): + # yield first to allow external draw before flip + yield + self._flip_all_windows(exp_win, ctl_win, clearBuffer) + # increment the progress bar depending on task flip rate + if self.progress_bar: + if self._progress_bar_refresh_rate and flip_idx % self._progress_bar_refresh_rate == 0: + self.progress_bar.update(1) + flip_idx += 1 + + def stop(self, exp_win, ctl_win): + if hasattr(self, "_stop"): + for clearBuffer in self._stop(exp_win, ctl_win): + yield + self._flip_all_windows(exp_win, ctl_win, clearBuffer) + if self.progress_bar: + self.progress_bar.clear() + self.progress_bar.close() + # 2 flips to clear screen and backbuffer + for i in range(2): + self._flip_all_windows(exp_win, ctl_win, True) def restart(self): - if hasattr(self, '_restart'): + if hasattr(self, "_restart"): self._restart() - def save(self): + def _log_event(self, event, clock='task'): + if clock == 'task': + onset = self.task_timer.getTime() + elif clock == 'flip': + onset = self._exp_win_last_flip_time - self._exp_win_first_flip_time + event.update({"onset": onset, "sample": time.monotonic()}) + self._events.append(event) + + def _save(self): + # to be overriden + # return False if events need not be saved + # allow to override events saving if transformation are needed pass -class Pause(Task): + def save(self): + # call custom task _save() + save_events = self._save() + if save_events is None and len(self._events): + fname = self._generate_unique_filename("events", "tsv") + df = pandas.DataFrame(self._events) + df.to_csv(fname, sep="\t", index=False) + +class Pause(Task): def __init__(self, text="Taking a short break, relax...", **kwargs): - self.wait_key = kwargs.pop('wait_key', False) - if not 'name' in kwargs: - kwargs['name'] = 'Pause' + self.wait_key = kwargs.pop("wait_key", False) + if not "name" in kwargs: + kwargs["name"] = "Pause" super().__init__(**kwargs) self.text = text def _setup(self, exp_win): self.use_fmri = False self.use_eyetracking = False + super()._setup(exp_win) def _run(self, exp_win, ctl_win): screen_text = visual.TextStim( - exp_win, text=self.text, - alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) + exp_win, + text=self.text, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) while True: if not self.wait_key is False: @@ -130,7 +179,10 @@ def _run(self, exp_win, ctl_win): screen_text.draw(exp_win) if ctl_win: screen_text.draw(ctl_win) - yield + yield True + + def _stop(self, exp_win, ctl_win): + yield True class Fixation(Task): @@ -139,32 +191,36 @@ class Fixation(Task): Please keep your eyes open and fixate the cross. Do not think about something in particular, let your mind wander...""" - def __init__(self, duration=7*60, symbol="+", **kwargs): - if not 'name' in kwargs: - kwargs['name'] = 'Pause' + def __init__(self, duration=7 * 60, symbol="+", **kwargs): + if not "name" in kwargs: + kwargs["name"] = "Pause" super().__init__(**kwargs) self.duration = duration self.symbol = symbol - def instructions(self, exp_win, ctl_win): + def _instructions(self, exp_win, ctl_win): screen_text = visual.TextStim( - exp_win, text=self.instruction, - alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) + exp_win, + text=self.instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): screen_text.draw(exp_win) if ctl_win: screen_text.draw(ctl_win) - yield + yield True def _run(self, exp_win, ctl_win): screen_text = visual.TextStim( - exp_win, text=self.symbol, - alignHoriz="center", color = 'white') - screen_text.height = .2 + exp_win, text=self.symbol, alignText="center", color="white" + ) + screen_text.height = 0.2 for frameN in range(config.FRAME_RATE * self.duration): screen_text.draw(exp_win) if ctl_win: screen_text.draw(ctl_win) - yield + yield True diff --git a/src/tasks/things.py b/src/tasks/things.py new file mode 100644 index 00000000..7eabe718 --- /dev/null +++ b/src/tasks/things.py @@ -0,0 +1,275 @@ +import os, sys, time +from psychopy import visual, core, data, logging, event +from .task_base import Task +import numpy as np +from colorama import Fore + +from ..shared import config, utils + +RESPONSE_KEY = "d" +RESPONSE_TIME = 4 +FINAL_WAIT = 9 + +class Things(Task): + + DEFAULT_INSTRUCTION = """You will see images on the screen. + +Press the button when you see an unrecognizable object that was generated.""" + + def __init__(self, design, images_path, run, *args, **kwargs): + super().__init__(**kwargs) + # TODO: image lists as params, subjects .... + design = data.importConditions(design) + self.run_id = run + self.design = [trial for trial in design if trial["run"] == run] + if os.path.exists(images_path) and os.path.exists( + os.path.join(images_path, self.design[0]["image_path"]) + ): + self.images_path = images_path + else: + raise ValueError("Cannot find the listed images in %s " % images_path) + + def _setup(self, exp_win): + self.fixation_cross = visual.ImageStim( + exp_win, + os.path.join("data", "things", "pngs", "fixation_cross.png"), + size=2, + units='deg', + ) + + # preload all images + self._stimuli = [] + for trial in self.design: + self._stimuli.append(visual.ImageStim( + exp_win, os.path.join(self.images_path, trial["image_path"]), + size=10, + units='deg', + )) + self.trials = data.TrialHandler(self.design, 1, method="sequential") + self.duration = len(self.design) + self._progress_bar_refresh_rate = 2 # 2 flips per trial + super()._setup(exp_win) + + def _instructions(self, exp_win, ctl_win): + screen_text = visual.TextStim( + exp_win, + text=self.instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) + + for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): + screen_text.draw(exp_win) + if ctl_win: + screen_text.draw(ctl_win) + yield () + + def _run(self, exp_win, ctl_win): + + exp_win.logOnFlip( + level=logging.EXP, msg="Things: task starting at %f" % time.time() + ) + self.fixation_cross.draw(exp_win) + if ctl_win: + self.fixation_cross.draw(ctl_win) + yield True + + for trial_n, (trial, stimuli) in enumerate(zip(self.trials, self._stimuli)): + exp_win.logOnFlip( + level=logging.EXP, + msg=f"image: {trial['condition']}:{trial['image_path']}", + ) + self.progress_bar.set_description( + f"Trial {trial_n}:: {trial['condition']}:{trial['image_path']}" + ) + + # draw to backbuffer + stimuli.draw(exp_win) + self.fixation_cross.draw(exp_win) + if ctl_win: + stimuli.draw(ctl_win) + self.fixation_cross.draw(ctl_win) + # wait onset + utils.wait_until(self.task_timer, trial["onset"] - 1 / config.FRAME_RATE) + yield True # flip + trial["onset_flip"] = ( + self._exp_win_last_flip_time - self._exp_win_first_flip_time + ) + + # draw to backbuffer + exp_win.logOnFlip(level=logging.EXP, msg="fixation") + self.fixation_cross.draw(exp_win) + if ctl_win: + self.fixation_cross.draw(ctl_win) + utils.wait_until(self.task_timer, trial["onset"] + trial["duration"] - 1 / config.FRAME_RATE) + yield True # flip + trial["offset_flip"] = ( + self._exp_win_last_flip_time - self._exp_win_first_flip_time + ) + + utils.wait_until(self.task_timer, trial["onset"] + RESPONSE_TIME - 1 / config.FRAME_RATE) + + keypress = event.getKeys([RESPONSE_KEY], timeStamped=self.task_timer) + trial["response"] = len(keypress) > 0 + trial["response_time"] = ( + (keypress[0][1] - trial["onset"]) if len(keypress) else None + ) + trial["duration_flip"] = trial["offset_flip"] - trial["onset_flip"] + + utils.wait_until(self.task_timer, trial["onset"] + RESPONSE_TIME + FINAL_WAIT) + + def _restart(self): + self.trials = data.TrialHandler(self.design, 1, method="sequential") + + def _save(self): + self.trials.saveAsWideText(self._generate_unique_filename("events", "tsv")) + return False + + def unload(self): + del self._stimuli + + +class ThingsMemory(Things): + + DEFAULT_INSTRUCTION = """You will see images on the screen. +Try to fixate the central marker at all time. +Press the buttons for each image to indicate your confidence in having seen or not that image previously. +""" + + EXTRA_INSTRUCTION = """ The response are: +-- surely not seen , +- not sure not seen, ++ not sure seen, +++ and surely seen. + + + + +The button mapping will change from trial to trial as indicated at the center of the screen with that image. + + + """ + RESPONSE_KEYS = ['up','right','left','down'] + RESPONSE_MAPPING = np.asarray(RESPONSE_KEYS).reshape(2,2) + RESPONSE_VALUES = np.asarray([[2,1],[-2,-1]]) + + def _instructions(self, exp_win, ctl_win): + screen_text = visual.TextStim( + exp_win, + text=self.instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) + + for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): + screen_text.draw(exp_win) + if ctl_win: + screen_text.draw(ctl_win) + yield frameN < 3 + yield True + screen_text.text = self.EXTRA_INSTRUCTION + if self.run_id == 1: + for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION * 2): + screen_text.draw(exp_win) + self._response_mapping.draw(exp_win) + if ctl_win: + screen_text.draw(ctl_win) + self._response_mapping.draw(ctl_win) + yield frameN + yield True + + def _setup(self, exp_win): + super()._setup(exp_win) + + self._response_mapping = visual.ImageStim( + exp_win, + os.path.join("data", "things", "pngs", "response_mapping3.png"), + size=2, + units='deg', + ) + + def _run(self, exp_win, ctl_win): + exp_win.logOnFlip( + level=logging.EXP, msg="ThingsMemory: task starting at %f" % time.time() + ) + self.fixation_cross.draw(exp_win) + if ctl_win: + self.fixation_cross.draw(ctl_win) + yield True + + for trial_n, (trial, stimuli) in enumerate(zip(self.trials, self._stimuli)): + exp_win.logOnFlip( + level=logging.EXP, + msg=f"image: {trial['condition']}:{trial['image_path']}", + ) + + # draw to backbuffer + stimuli.draw(exp_win) + self._response_mapping.flipHoriz = trial["response_mapping_flip_h"] + self._response_mapping.flipVert = trial["response_mapping_flip_v"] + self._response_mapping.pos = (0,0) #force update to flip + self._response_mapping.draw(exp_win) + if ctl_win: + stimuli.draw(ctl_win) + self._response_mapping.draw(ctl_win) + # wait onset + utils.wait_until(self.task_timer, trial["onset"] - 1 / config.FRAME_RATE) + keypresses = event.getKeys(self.RESPONSE_KEYS) # flush response keys + yield True # flip + trial["onset_flip"] = ( + self._exp_win_last_flip_time - self._exp_win_first_flip_time + ) + self.progress_bar.set_description( + f"Trial {trial_n}:: {trial['condition']}:{trial['image_path']}" + ) + + # draw to backbuffer + exp_win.logOnFlip(level=logging.EXP, msg="fixation") + self.fixation_cross.draw(exp_win) + if ctl_win: + self.fixation_cross.draw(ctl_win) + utils.wait_until(self.task_timer, trial["onset"] + trial["duration"] - 1 / config.FRAME_RATE) + yield True # flip + trial["offset_flip"] = ( + self._exp_win_last_flip_time - self._exp_win_first_flip_time + ) + + utils.wait_until(self.task_timer, trial["onset"] + RESPONSE_TIME - 1 / config.FRAME_RATE) + + keypresses = event.getKeys(self.RESPONSE_KEYS, timeStamped=self.task_timer) + if len(keypresses): + trial['keypresses'] = keypresses # log all keypresses with timing + idxs = [np.where(self.RESPONSE_MAPPING == k[0]) for k in keypresses] + response_mapping_flipped = self.RESPONSE_VALUES.copy() + if trial["response_mapping_flip_h"]: + response_mapping_flipped = np.rot90(np.fliplr(response_mapping_flipped)) + if trial["response_mapping_flip_v"]: + response_mapping_flipped = np.rot90(np.fliplr(response_mapping_flipped),3) + responses = [response_mapping_flipped[idx[0][0],idx[1][0]] for idx in idxs] + + main_key = keypresses[0] # take the first response as main one, to be decided + main_response = responses[0] + trial["response"] = main_response + trial["response_txt"] = "seen" if main_response > 0 else "unseen" + trial["error"] = trial["response_txt"] != trial["condition"] + trial["response_confidence"] = abs(main_response) > 1 + trial["response_time"] = (main_key[1] - trial["onset"]) + if trial['error']: + self.progress_bar.set_description( + f"Trial {trial_n}:: {trial['condition']}:{trial['image_path']} \u274c") + else: + self.progress_bar.set_description( + f"Trial {trial_n}:: {trial['condition']}:{trial['image_path']} \u2705") + else: + # we need to force empty values for the first trials + # otherwise following values are not recorded!? + for k in ['keypresses', 'response', 'response_txt', 'error', 'response_confidence', 'response_time']: + trial[k] = '' + self.progress_bar.set_description( + f"{Fore.RED}Trial {trial_n}:: {trial['condition']}:{trial['image_path']}: no response{Fore.RESET}") + + trial["duration_flip"] = trial["offset_flip"] - trial["onset_flip"] + + utils.wait_until(self.task_timer, trial["onset"] + RESPONSE_TIME + FINAL_WAIT) diff --git a/src/tasks/video.py b/src/tasks/video.py index 79ca6e7d..63da25fc 100644 --- a/src/tasks/video.py +++ b/src/tasks/video.py @@ -7,42 +7,54 @@ FADE_TO_GREY_DURATION = 2 + class SingleVideo(Task): DEFAULT_INSTRUCTION = """You are about to watch a video. Please keep your eyes open.""" - def __init__(self, filepath, *args,**kwargs): - self._aspect_ratio = kwargs.pop('aspect_ratio', None) - self._scaling = kwargs.pop('scaling', None) + def __init__(self, filepath, *args, **kwargs): + self._aspect_ratio = kwargs.pop("aspect_ratio", None) + self._scaling = kwargs.pop("scaling", None) super().__init__(**kwargs) self.filepath = filepath if not os.path.exists(self.filepath): - raise ValueError('File %s does not exists'%self.filepath) + raise ValueError("File %s does not exists" % self.filepath) - def instructions(self, exp_win, ctl_win): + def _instructions(self, exp_win, ctl_win): screen_text = visual.TextStim( - exp_win, text=self.instruction, - alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) + exp_win, + text=self.instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): - exp_win.setColor([-float(frameN)/config.FRAME_RATE/config.INSTRUCTION_DURATION]*3) + grey = [ + -float(frameN) / config.FRAME_RATE / config.INSTRUCTION_DURATION + ] * 3 + exp_win.setColor(grey, colorSpace='rgb') screen_text.draw(exp_win) if ctl_win: + ctl_win.setColor(grey) screen_text.draw(ctl_win) - yield + yield True def _setup(self, exp_win): - self.movie_stim = visual.MovieStim3(exp_win, self.filepath, units='pixels') - aspect_ratio = self._aspect_ratio or self.movie_stim.size[0]/self.movie_stim.size[1] - min_ratio = min( - exp_win.size[0]/ self.movie_stim.size[0], - exp_win.size[1]/ self.movie_stim.size[0]*aspect_ratio) - + self.movie_stim = visual.MovieStim2(exp_win, self.filepath, units="pixels") + # print(self.movie_stim._audioStream.__class__) + aspect_ratio = ( + self._aspect_ratio or self.movie_stim.size[0] / self.movie_stim.size[1] + ) + min_ratio = min( + exp_win.size[0] / self.movie_stim.size[0], + exp_win.size[1] / self.movie_stim.size[0] * aspect_ratio, + ) - width = min_ratio*self.movie_stim.size[0] - height = min_ratio*self.movie_stim.size[0]/aspect_ratio + width = min_ratio * self.movie_stim.size[0] + height = min_ratio * self.movie_stim.size[0] / aspect_ratio if self._scaling is not None: width *= self._scaling @@ -50,29 +62,31 @@ def _setup(self, exp_win): self.movie_stim.size = (width, height) self.duration = self.movie_stim.duration -# print(self.movie_stim.size) -# print(self.movie_stim.duration) + # print(self.movie_stim.size) + # print(self.movie_stim.duration) + super()._setup(exp_win) def _run(self, exp_win, ctl_win): # give the original size of the movie in pixels: - #print(self.movie_stim.format.width, self.movie_stim.format.height) + # print(self.movie_stim.format.width, self.movie_stim.format.height) exp_win.logOnFlip( - level=logging.EXP, - msg='video: task starting at %f'%time.time()) + level=logging.EXP, msg="video: task starting at %f" % time.time() + ) self.movie_stim.play() while self.movie_stim.status != visual.FINISHED: self.movie_stim.draw(exp_win) if ctl_win: self.movie_stim.draw(ctl_win) + yield False - yield - for frameN in range(config.FRAME_RATE * FADE_TO_GREY_DURATION): - exp_win.setColor([float(frameN)/config.FRAME_RATE/FADE_TO_GREY_DURATION-1]*3) - yield - - def stop(self): + def _stop(self, exp_win, ctl_win): self.movie_stim.stop() - self.movie_stim.win.setColor([0,0,0]) + for frameN in range(config.FRAME_RATE * FADE_TO_GREY_DURATION): + grey = [float(frameN) / config.FRAME_RATE / FADE_TO_GREY_DURATION - 1] * 3 + exp_win.setColor(grey, colorSpace='rgb') + if ctl_win: + ctl_win.setColor(grey) + yield True def _restart(self): self.movie_stim.setMovie(self.filepath) @@ -80,13 +94,13 @@ def _restart(self): def unload(self): del self.movie_stim + class VideoAudioCheckLoop(SingleVideo): DEFAULT_INSTRUCTION = """We are setting up for the MRI session. Make yourself comfortable. We will play your personalized video so that you can ensure you can see the full screen and that the image is sharp.""" - def _setup(self, exp_win): super()._setup(exp_win) # set infinite loop for setup, need to be skipped diff --git a/src/tasks/videogame.py b/src/tasks/videogame.py index f9673f38..77fa398e 100644 --- a/src/tasks/videogame.py +++ b/src/tasks/videogame.py @@ -9,36 +9,38 @@ import retro -DEFAULT_GAME_NAME = 'ShinobiIIIReturnOfTheNinjaMaster-Genesis' +DEFAULT_GAME_NAME = "ShinobiIIIReturnOfTheNinjaMaster-Genesis" -#KEY_SET = 'zx__abudlr_y' -#KEY_SET = 'zx__udlry___' -#KEY_SET = ['a','b','c','d','up','down','left','right','x','y','z','k'] -#KEY_SET = ['x','z','_','_','up','down','left','right','c','_','_','_'] -KEY_SET = ['y','a','_','_','u','d','l','r','b','_','_','_'] +# KEY_SET = 'zx__abudlr_y' +# KEY_SET = 'zx__udlry___' +# KEY_SET = ['a','b','c','d','up','down','left','right','x','y','z','k'] +# KEY_SET = ['x','z','_','_','up','down','left','right','c','_','_','_'] +KEY_SET = ["y", "a", "_", "_", "u", "d", "l", "r", "b", "_", "_", "_"] -#KEY_SET = '0123456789' +# KEY_SET = '0123456789' _keyPressBuffer = [] _keyReleaseBuffer = [] import pyglet + def _onPygletKeyPress(symbol, modifier): if modifier: event._onPygletKey(symbol, modifier) global _keyPressBuffer keyTime = core.getTime() - key = pyglet.window.key.symbol_string(symbol).lower().lstrip('_').lstrip('NUM_') + key = pyglet.window.key.symbol_string(symbol).lower().lstrip("_").lstrip("NUM_") _keyPressBuffer.append((key, keyTime)) + def _onPygletKeyRelease(symbol, modifier): global _keyReleaseBuffer keyTime = core.getTime() - key = pyglet.window.key.symbol_string(symbol).lower().lstrip('_').lstrip('NUM_') + key = pyglet.window.key.symbol_string(symbol).lower().lstrip("_").lstrip("NUM_") _keyReleaseBuffer.append((key, keyTime)) -class SoundDeviceBlockStream(sound.backend_sounddevice.SoundDeviceSound): +class SoundDeviceBlockStream(sound.backend_sounddevice.SoundDeviceSound): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.blocks = queue.Queue() @@ -48,155 +50,565 @@ def add_block(self, block): with self.lock: self.blocks.put(block) + def flush(self): + with self.lock: + self.blocks = queue.Queue() + def _nextBlock(self): if self.status == constants.STOPPED: return if self.blocks.empty(): - block = np.zeros((self.blockSize,2),dtype=np.float) + block = np.zeros((self.blockSize, 2), dtype=np.float) else: with self.lock: block = self.blocks.get() - self.t += self.blockSize/float(self.sampleRate) + self.t += self.blockSize / float(self.sampleRate) return block + class VideoGameBase(Task): + def __init__( + self, + game_name=DEFAULT_GAME_NAME, + state_name=None, + scenario=None, + repeat_scenario=True, + inttype=retro.data.Integrations.CUSTOM_ONLY, + *args, + **kwargs + ): + + super().__init__(**kwargs) + self.game_name = game_name + self.state_name = state_name + self.scenario = scenario + self.repeat_scenario = repeat_scenario + self.inttype = inttype + + def _setup(self, exp_win): + + super()._setup(exp_win) + + self._first_frame = self.emulator.reset() + first_sound_chunk = self.emulator.em.get_audio() + blockSize = first_sound_chunk.shape[0] + self.game_sound = SoundDeviceBlockStream( + sampleRate = self.emulator.em.get_audio_rate(), + stereo=(first_sound_chunk.ndim==2 & first_sound_chunk.shape[1]==2), + blockSize=blockSize) + + min_ratio = min( + exp_win.size[0] / self._first_frame.shape[1], + exp_win.size[1] / self._first_frame.shape[0], + ) + width = int(min_ratio * self._first_frame.shape[1]) + height = int(min_ratio * self._first_frame.shape[0]) + + self.game_vis_stim = visual.ImageStim( + exp_win, + size=(width, height), + units="pixels", + interpolate=False, + flipVert=True, + autoLog=False, + ) + + def _transform_soundblock(self, sound_block): + return sound_block[:self.game_sound.blockSize] / float(2 ** 15) + def _render_graphics_sound(self, obs, sound_block, exp_win, ctl_win): - self.game_vis_stim.image = np.flip(obs,0)/255. + self.game_vis_stim.image = obs / 255.0 self.game_vis_stim.draw(exp_win) if ctl_win: self.game_vis_stim.draw(ctl_win) - self.game_sound.add_block(sound_block[:735]/float(2**15)) + self.game_sound.add_block(self._transform_soundblock(sound_block)) if not self.game_sound.status == constants.PLAYING: - self.game_sound.play() + exp_win.callOnFlip(self.game_sound.play) # start sound only at flip - def stop(self): + def _stop(self, exp_win, ctl_win): self.game_sound.stop() + self.emulator.stop_record() # to be sure to save the bk2 + exp_win.setColor([0] * 3, colorSpace='rgb') + if ctl_win: + ctl_win.setColor([0] * 3, colorSpace='rgb') + yield True def unload(self): self.emulator.close() + class VideoGame(VideoGameBase): - DEFAULT_INSTRUCTION = "Let's play a video game.\n%s : %s\nHave fun!" + DEFAULT_INSTRUCTION = "Let's play {game_name}: {state_name}\nHave fun!" - def __init__(self, - game_name=DEFAULT_GAME_NAME, - state_name=None, - scenario=None, - repeat_scenario=True, + def __init__( + self, max_duration=0, - *args,**kwargs): + post_level_ratings=None, + *args, + **kwargs + ): super().__init__(**kwargs) - self.game_name = game_name - self.state_name = state_name - self.scenario = scenario - self.repeat_scenario = repeat_scenario self.max_duration = max_duration - self.instruction = self.instruction%(self.game_name, self.state_name) + self.duration = max_duration + self.post_level_ratings = post_level_ratings + self._completed = False - def instructions(self, exp_win, ctl_win): + def _instructions(self, exp_win, ctl_win): + + instruction = self.instruction.format( + **{'game_name':self.game_name, + 'state_name':self.state_name}) screen_text = visual.TextStim( - exp_win, text=self.instruction, - alignHoriz="center", color = 'white', wrapWidth=config.WRAP_WIDTH) + exp_win, + text=instruction, + alignText="center", + color="white", + wrapWidth=config.WRAP_WIDTH, + ) for frameN in range(config.FRAME_RATE * config.INSTRUCTION_DURATION): screen_text.draw(exp_win) if ctl_win: screen_text.draw(ctl_win) - yield + yield frameN < 2 + yield True def _setup(self, exp_win): + retraceRate = exp_win._monitorFrameRate + if retraceRate is None: + retraceRate = exp_win.getActualFrameRate() + if retraceRate is None: + logging.warning("FrameRate could not be supplied by psychopy; " + "defaulting to 60.0") + retraceRate = 60.0 + self._retraceInterval = 1.0/retraceRate + self.emulator = retro.make( self.game_name, state=self.state_name, scenario=self.scenario, - record=False) + record=False, + inttype=self.inttype + ) - self.game_vis_stim = visual.ImageStim(exp_win,size=exp_win.size,units='pixels',autoLog=False) - self.game_sound = SoundDeviceBlockStream(stereo=True, blockSize=735) + self.game_fps = self.emulator.em.get_screen_rate() + self._frameInterval = 1.0/self.game_fps - def _run(self, exp_win, ctl_win): - global _keyReleaseBuffer, _keyPressBuffer + super()._setup(exp_win) + self._set_recording_file() + self._set_key_handler(exp_win) + + def _set_recording_file(self): + nnn = 0 + while True: + self.movie_path = os.path.join( + self.output_path, + "%s_%s_%s_%03d.bk2" + % (self.output_fname_base, self.game_name, self.state_name, nnn), + ) + if not os.path.exists(self.movie_path): + break + nnn += 1 + logging.exp("VideoGame: recording movie in %s" % self.movie_path) + self.emulator.record_movie(self.movie_path) + + def _handle_controller_presses(self, exp_win): + exp_win.winHandle.dispatch_events() + global _keyPressBuffer, _keyReleaseBuffer + + for k in _keyReleaseBuffer: + self.pressed_keys.discard(k[0]) + logging.data(f"Keyrelease: {k[0]}") + _keyReleaseBuffer.clear() + for k in _keyPressBuffer: + self.pressed_keys.add(k[0]) + self._new_key_pressed = _keyPressBuffer[:] #copy + _keyPressBuffer.clear() + return self.pressed_keys + + def clear_key_buffers(self): + global _keyPressBuffer, _keyReleaseBuffer + self.pressed_keys.clear() + _keyReleaseBuffer.clear() + _keyPressBuffer.clear() + + def _run_emulator(self, exp_win, ctl_win): + + total_reward = 0 + _done = False + level_step = 0 + keys = [False] * 12 + + # flush all keys to avoid unwanted actions + self.clear_key_buffers() + + # render the initial frame and audio + self._render_graphics_sound( + self._first_frame, self.emulator.em.get_audio(), exp_win, ctl_win + ) + exp_win.logOnFlip(level=logging.EXP, msg="level step: %d" % level_step) + exp_win.callOnFlip( + self._log_event, + { + "trial_type": "gym-retro_game", + "game": self.game_name, + "level": self.state_name, + "stim_file": self.movie_path, + }, + ) + yield True + _nextFrameT = self._retraceInterval + while not _done: + level_step += 1 + while _nextFrameT > (self.task_timer.getTime() - + self._retraceInterval/2.0): + time.sleep(.0001) + self._handle_controller_presses(exp_win) + keys = [k in self.pressed_keys for k in KEY_SET] + _obs, _rew, _done, self._game_info = self.emulator.step(keys) + total_reward += _rew + if _rew > 0: + exp_win.logOnFlip(level=logging.EXP, msg="Reward %f" % (total_reward)) + self._render_graphics_sound( + _obs, self.emulator.em.get_audio(), exp_win, ctl_win + ) + if _done: + exp_win.logOnFlip( + level=logging.EXP, + msg="VideoGame %s: %s stopped at %f" + % (self.game_name, self.state_name, time.time()), + ) + if not level_step % config.FRAME_RATE: + exp_win.logOnFlip(level=logging.EXP, msg="level step: %d" % level_step) + yield True + + _nextFrameT += self._frameInterval + self._completed = self._completed or self._game_info['lives'] > -1 + self.game_sound.flush() + self.game_sound.stop() + self.emulator.stop_record() + + def _set_key_handler(self, exp_win): # activate repeat keys exp_win.winHandle.on_key_press = _onPygletKeyPress exp_win.winHandle.on_key_release = _onPygletKeyRelease - if ctl_win: - ctl_win.winHandle.on_key_press = _onPygletKeyPress - ctl_win.winHandle.on_key_release = _onPygletKeyRelease - keys = [False]*12 + self.pressed_keys = set() + def _unset_key_handler(self, exp_win): + # deactivate custom keys handling + exp_win.winHandle.on_key_press = event._onPygletKey + # del exp_win.winHandle.on_key_release + + def _run(self, exp_win, ctl_win): + + self._set_key_handler(exp_win) + self._nlevels = 0 + exp_win.setColor([-1.0] * 3, colorSpace='rgb') + if ctl_win: + ctl_win.setColor([-1.0] * 3, colorSpace='rgb') while True: + self._nlevels += 1 + exp_win.logOnFlip( + level=logging.EXP, + msg="VideoGame %s: %s starting at %f" + % (self.game_name, self.state_name, time.time()), + ) + yield from self._run_emulator(exp_win, ctl_win) + if self.post_level_ratings: + yield from self._run_ratings(exp_win, ctl_win) + if not self.repeat_scenario or ( + self.max_duration and self.task_timer.getTime() > self.max_duration + ): # stop if we are above the planned duration + break self.emulator.reset() - nnn = 0 - while True: - movie_path = os.path.join( - self.output_path, - "%s_%s_%s_%03d.bk2"%(self.output_fname_base,self.game_name,self.state_name, nnn)) - if not os.path.exists(movie_path): - break - nnn += 1 - logging.exp('VideoGame: recording movie in %s'%movie_path) - self.emulator.record_movie(movie_path) - total_reward = 0 + exp_win.setColor([0] * 3, colorSpace='rgb') + if ctl_win: + ctl_win.setColor([0] * 3, colorSpace='rgb') + + def _run_ratings(self, exp_win, ctl_win): + for question, n_pts in self.post_level_ratings: + yield from self._likert_scale_answer(exp_win, ctl_win, question, n_pts) + + text = visual.TextStim(exp_win, "Thanks for your answers", pos=(0, 0)) + for i in range(config.FRAME_RATE): + text.draw(exp_win) + yield i < 3 + # clear screen + for i in range(2): + yield True + + def _questionnaire(self, exp_win, ctl_win): + + exp_win.setColor([0] * 3, colorSpace='rgb') + if self.post_level_ratings is None: + return + lines = [] + bullets = [] + responses = [] + texts = [] + y_spacing = 80 + win_width = exp_win.size[0] + scales_block_x = win_width * 0.25 + scales_block_y = len(self.post_level_ratings) // 2 * y_spacing + extent = win_width * 0.2 + + + active_question = 0 + + # create all stimuli + #all_questions_text = "" + for q_n, (key, question, n_pts) in enumerate(self.post_level_ratings): + default_response = n_pts // 2 + responses.append(default_response) + x_spacing = extent * 2 / (n_pts - 1) + #all_questions_text += question + "\n\n" + + y_pos = scales_block_y - q_n * y_spacing + + lines.append( + visual.Line( + exp_win, + (scales_block_x - extent, y_pos), + (scales_block_x + extent, y_pos), + units="pixels", + lineWidth=6, + autoLog=False, + lineColor=((0, -1, -1) if q_n == 0 else (-1, -1, -1)), + ) + ) + bullets.append( + [ + visual.Circle( + exp_win, + units="pixels", + radius=10, + pos=( + scales_block_x - extent + i * x_spacing, + y_pos, + ), + fillColor=( + (1, 1, 1) if default_response == i else (-1, -1, -1) + ), + lineColor=(-1, -1, -1), + lineWidth=10, + autoLog=False, + ) + for i in range(n_pts) + ] + ) + texts.append(visual.TextStim( + exp_win, + text = question, + units="pixels", + bold = q_n == active_question, + pos=(0, y_pos), + wrapWidth= win_width * 0.5, + height= y_spacing / 3, + anchorHoriz="right", + alignText="right" + )) + responses[q_n] = default_response + + + + # questionnaire interaction loop + n_flips = 0 + while True: + self._handle_controller_presses(exp_win) + new_key_pressed = [k[0] for k in self._new_key_pressed] + if "u" in new_key_pressed and active_question > 0: + active_question -= 1 + elif "d" in new_key_pressed and active_question < len(self.post_level_ratings)-1: + active_question += 1 + elif "r" in new_key_pressed and responses[active_question] < n_pts - 1: + responses[active_question] += 1 + elif "l" in new_key_pressed and responses[active_question] > 0: + responses[active_question] -= 1 + elif "a" in new_key_pressed: + for (key, question, n_pts), value in zip(self.post_level_ratings, responses): + self._log_event({ + "trial_type": "questionnaire-answer", + "game": self.game_name, + "level": self.state_name, + "stim_file": self.movie_path, + "question": key, + "value": value + }) + break + elif n_flips > 1: + time.sleep(.01) + continue + + if n_flips > 0: #avoid double log when first loading questionnaire + self._log_event({ + "trial_type": "questionnaire-value-change", + "game": self.game_name, + "level": self.state_name, + "stim_file": self.movie_path, + "question": self.post_level_ratings[active_question][0], + "value": responses[active_question] + }) + exp_win.logOnFlip( level=logging.EXP, - msg='VideoGame %s: %s starting at %f'%(self.game_name, self.state_name, time.time())) - while True: - # TODO: get real action from controller - #gamectrl_keys = event.getKeys(list(KEY_SET)) - #keys = [k in gamectrl_keys for k in KEY_SET] - for k in _keyReleaseBuffer: - #print('release',k) - if k[0] in KEY_SET: - keys[KEY_SET.index(k[0])] = False - _keyReleaseBuffer.clear() - for k in _keyPressBuffer: - #print('press',k) - if k[0] in KEY_SET: - keys[KEY_SET.index(k[0])] = True - _keyPressBuffer.clear() - - _obs, _rew, _done, _info = self.emulator.step(keys) - total_reward += _rew - if _rew > 0 : - exp_win.logOnFlip(level=logging.EXP, msg='Reward %f'%(total_reward)) - self._render_graphics_sound(_obs,self.emulator.em.get_audio(),exp_win, ctl_win) - yield - if _done: - break + msg="level ratings %s" % responses) + for q_n, (txt, line, bullet_q) in enumerate(zip(texts, lines, bullets)): + #txt.bold = q_n == active_question + txt._pygletTextObj.set_style('bold', q_n == active_question) + line.lineColor = ((0, -1, -1) if q_n == active_question else (-1, -1, -1)), + for bullet_n, bullet in enumerate(bullet_q): + bullet.fillColor = (1, 1, 1) if responses[q_n] == bullet_n else (-1, -1, -1) + + for stim in lines + sum(bullets, []) + texts: + stim.draw(exp_win) + yield True + n_flips += 1 + + def _likert_scale_answer( + self, exp_win, ctl_win, question, n_pts=7, extent=0.6, autoLog=False + ): + extent *= config.EXP_WINDOW["size"][0] + value = n_pts // 2 + answered = False + text = visual.TextStim(exp_win, question, pos=(0, 0.5)) + line = visual.Line( + exp_win, + (-extent, 0), + (extent, 0), + units="pixels", + lineWidth=2, + autoLog=False, + ) + x_spacing = extent * 2 / (n_pts - 1) + circles = [ + visual.Circle( + exp_win, + units="pixels", + radius=40, + pos=(-extent + i * x_spacing, 0), + fillColor=(-1, -1, -1), + lineColor=(-1, -1, -1), + lineWidth=10, + autoLog=False, + ) + for i in range(n_pts) + ] + circles[value].fillColor = (1, 1, 1) + frame = 0 + while not answered: + frame += 1 + for stim in [text, line] + circles: + stim.draw(exp_win) + self._handle_controller_presses(exp_win) + if "a" in self.pressed_keys: + exp_win.logOnFlip( + level=logging.EXP, + msg="nlevel: %d, question: %s, answer: %d" + % (self._nlevels, question, value), + ) + for i in range(config.FRAME_RATE): + yield True + self.pressed_keys.clear() + break + if "r" in self.pressed_keys and value < n_pts - 1: + value += 1 + elif "l" in self.pressed_keys and value > 0: + value -= 1 + else: + yield frame < 4 + continue + self.pressed_keys.clear() + for c in circles: + c.fillColor = (-1, -1, -1) + circles[value].fillColor = (1, 1, 1) + + yield True + yield True + + def _stop(self, exp_win, ctl_win): + self._unset_key_handler(exp_win) + exp_win.waitBlanking = True + yield from super()._stop(exp_win, ctl_win) + + +class VideoGameMultiLevel(VideoGame): + def __init__(self, *args, **kwargs): + + self._state_names = kwargs.pop("state_names") + self._scenarii = kwargs.pop("scenarii") + self._repeat_scenario_multilevel = kwargs.get("repeat_scenario", False) - if not self.repeat_scenario or \ - (self.max_duration and - self.task_timer.getTime() > self.max_duration): # stop if we are above the planned duration + kwargs["repeat_scenario"] = False + super().__init__( + state_name=self._state_names[0], scenario=self._scenarii[0], **kwargs + ) + + def _run(self, exp_win, ctl_win): + + #exp_win.waitBlanking = False + + self._nlevels = 0 + while True: + for level, scenario in zip(self._state_names, self._scenarii): + self._nlevels += 1 + self.state_name = level + self.emulator.load_state(level, inttype=self.inttype) + self.emulator.data.load( + retro.data.get_file_path(self.game_name, "data.json", inttype=self.inttype), + retro.data.get_file_path(self.game_name, f"{scenario}.json", inttype=self.inttype) + ) + self._first_frame = self.emulator.reset() + if self._nlevels > 1: + self._set_recording_file() + yield from self._instructions(exp_win, ctl_win) + + exp_win.setColor([-1.0] * 3, colorSpace='rgb') + if ctl_win: + ctl_win.setColor([-1.0] * 3, colorSpace='rgb') + yield from super()._run_emulator(exp_win, ctl_win) + self.game_sound.stop() + + yield from self._questionnaire( + exp_win, ctl_win + ) + + time_exceeded = ( + self.max_duration and self.task_timer.getTime() > self.max_duration + ) + if time_exceeded: # stop if we are above the planned duration + break + if time_exceeded or not self._repeat_scenario_multilevel: break - # deactivate custom keys handling - exp_win.winHandle.on_key_press = event._onPygletKey - del exp_win.winHandle.on_key_release - if ctl_win: - ctl_win.winHandle.on_key_press = event._onPygletKey - del ctl_win.winHandle.on_key_release -class VideoGameReplay(VideoGameBase): + #exp_win.waitBlanking = True - def __init__(self, movie_filename, game_name=DEFAULT_GAME_NAME, scenario=None, *args, **kwargs): + +class VideoGameReplay(VideoGameBase): + def __init__( + self, + movie_filename, + *args, + **kwargs + ): super().__init__(**kwargs) self.game_name = game_name self.scenario = scenario self.movie_filename = movie_filename if not os.path.exists(self.movie_filename): - raise ValueError('file %s does not exists'%self.movie_filename) + raise ValueError("file %s does not exists" % self.movie_filename) def instructions(self, exp_win, ctl_win): - instruction_text = "You are going to watch someone play %s."%self.game_name + instruction_text = "You are going to watch someone play %s." % self.game_name screen_text = visual.TextStim( - exp_win, text=instruction_text, - alignHoriz="center", color = 'white') + exp_win, text=instruction_text, alignText="center", color="white" + ) for frameN in range(config.FRAME_RATE * INSTRUCTION_DURATION): screen_text.draw(exp_win) @@ -204,29 +616,28 @@ def instructions(self, exp_win, ctl_win): screen_text.draw(ctl_win) yield - def setup(self, exp_win, output_path, output_fname_base): - super().setup(exp_win, output_path, output_fname_base) + def _setup(self, exp_win): self.movie = retro.Movie(self.movie_filename) self.emulator = retro.make( self.game_name, record=False, state=retro.State.NONE, scenario=self.scenario, - #use_restricted_actions=retro.Actions.ALL, - players=self.movie.players) - self.emulator.initial_state = self.movie.get_state() - self.emulator.reset() + # use_restricted_actions=retro.Actions.ALL, + players=self.movie.players, + ) - self.game_vis_stim = visual.ImageStim(exp_win,size=exp_win.size,units='pixels',autoLog=False) - self.game_sound = SoundDeviceBlockStream(stereo=True, blockSize=735) + self.emulator.initial_state = self.movie.get_state() + super()._setup(exp_win) def _run(self, exp_win, ctl_win): # give the original size of the movie in pixels: - #print(self.movie_stim.format.width, self.movie_stim.format.height) + # print(self.movie_stim.format.width, self.movie_stim.format.height) total_reward = 0 exp_win.logOnFlip( level=logging.EXP, - msg='VideoGameReplay %s starting at %f'%(self.game_name, time.time())) + msg="VideoGameReplay %s starting at %f" % (self.game_name, time.time()), + ) while self.movie.step(): keys = [] for p in range(self.movie.players): @@ -236,8 +647,10 @@ def _run(self, exp_win, ctl_win): _obs, _rew, _done, _info = self.emulator.step(keys) total_reward += _rew - if _rew > 0 : - exp_win.logOnFlip(level=logging.EXP, msg='Reward %f'%(total_reward)) + if _rew > 0: + exp_win.logOnFlip(level=logging.EXP, msg="Reward %f" % (total_reward)) - self._render_graphics_sound(_obs,self.emulator.em.get_audio(), exp_win, ctl_win) + self._render_graphics_sound( + _obs, self.emulator.em.get_audio(), exp_win, ctl_win + ) yield