10 #define FLAG_SYNC CONTROL_FRAME_SYNC
12 //std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
16 void hash_string(uint8_t* res
, const std::string
& s
) throw(std::bad_alloc
)
20 std::copy(s
.begin(), s
.end(), t
.begin());
24 uint8_t* enlarge(std::vector
<uint8_t>& v
, size_t amount
) throw(std::bad_alloc
)
31 inline void write64(uint8_t* buffer
, uint64_t value
) throw()
33 buffer
[0] = value
>> 56;
34 buffer
[1] = value
>> 48;
35 buffer
[2] = value
>> 40;
36 buffer
[3] = value
>> 32;
37 buffer
[4] = value
>> 24;
38 buffer
[5] = value
>> 16;
39 buffer
[6] = value
>> 8;
43 inline void write32(uint8_t* buffer
, uint32_t value
) throw()
45 buffer
[0] = value
>> 24;
46 buffer
[1] = value
>> 16;
47 buffer
[2] = value
>> 8;
51 inline uint32_t read32(const uint8_t* buffer
) throw()
53 return (static_cast<uint32_t>(buffer
[0]) << 24) |
54 (static_cast<uint32_t>(buffer
[1]) << 16) |
55 (static_cast<uint32_t>(buffer
[2]) << 8) |
56 (static_cast<uint32_t>(buffer
[3]));
59 inline uint64_t read64(const uint8_t* buffer
) throw()
61 return (static_cast<uint64_t>(buffer
[0]) << 56) |
62 (static_cast<uint64_t>(buffer
[1]) << 48) |
63 (static_cast<uint64_t>(buffer
[2]) << 40) |
64 (static_cast<uint64_t>(buffer
[3]) << 32) |
65 (static_cast<uint64_t>(buffer
[4]) << 24) |
66 (static_cast<uint64_t>(buffer
[5]) << 16) |
67 (static_cast<uint64_t>(buffer
[6]) << 8) |
68 (static_cast<uint64_t>(buffer
[7]));
71 inline void write16s(uint8_t* buffer
, int16_t x
) throw()
73 uint16_t y
= static_cast<uint16_t>(x
);
78 void hash_subframe(sha256
& ctx
, const controls_t
& ctrl
) throw()
80 uint8_t buf
[2 * TOTAL_CONTROLS
];
81 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++)
82 write16s(buf
+ 2 * i
, ctrl(i
));
83 ctx
.write(buf
, 2 * TOTAL_CONTROLS
);
86 //Hashes frame and returns starting subframe of next frame.
87 uint64_t hash_frame(sha256
& ctx
, std::vector
<controls_t
>& input
, uint64_t first_subframe
,
88 uint32_t bound
) throw()
91 //Ignore this frame completely.
92 if(first_subframe
>= input
.size())
93 return first_subframe
;
95 while(first_subframe
< input
.size() && !input
[first_subframe
](CONTROL_FRAME_SYNC
))
97 return first_subframe
;
99 if(first_subframe
>= input
.size()) {
100 //Hash an empty frame.
101 hash_subframe(ctx
, controls_t(true));
102 return first_subframe
;
105 uint64_t subframes_to_hash
= 1;
106 uint64_t last_differing
= 1;
108 controls_t prev
= input
[first_subframe
];
109 prev(CONTROL_FRAME_SYNC
) = 0;
111 while(first_subframe
+ subframes_to_hash
< input
.size() && !input
[first_subframe
+ subframes_to_hash
]
112 (CONTROL_FRAME_SYNC
)) {
113 if(!(input
[first_subframe
+ subframes_to_hash
] == prev
))
114 last_differing
= subframes_to_hash
+ 1;
115 prev
= input
[first_subframe
+ subframes_to_hash
];
118 next
= first_subframe
+ subframes_to_hash
;
119 subframes_to_hash
= last_differing
;
120 for(uint64_t i
= 0; i
< subframes_to_hash
&& i
< bound
; i
++)
121 hash_subframe(ctx
, input
[first_subframe
+ i
]);
125 void hash_movie(uint8_t* res
, uint64_t current_frame
, uint32_t* pollcounters
,
126 std::vector
<controls_t
>& input
) throw(std::bad_alloc
)
129 //If current_frame == 0, hash is empty.
135 uint64_t current_subframe
= 0;
136 for(uint64_t i
= 1; i
< current_frame
; i
++)
137 current_subframe
= hash_frame(ctx
, input
, current_subframe
, 0x7FFFFFFF);
138 //Current frame is special.
139 for(size_t i
= 0; i
< TOTAL_CONTROLS
; i
++) {
140 uint32_t polls
= pollcounters
[i
] & 0x7FFFFFFF;
141 uint32_t last_seen
= 0;
142 for(size_t j
= 0; j
< polls
; j
++) {
143 if(current_subframe
+ j
< input
.size() && !input
[current_subframe
+ j
]
144 (CONTROL_FRAME_SYNC
))
145 last_seen
= input
[current_subframe
+ j
](i
);
147 write16s(buf
, last_seen
);
155 void movie::set_all_DRDY() throw()
157 for(size_t i
= 0; i
< TOTAL_CONTROLS
; i
++)
158 pollcounters
[i
] |= 0x80000000UL
;
161 std::string
movie::rerecord_count() throw(std::bad_alloc
)
166 void movie::rerecord_count(const std::string
& count
) throw(std::bad_alloc
)
171 std::string
movie::project_id() throw(std::bad_alloc
)
176 void movie::project_id(const std::string
& id
) throw(std::bad_alloc
)
181 bool movie::readonly_mode() throw()
186 short movie::next_input(unsigned port
, unsigned controller
, unsigned index
) throw(std::bad_alloc
, std::logic_error
)
188 return next_input(ccindex2(port
, controller
, index
));
191 void movie::set_controls(controls_t controls
) throw()
193 current_controls
= controls
;
196 uint32_t movie::count_changes(uint64_t first_subframe
) throw()
198 if(first_subframe
>= movie_data
.size())
201 while(first_subframe
+ ret
< movie_data
.size() && !movie_data
[first_subframe
+ ret
](CONTROL_FRAME_SYNC
))
206 controls_t
movie::get_controls() throw()
209 return current_controls
;
211 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
212 if(current_frame
== 0)
214 //Otherwise find the last valid frame of input.
215 uint32_t changes
= count_changes(current_frame_first_subframe
);
217 return c
; //End of movie.
218 for(size_t i
= 0; i
< TOTAL_CONTROLS
; i
++) {
219 uint32_t polls
= pollcounters
[i
] & 0x7FFFFFFF;
220 uint32_t index
= (changes
> polls
) ? polls
: changes
- 1;
221 c(i
) = movie_data
[current_frame_first_subframe
+ index
](i
);
226 uint64_t movie::get_current_frame() throw()
228 return current_frame
;
231 uint64_t movie::get_lag_frames() throw()
236 uint64_t movie::get_frame_count() throw()
238 return frames_in_movie
;
241 void movie::next_frame() throw(std::bad_alloc
)
243 //If all poll counters are zero for all real controls, this frame is lag.
244 bool this_frame_lag
= true;
245 for(size_t i
= MAX_SYSTEM_CONTROLS
; i
< TOTAL_CONTROLS
; i
++)
246 if(pollcounters
[i
] & 0x7FFFFFFF)
247 this_frame_lag
= false;
248 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
249 if(pollcounters
[CONTROL_SYSTEM_RESET
] & 0x7FFFFFFF)
250 this_frame_lag
= false;
251 //Oh, frame 0 must not be considered lag.
252 if(current_frame
&& this_frame_lag
) {
254 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
256 //If in read-write mode, write a dummy record for the frame. Force sync flag.
257 //As index should be movie_data.size(), it is correct afterwards.
258 controls_t c
= current_controls
;
259 c(CONTROL_FRAME_SYNC
) = 1;
260 movie_data
.push_back(c
);
265 //Reset the poll counters and DRDY flags.
266 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++)
269 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
270 //frame 0 and 0 for frame 1.
272 current_frame_first_subframe
= current_frame_first_subframe
+
273 count_changes(current_frame_first_subframe
);
275 current_frame_first_subframe
= 0;
279 bool movie::get_DRDY(unsigned controlindex
) throw(std::logic_error
)
281 if(controlindex
>= TOTAL_CONTROLS
)
282 throw std::logic_error("movie::get_DRDY: Bad index");
283 return ((pollcounters
[controlindex
] & 0x80000000UL
) != 0);
286 bool movie::get_DRDY(unsigned port
, unsigned controller
, unsigned index
) throw(std::logic_error
)
288 return get_DRDY(ccindex2(port
, controller
, index
));
291 short movie::next_input(unsigned controlindex
) throw(std::bad_alloc
, std::logic_error
)
293 //Check validity of index.
294 if(controlindex
== FLAG_SYNC
)
296 if(controlindex
>= TOTAL_CONTROLS
)
297 throw std::logic_error("movie::next_input: Invalid control index");
299 //Clear the DRDY flag.
300 pollcounters
[controlindex
] &= 0x7FFFFFFF;
303 //In readonly mode...
304 //If at the end of the movie, return released / neutral (but also record the poll)...
305 if(current_frame_first_subframe
>= movie_data
.size()) {
306 pollcounters
[controlindex
]++;
309 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
310 if(current_frame
== 0)
312 //Otherwise find the last valid frame of input.
313 uint32_t changes
= count_changes(current_frame_first_subframe
);
314 uint32_t polls
= (pollcounters
[controlindex
]++) & 0x7FFFFFFF;
315 uint32_t index
= (changes
> polls
) ? polls
: changes
- 1;
316 //debuglog << "Frame=" << current_frame << " Subframe=" << polls << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe + index](controlindex) << " fetchrow=" << current_frame_first_subframe + index << std::endl << std::flush;
317 return movie_data
[current_frame_first_subframe
+ index
](controlindex
);
320 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
321 //Also, frame 0 must not be added to movie file.
322 if(current_frame
== 0)
324 //If at movie end, insert complete input with frame sync set (this is the first subframe).
325 if(current_frame_first_subframe
>= movie_data
.size()) {
326 controls_t c
= current_controls
;
327 c(CONTROL_FRAME_SYNC
) = 1;
328 movie_data
.push_back(c
);
329 //current_frame_first_subframe should be movie_data.size(), so it is right.
330 pollcounters
[controlindex
]++;
332 assert(pollcounters
[controlindex
] == 1);
333 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe](controlindex) << " fetchrow=" << current_frame_first_subframe << std::endl << std::flush;
334 return movie_data
[current_frame_first_subframe
](controlindex
);
336 short new_value
= current_controls(controlindex
);
337 //Fortunately, we know this frame is the last one in movie_data.
338 uint32_t pollcounter
= pollcounters
[controlindex
] & 0x7FFFFFFF;
339 uint64_t fetchrow
= movie_data
.size() - 1;
340 if(current_frame_first_subframe
+ pollcounter
< movie_data
.size()) {
341 //The index is within existing size. Change the value and propagate to all subsequent
343 for(uint64_t i
= current_frame_first_subframe
+ pollcounter
; i
< movie_data
.size(); i
++)
344 movie_data
[i
](controlindex
) = new_value
;
345 fetchrow
= current_frame_first_subframe
+ pollcounter
;
346 } else if(new_value
!= movie_data
[movie_data
.size() - 1](controlindex
)) {
347 //The index is not within existing size and value does not match. We need to create a new
348 //subframes(s), copying the last subframe.
349 while(current_frame_first_subframe
+ pollcounter
>= movie_data
.size()) {
350 controls_t c
= movie_data
[movie_data
.size() - 1];
351 c(CONTROL_FRAME_SYNC
) = 0;
352 movie_data
.push_back(c
);
354 fetchrow
= current_frame_first_subframe
+ pollcounter
;
355 movie_data
[current_frame_first_subframe
+ pollcounter
](controlindex
) = new_value
;
357 pollcounters
[controlindex
]++;
358 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
363 movie::movie() throw(std::bad_alloc
)
370 current_frame_first_subframe
= 0;
371 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
373 current_controls(i
) = 0;
379 void movie::load(const std::string
& rerecs
, const std::string
& project_id
, const std::vector
<controls_t
>& input
)
380 throw(std::bad_alloc
, std::runtime_error
)
382 if(input
.size() > 0 && !input
[0](CONTROL_FRAME_SYNC
))
383 throw std::runtime_error("First subframe MUST have frame sync flag set");
387 if(i(CONTROL_FRAME_SYNC
))
391 _project_id
= project_id
;
393 current_frame_first_subframe
= 0;
394 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
401 std::vector
<controls_t
> movie::save() throw(std::bad_alloc
)
406 void movie::commit_reset(long delay
) throw(std::bad_alloc
)
408 if(readonly
|| delay
< 0)
410 //If this frame is lagged, we need to write entry for it.
411 bool this_frame_lag
= true;
412 for(size_t i
= MAX_SYSTEM_CONTROLS
; i
< TOTAL_CONTROLS
; i
++)
413 if(pollcounters
[i
] & 0x7FFFFFFF)
414 this_frame_lag
= false;
415 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
416 if(pollcounters
[CONTROL_SYSTEM_RESET
] & 0x7FFFFFFF)
417 this_frame_lag
= false;
419 controls_t c
= current_controls
;
420 c(CONTROL_FRAME_SYNC
) = 1;
421 movie_data
.push_back(c
);
423 //Current_frame_first_subframe is correct.
425 //Also set poll counters on reset cycles to avoid special cases elsewhere.
426 pollcounters
[CONTROL_SYSTEM_RESET
] = 1;
427 pollcounters
[CONTROL_SYSTEM_RESET_CYCLES_HI
] = 1;
428 pollcounters
[CONTROL_SYSTEM_RESET_CYCLES_LO
] = 1;
429 //Current frame is always last in rw mode.
430 movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET
) = 1;
431 movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET_CYCLES_HI
) = delay
/ 10000;
432 movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET_CYCLES_LO
) = delay
% 10000;
435 unsigned movie::next_poll_number()
438 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++)
439 if(max
< (pollcounters
[i
] & 0x7FFFFFFF))
440 max
= (pollcounters
[i
] & 0x7FFFFFFF);
444 void movie::readonly_mode(bool enable
) throw(std::bad_alloc
)
446 bool was_in_readonly
= readonly
;
448 if(was_in_readonly
&& !readonly
) {
450 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
451 if(current_frame
== 0) {
452 //WTF... At before first frame. Blank the entiere movie.
457 //Fun special case: Current frame is not in movie (current_frame_first_subframe >= movie_data.size()).
458 //In this case, we have to extend the movie data.
459 if(current_frame_first_subframe
>= movie_data
.size()) {
460 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
461 while(frames_in_movie
< current_frame
) {
463 movie_data
.push_back(c
);
466 current_frame_first_subframe
= movie_data
.size() - 1;
469 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
470 //forward values with smaller poll counters.
471 uint64_t next_frame_first_subframe
= current_frame_first_subframe
+
472 count_changes(current_frame_first_subframe
);
473 uint64_t max_readable_subframes
= current_frame_first_subframe
;
474 for(size_t i
= 0; i
< TOTAL_CONTROLS
; i
++) {
475 uint32_t polls
= pollcounters
[i
] & 0x7FFFFFFF;
476 if(current_frame_first_subframe
+ polls
>= next_frame_first_subframe
)
477 max_readable_subframes
= next_frame_first_subframe
;
478 else if(current_frame_first_subframe
+ polls
> max_readable_subframes
)
479 max_readable_subframes
= current_frame_first_subframe
+ polls
;
481 movie_data
.resize(max_readable_subframes
);
482 next_frame_first_subframe
= max_readable_subframes
;
483 for(size_t i
= 1; i
< TOTAL_CONTROLS
; i
++) {
484 uint32_t polls
= pollcounters
[i
] & 0x7FFFFFFF;
487 for(uint64_t j
= current_frame_first_subframe
+ polls
; j
< next_frame_first_subframe
; j
++)
488 movie_data
[j
](i
) = movie_data
[current_frame_first_subframe
+ polls
- 1](i
);
490 frames_in_movie
= current_frame
- ((current_frame_first_subframe
>= movie_data
.size()) ? 1 : 0);
494 //Save state of movie code.
495 std::vector
<uint8_t> movie::save_state() throw(std::bad_alloc
)
497 //debuglog << "--------------------------------------------" << std::endl;
498 //debuglog << "SAVING STATE:" << std::endl;
499 std::vector
<uint8_t> ret
;
500 hash_string(enlarge(ret
, 32), _project_id
);
501 write64(enlarge(ret
, 8), current_frame
);
502 //debuglog << "Current frame is " << current_frame << std::endl;
503 //debuglog << "Poll counters: ";
504 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
505 uint32_t v
= pollcounters
[i
];
507 if(v
& 0x80000000UL
) {
511 write32(enlarge(ret
, 4), v
);
513 //debuglog << std::endl;
515 uint64_t v
= lag_frames
;
516 //debuglog << "Lag frame count: " << lag_frames << std::endl;
517 write64(enlarge(ret
, 8), v
);
519 hash_movie(enlarge(ret
, 32), current_frame
, pollcounters
, movie_data
);
521 sha256::hash(hash
, ret
);
522 memcpy(enlarge(ret
, 32), hash
, 32);
523 //debuglog << "--------------------------------------------" << std::endl;
528 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
529 size_t movie::restore_state(const std::vector
<uint8_t>& state
, bool ro
) throw(std::bad_alloc
, std::runtime_error
)
531 //Check the whole-data checksum.
534 if(state
.size() != 112+4*TOTAL_CONTROLS
)
535 throw std::runtime_error("Movie save data corrupt: Wrong length");
536 sha256::hash(tmp
, &state
[0], state
.size() - 32);
537 if(memcmp(tmp
, &state
[state
.size() - 32], 32))
538 throw std::runtime_error("Movie save data corrupt: Checksum does not match");
539 //debuglog << "--------------------------------------------" << std::endl;
540 //debuglog << "RESTORING STATE:" << std::endl;
542 hash_string(tmp
, _project_id
);
543 if(memcmp(tmp
, &state
[ptr
], 32))
544 throw std::runtime_error("Save is not from this movie");
546 //Read current frame.
547 uint64_t tmp_curframe
= read64(&state
[ptr
]);
548 uint64_t tmp_firstsubframe
= 0;
549 for(uint64_t i
= 1; i
< tmp_curframe
; i
++)
550 tmp_firstsubframe
= tmp_firstsubframe
+ count_changes(tmp_firstsubframe
);
552 //Read poll counters and drdy flags.
553 uint32_t tmp_pollcount
[TOTAL_CONTROLS
];
554 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
555 uint32_t v
= read32(&state
[ptr
]);
557 tmp_pollcount
[i
] = v
;
559 uint64_t tmp_lagframes
= read64(&state
[ptr
]);
560 tmp_lagframes
&= 0x7FFFFFFFFFFFFFFFULL
;
562 hash_movie(tmp
, tmp_curframe
, tmp_pollcount
, movie_data
);
563 if(memcmp(tmp
, &state
[ptr
], 32))
564 throw std::runtime_error("Save is not from this movie");
566 //Ok, all checks pass. Copy the state. Do this in readonly mode so we can use normal routine to switch
569 current_frame
= tmp_curframe
;
570 current_frame_first_subframe
= tmp_firstsubframe
;
571 memcpy(pollcounters
, tmp_pollcount
, sizeof(tmp_pollcount
));
572 lag_frames
= tmp_lagframes
;
574 //debuglog << "Current frame is " << current_frame << std::endl;
575 //debuglog << "Poll counters: ";
576 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
577 uint32_t v
= pollcounters
[i
];
579 if(v
& 0x80000000UL
) {
584 //debuglog << std::endl;
586 //debuglog << "Lag frame count: " << lag_frames << std::endl;
589 //debuglog << "--------------------------------------------" << std::endl;
592 //Move to readwrite mode if needed.
597 long movie::get_reset_status() throw()
599 if(current_frame
== 0 || current_frame_first_subframe
>= movie_data
.size())
600 return -1; //No resets out of range.
601 if(!movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET
))
602 return -1; //Not a reset.
603 //Also set poll counters on reset cycles to avoid special cases elsewhere.
604 pollcounters
[CONTROL_SYSTEM_RESET
] = 1;
605 pollcounters
[CONTROL_SYSTEM_RESET_CYCLES_HI
] = 1;
606 pollcounters
[CONTROL_SYSTEM_RESET_CYCLES_LO
] = 1;
607 long hi
= movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET_CYCLES_HI
);
608 long lo
= movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET_CYCLES_LO
);
609 return hi
* 10000 + lo
;
612 uint64_t movie::frame_subframes(uint64_t frame
) throw()
614 if(frame
< cached_frame
)
616 uint64_t p
= cached_subframe
;
617 for(uint64_t i
= cached_frame
; i
< frame
; i
++)
618 p
= p
+ count_changes(p
);
619 cached_frame
= frame
;
621 return count_changes(p
);
624 void movie::clear_caches() throw()
630 controls_t
movie::read_subframe(uint64_t frame
, uint64_t subframe
) throw()
632 if(frame
< cached_frame
)
634 uint64_t p
= cached_subframe
;
635 for(uint64_t i
= cached_frame
; i
< frame
; i
++)
636 p
= p
+ count_changes(p
);
637 cached_frame
= frame
;
639 uint64_t max
= count_changes(p
);
641 return controls_t(true);
644 return movie_data
[p
+ subframe
];
648 movie_logic::movie_logic() throw()
652 movie
& movie_logic::get_movie() throw()
657 long movie_logic::new_frame_starting(bool dont_poll
) throw(std::bad_alloc
, std::runtime_error
)
660 controls_t c
= update_controls(false);
661 if(!mov
.readonly_mode()) {
665 if(c(CONTROL_SYSTEM_RESET
)) {
666 long hi
= c(CONTROL_SYSTEM_RESET_CYCLES_HI
);
667 long lo
= c(CONTROL_SYSTEM_RESET_CYCLES_LO
);
668 mov
.commit_reset(hi
* 10000 + lo
);
671 return mov
.get_reset_status();
674 short movie_logic::input_poll(bool port
, unsigned dev
, unsigned id
) throw(std::bad_alloc
, std::runtime_error
)
676 if(dev
>= MAX_CONTROLLERS_PER_PORT
|| id
>= CONTROLLER_CONTROLS
)
678 if(!mov
.get_DRDY(port
? 1 : 0, dev
, id
)) {
679 mov
.set_controls(update_controls(true));
682 int16_t in
= mov
.next_input(port
? 1 : 0, dev
, id
);
683 //debuglog << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame " << mov.get_current_frame()
684 // << ") giving " << in << std::endl;