CLK/Storage/Disk/Track/PCMSegment.hpp

206 lines
5.7 KiB
C++

//
// PCMSegment.hpp
// Clock Signal
//
// Created by Thomas Harte on 17/12/2016.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#pragma once
#include <cstdint>
#include <memory>
#include <vector>
#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<bool>
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<bool> 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<bool> 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<bool> &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<uint8_t> &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<uint8_t> &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<uint8_t> &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<uint8_t> byte_data(bool msb_first = true) const {
std::vector<uint8_t> 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 &);
PCMSegmentEventSource &operator =(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<PCMSegment> segment_;
std::size_t bit_pointer_;
Track::Event next_event_;
Numeric::LFSR<uint64_t> lfsr_;
};
}