2016-12-18 02:13:57 +00:00
//
// PCMSegment.cpp
// Clock Signal
//
// Created by Thomas Harte on 17/12/2016.
2018-05-13 19:19:52 +00:00
// Copyright 2016 Thomas Harte. All rights reserved.
2016-12-18 02:13:57 +00:00
//
# include "PCMSegment.hpp"
2018-06-11 01:02:19 +00:00
# include <cassert>
2020-01-20 01:09:11 +00:00
# include <cstdlib>
2018-06-11 01:02:19 +00:00
2016-12-18 02:13:57 +00:00
using namespace Storage : : Disk ;
PCMSegmentEventSource : : PCMSegmentEventSource ( const PCMSegment & segment ) :
2017-03-26 18:34:47 +00:00
segment_ ( new PCMSegment ( segment ) ) {
2016-12-19 03:53:24 +00:00
// add an extra bit of storage at the bottom if one is going to be needed;
// events returned are going to be in integral multiples of the length of a bit
// other than the very first and very last which will include a half bit length
2017-03-26 18:34:47 +00:00
if ( segment_ - > length_of_a_bit . length & 1 ) {
2016-12-30 19:23:26 +00:00
segment_ - > length_of_a_bit . length < < = 1 ;
segment_ - > length_of_a_bit . clock_rate < < = 1 ;
2016-12-18 02:13:57 +00:00
}
2016-12-19 03:53:24 +00:00
// load up the clock rate once only
2016-12-30 19:23:26 +00:00
next_event_ . length . clock_rate = segment_ - > length_of_a_bit . clock_rate ;
2016-12-19 03:53:24 +00:00
// set initial conditions
2016-12-18 02:13:57 +00:00
reset ( ) ;
}
2017-03-26 18:34:47 +00:00
PCMSegmentEventSource : : PCMSegmentEventSource ( const PCMSegmentEventSource & original ) {
2024-04-18 02:15:05 +00:00
* this = original ;
}
PCMSegmentEventSource & PCMSegmentEventSource : : operator = ( const PCMSegmentEventSource & original ) {
2016-12-30 19:23:26 +00:00
// share underlying data with the original
segment_ = original . segment_ ;
2016-12-30 22:55:46 +00:00
// load up the clock rate and set initial conditions
next_event_ . length . clock_rate = segment_ - > length_of_a_bit . clock_rate ;
2016-12-30 19:23:26 +00:00
reset ( ) ;
2024-04-18 02:15:05 +00:00
return * this ;
2016-12-30 19:23:26 +00:00
}
2024-04-18 02:15:05 +00:00
2017-03-26 18:34:47 +00:00
void PCMSegmentEventSource : : reset ( ) {
2016-12-19 03:53:24 +00:00
// start with the first bit to be considered the zeroth, and assume that it'll be
// flux transitions for the foreseeable
2016-12-18 02:13:57 +00:00
bit_pointer_ = 0 ;
next_event_ . type = Track : : Event : : FluxTransition ;
}
2018-05-02 00:31:42 +00:00
PCMSegment & PCMSegment : : operator + = ( const PCMSegment & rhs ) {
2018-07-01 16:05:41 +00:00
data . insert ( data . end ( ) , rhs . data . begin ( ) , rhs . data . end ( ) ) ;
2018-05-02 00:31:42 +00:00
return * this ;
}
2018-08-28 00:56:25 +00:00
void PCMSegment : : rotate_right ( size_t length ) {
length % = data . size ( ) ;
if ( ! length ) return ;
// To rotate to the right, front-insert the proper number
// of bits from the end and then resize. To rotate to
// the left, do the opposite.
std : : vector < uint8_t > data_copy ;
if ( length > 0 ) {
2022-04-27 23:16:37 +00:00
data_copy . insert ( data_copy . end ( ) , data . end ( ) - ptrdiff_t ( length ) , data . end ( ) ) ;
data . erase ( data . end ( ) - ptrdiff_t ( length ) , data . end ( ) ) ;
2018-08-28 00:56:25 +00:00
data . insert ( data . begin ( ) , data_copy . begin ( ) , data_copy . end ( ) ) ;
} else {
2022-04-27 23:16:37 +00:00
data_copy . insert ( data_copy . end ( ) , data . begin ( ) , data . begin ( ) - ptrdiff_t ( length ) ) ;
data . erase ( data . begin ( ) , data . begin ( ) - ptrdiff_t ( length ) ) ;
2018-08-28 00:56:25 +00:00
data . insert ( data . end ( ) , data_copy . begin ( ) , data_copy . end ( ) ) ;
}
}
2017-03-26 18:34:47 +00:00
Storage : : Disk : : Track : : Event PCMSegmentEventSource : : get_next_event ( ) {
2020-01-20 01:09:11 +00:00
// Track the initial bit pointer for potentially considering whether this was an
// initial index hole or a subsequent one later on.
2018-07-01 19:38:42 +00:00
const std : : size_t initial_bit_pointer = bit_pointer_ ;
2016-12-19 03:53:24 +00:00
2020-01-20 01:09:11 +00:00
// If starting from the beginning, pull half a bit backward, as if the initial bit
// is set, it should be in the centre of its window.
2016-12-30 19:23:26 +00:00
next_event_ . length . length = bit_pointer_ ? 0 : - ( segment_ - > length_of_a_bit . length > > 1 ) ;
2016-12-18 02:13:57 +00:00
2016-12-19 03:53:24 +00:00
// search for the next bit that is set, if any
2018-07-01 16:05:41 +00:00
while ( bit_pointer_ < segment_ - > data . size ( ) ) {
bool bit = segment_ - > data [ bit_pointer_ ] ;
2020-01-20 01:09:11 +00:00
+ + bit_pointer_ ; // so this always points one beyond the most recent bit returned
2016-12-30 19:23:26 +00:00
next_event_ . length . length + = segment_ - > length_of_a_bit . length ;
2016-12-18 02:13:57 +00:00
2020-01-20 01:09:11 +00:00
// if this bit is set, or is fuzzy and a random bit of 1 is selected, return the event.
if ( bit | |
( ! segment_ - > fuzzy_mask . empty ( ) & & segment_ - > fuzzy_mask [ bit_pointer_ ] & & lfsr_ . next ( ) )
) return next_event_ ;
2016-12-18 02:13:57 +00:00
}
2020-01-20 01:09:11 +00:00
// If the end is reached without a bit being set, it'll be index holes from now on.
2016-12-18 02:13:57 +00:00
next_event_ . type = Track : : Event : : IndexHole ;
2016-12-19 03:53:24 +00:00
2020-01-20 01:09:11 +00:00
// Test whether this is the very first time that bits have been exhausted. If so then
2016-12-19 03:53:24 +00:00
// allow an extra half bit's length to run from the position of the potential final transition
// event to the end of the segment. Otherwise don't allow any extra time, as it's already
2020-01-20 01:09:11 +00:00
// been consumed.
2018-07-01 16:05:41 +00:00
if ( initial_bit_pointer < = segment_ - > data . size ( ) ) {
2016-12-30 19:23:26 +00:00
next_event_ . length . length + = ( segment_ - > length_of_a_bit . length > > 1 ) ;
2016-12-19 03:53:24 +00:00
bit_pointer_ + + ;
}
2016-12-18 02:47:13 +00:00
return next_event_ ;
2016-12-18 02:13:57 +00:00
}
2016-12-18 03:44:33 +00:00
2017-03-26 18:34:47 +00:00
Storage : : Time PCMSegmentEventSource : : get_length ( ) {
2020-05-10 03:00:39 +00:00
return segment_ - > length_of_a_bit * unsigned ( segment_ - > data . size ( ) ) ;
2016-12-18 03:44:33 +00:00
}
2020-07-18 02:08:58 +00:00
float PCMSegmentEventSource : : seek_to ( float time_from_start ) {
2016-12-18 03:44:33 +00:00
// test for requested time being beyond the end
2020-07-18 02:08:58 +00:00
const float length = get_length ( ) . get < float > ( ) ;
2017-03-26 18:34:47 +00:00
if ( time_from_start > = length ) {
2016-12-18 03:44:33 +00:00
next_event_ . type = Track : : Event : : IndexHole ;
2018-07-01 16:05:41 +00:00
bit_pointer_ = segment_ - > data . size ( ) + 1 ;
2016-12-18 03:44:33 +00:00
return length ;
}
// if not beyond the end then make an initial assumption that the next thing encountered will be a flux transition
next_event_ . type = Track : : Event : : FluxTransition ;
// test for requested time being before the first bit
2020-07-18 02:08:58 +00:00
const float bit_length = segment_ - > length_of_a_bit . get < float > ( ) ;
const float half_bit_length = bit_length / 2.0f ;
2017-03-26 18:34:47 +00:00
if ( time_from_start < half_bit_length ) {
2016-12-18 03:44:33 +00:00
bit_pointer_ = 0 ;
2020-07-18 02:08:58 +00:00
return 0.0f ;
2016-12-18 03:44:33 +00:00
}
2016-12-18 15:19:24 +00:00
// adjust for time to get to bit zero and determine number of bits in;
// bit_pointer_ always records _the next bit_ that might trigger an event,
// so should be one beyond the one reached by a seek.
2020-07-18 03:18:08 +00:00
const float relative_time = time_from_start + half_bit_length ; // the period [0, 0.5) should map to window 0, ending with bit 0; [0.5, 1.5) should map to window 1; etc.
bit_pointer_ = size_t ( relative_time / bit_length ) ;
2016-12-18 03:44:33 +00:00
2020-07-18 03:18:08 +00:00
// Map up to the correct amount of time; this should be the start of the window that ends upon the bit at bit_pointer_.
return bit_length * float ( bit_pointer_ ) - half_bit_length ;
2016-12-18 03:44:33 +00:00
}
2018-07-01 16:05:41 +00:00
const PCMSegment & PCMSegmentEventSource : : segment ( ) const {
return * segment_ ;
}
2018-07-01 22:28:25 +00:00
PCMSegment & PCMSegmentEventSource : : segment ( ) {
return * segment_ ;
}