// // PCMSegment.hpp // Clock Signal // // Created by Thomas Harte on 17/12/2016. // Copyright 2016 Thomas Harte. All rights reserved. // #pragma once #include #include #include #include "../../Storage.hpp" #include "../../../Numeric/LFSR.hpp" #include "Track.hpp" namespace Storage::Disk { /*! A segment of PCM-sampled data. */ struct PCMSegment { /*! Determines the amount of space that each bit of data occupies; allows PCMSegments of different densities. */ Time length_of_a_bit = Time(1); /*! This is the actual data, taking advantage of the std::vector specialisation to use whatever one-bit-per-value encoding is most suited to this architecture. If a value is @c true then a flux transition occurs in that window. If it is @c false then no flux transition occurs. */ std::vector data; /*! If a segment has a fuzzy mask then anywhere the mask has a value of @c true, a random bit will be ORd onto whatever is in the corresponding slot in @c data. */ std::vector fuzzy_mask; /*! Constructs an instance of PCMSegment with the specified @c length_of_a_bit and @c data. */ PCMSegment(Time length_of_a_bit, const std::vector &data) : length_of_a_bit(length_of_a_bit), data(data) {} /*! Constructs an instance of PCMSegment where each bit window is 1 unit of time long and @c data is populated from the supplied @c source by serialising it from MSB to LSB for @c number_of_bits. */ PCMSegment(size_t number_of_bits, const uint8_t *source) : data(number_of_bits, false) { for(size_t c = 0; c < number_of_bits; ++c) { if((source[c >> 3] >> (7 ^ (c & 7)))&1) { data[c] = true; } } } /*! Constructs an instance of PCMSegment where each bit window is the length specified by @c length_of_a_bit, and @c data is populated from the supplied @c source by serialising it from MSB to LSB for @c number_of_bits. */ PCMSegment(Time length_of_a_bit, size_t number_of_bits, const uint8_t *source) : PCMSegment(number_of_bits, source) { this->length_of_a_bit = length_of_a_bit; } /*! Constructs an instance of PCMSegment where each bit window is the length specified by @c length_of_a_bit, and @c data is populated from the supplied @c source by serialising it from MSB to LSB for @c number_of_bits. */ PCMSegment(Time length_of_a_bit, size_t number_of_bits, const std::vector &source) : PCMSegment(length_of_a_bit, number_of_bits, source.data()) {} /*! Constructs an instance of PCMSegment where each bit window is 1 unit of time long and @c data is populated from the supplied @c source by serialising it from MSB to LSB for @c number_of_bits. */ PCMSegment(size_t number_of_bits, const std::vector &source) : PCMSegment(number_of_bits, source.data()) {} /*! Constructs an instance of PCMSegment where each bit window is 1 unit of time long and @c data is populated from the supplied @c source by serialising it from MSB to LSB, assuming every bit provided is used. */ PCMSegment(const std::vector &source) : PCMSegment(source.size() * 8, source.data()) {} /*! Constructs an instance of PCMSegment where each bit window is 1 unit of time long and @c data is empty. */ PCMSegment() = default; /// Empties the PCMSegment. void clear() { data.clear(); } /*! Rotates all bits in this segment by @c length bits. @c length is signed; to rotate left provide a negative number. */ void rotate_right(size_t length); /*! Produces a byte buffer where the contents of @c data are serialised into bytes If @c msb_first is @c true then each byte is expected to be deserialised from MSB to LSB. If @c msb_first is @c false then each byte is expected to be deserialised from LSB to MSB. */ std::vector byte_data(bool msb_first = true) const { std::vector bytes((data.size() + 7) >> 3); size_t pointer = 0; const size_t pointer_mask = msb_first ? 7 : 0; for(const auto bit: data) { if(bit) bytes[pointer >> 3] |= 1 << ((pointer & 7) ^ pointer_mask); ++pointer; } return bytes; } /// Appends the data of @c rhs to the current data. Does not adjust @c length_of_a_bit. PCMSegment &operator +=(const PCMSegment &rhs); /// @returns the total amount of time occupied by all the data stored in this segment. Time length() const { return length_of_a_bit * unsigned(data.size()); } }; /*! Provides a stream of events by inspecting a PCMSegment. */ class PCMSegmentEventSource { public: /*! Constructs a @c PCMSegmentEventSource that will derive events from @c segment. The event source is initially @c reset. */ PCMSegmentEventSource(const PCMSegment &); /*! Copy constructor; produces a segment event source with the same underlying segment but a unique pointer into it. */ PCMSegmentEventSource(const PCMSegmentEventSource &); /*! @returns the next event that will occur in this event stream. */ Track::Event get_next_event(); /*! Resets the event source to the beginning of its event stream, exactly as if it has just been constructed. */ void reset(); /*! Seeks as close to @c time_from_start as the event source can manage while not exceeding it. @returns the time the source is now at. */ float seek_to(float time_from_start); /*! @returns the total length of the stream of data that the source will provide. */ Time get_length(); /*! @returns a reference to the underlying segment. */ const PCMSegment &segment() const; PCMSegment &segment(); private: std::shared_ptr segment_; std::size_t bit_pointer_; Track::Event next_event_; Numeric::LFSR lfsr_; }; }