EchoMap 2026-07-25 6d3977c
An experimental cross-platform digital signal processing application for sound-source localisation.
Loading...
Searching...
No Matches
echomap::Signal Class Reference

A single channel of discretely sampled audio data. More...

#include <Signal.hpp>

Inheritance diagram for echomap::Signal:
[legend]

Classes

struct  Sample
 A PCM float-32 sampled audio point at an explicit time offset. More...
struct  Source
 Indicates an external source of a Signal on the filesystem. More...
struct  Baseline
 Provides basic timing information relating to the Signal samples. More...

Public Member Functions

std::uint64_t get_sample_count () const noexcept
 Retrieves the total number of samples in the Signal stream.
const std::optional< Source > & observe_source () const noexcept
 Retrieves the optional Source of the Signal.
Sample::TimeT get_time_offset () const noexcept
std::size_t get_sample_rate () const noexcept
decltype(samples) ::const_iterator begin () const
decltype(samples) ::const_iterator end () const
decltype(samples) ::const_iterator cbegin () const noexcept
decltype(samples) ::const_iterator cend () const noexcept
auto timed_samples () const
Sample::AmplitudeT operator[] (std::size_t index) const noexcept
std::span< const Sample::AmplitudeTamplitudes () const noexcept
bool is_uniformly_sampled () const noexcept
Sample::TimeT get_time_at_index (std::size_t index) const noexcept
 Determines the corresponding time of the amplitude appearing at the given index in the sample array.
 Signal (const Signal &old_signal)
 Signal (const Signal &old_signal, std::string_view new_name)
Public Member Functions inherited from echomap::Object< Signal >
 ~Object ()=default
 Non-virtual base destructor.
id_type get_id () const noexcept
bool is_valid () const noexcept
void set_name (const std::string_view new_name)
std::string_view get_name () const noexcept
const char * get_imgui_name () const noexcept
 Object (const Object &)=delete
Object & operator= (const Object &)=delete
bool operator== (const Object &other) const noexcept
 Determine shallow equality between two Object instances.

Static Public Attributes

static constexpr std::pair< Sample::AmplitudeT, Sample::AmplitudeTnormalised_range = {-1.0f, 1.0f}
 The range within which the amplitude values are normalised.

Private Member Functions

 Signal (std::string_view name={}, const std::optional< Source > &source={})
 Creates an empty optionally named Signal.
void emplace_sample (Sample::AmplitudeT amplitude)
 Emplace a sample to the back of the channel sample data.
void emplace_sample (Sample::TimeT time, Sample::AmplitudeT amplitude)
 Emplace a sample to the back of the channel sample data.
void emplace_sample (const Sample &sample)
 Emplace a sample to the back of the channel sample data.
void emplace_sample_from_source (Sample::AmplitudeT amplitude)
 Emplace an externally sourced sample to the back of the channel sample data.
void emplace_sample_from_source (Sample::TimeT time, Sample::AmplitudeT amplitude)
 Emplace an externally sourced sample to the back of the channel sample data.
void emplace_sample_from_source (const Sample &sample)
 Emplace an externally sourced sample to the back of the channel sample data.
void reserve_samples (std::size_t count)
 Reserves memory to store the given number of total samples in the channel.
void set_source (const std::filesystem::path &path, std::size_t channel)
void set_time_offset (Sample::TimeT new_time_offset) noexcept
void set_sample_rate (std::size_t new_sample_rate) noexcept
void emplace_time (Sample::TimeT given_time)
 Implementation helper to associate the latest amplitude sample with the given time.

Private Attributes

std::vector< Sample::AmplitudeTsamples
 Amplitude sample stream.
Baseline timing_baseline
 A baseline of timing parameters.
std::optional< Sourcefs_source
 External source, if any, of the Signal Sample stream.
std::optional< std::vector< Sample::TimeT > > time_offsets
 An optional vector of explicit timestamps for each sample.

Friends

class SignalFactory

Additional Inherited Members

Static Public Member Functions inherited from echomap::Object< Signal >
static std::string_view get_class_name () noexcept
Protected Member Functions inherited from echomap::Object< Signal >
void move_identity_from (Object &&other) noexcept
 Helper for derived classes to provide move-assignment operations.

Detailed Description

A single channel of discretely sampled audio data.

Signals always store their sampled time series in memory, but they optionally express an external source. This is currently a file specification on the local file system, and an internal channel number, indicating the origin of the sample data.

Signal objects must be constructed by a SignalFactory. Following release from the factory, all non-metadata state is immutable, particularly the Sample time series. Trivial metadata (such as the display name from Object) may be modified at will.

Invariant
Amplitude samples are stored contiguously in memory. Timing information may either be inferred from the baseline sampling model or adjusted by an optional per-sample offset array. The timed samples range is a lazy logical view and is not contiguous.

Definition at line 39 of file Signal.hpp.

Constructor & Destructor Documentation

◆ Signal() [1/3]

echomap::Signal::Signal ( const Signal & old_signal)

Definition at line 105 of file Signal.cpp.

107 :
108 Object(CopyTag{},
109 old_signal),
110 samples(old_signal.samples),
111 timing_baseline(old_signal.timing_baseline),
112 fs_source(old_signal.fs_source),
113 time_offsets(old_signal.time_offsets)
114{
115}
std::vector< Sample::AmplitudeT > samples
Amplitude sample stream.
Definition Signal.hpp:55
std::optional< Source > fs_source
External source, if any, of the Signal Sample stream.
Definition Signal.hpp:257
Baseline timing_baseline
A baseline of timing parameters.
Definition Signal.hpp:256
std::optional< std::vector< Sample::TimeT > > time_offsets
An optional vector of explicit timestamps for each sample.
Definition Signal.hpp:281

◆ Signal() [2/3]

echomap::Signal::Signal ( const Signal & old_signal,
std::string_view new_name )

Definition at line 117 of file Signal.cpp.

120 :
121 Object(CopyTag{},
122 old_signal,
123 new_name),
124 samples(old_signal.samples),
125 timing_baseline(old_signal.timing_baseline),
126 fs_source(old_signal.fs_source),
127 time_offsets(old_signal.time_offsets)
128{
129}

◆ Signal() [3/3]

echomap::Signal::Signal ( std::string_view name = {},
const std::optional< Source > & source = {} )
explicitprivate

Creates an empty optionally named Signal.

Parameters
nameOptional display name.
sourceOptional file system source path indicating the origin of the file.

Definition at line 19 of file Signal.cpp.

22 :
23 Object(name),
24 fs_source(source)
25{
26}

Member Function Documentation

◆ amplitudes()

std::span< const Signal::Sample::AmplitudeT > echomap::Signal::amplitudes ( ) const
nodiscardnoexcept

Definition at line 85 of file Signal.cpp.

86{
87 return samples;
88}

◆ begin()

decltype(Signal::samples) const_iterator echomap::Signal::begin ( ) const
nodiscard

Definition at line 58 of file Signal.cpp.

59{
60 return samples.begin();
61}

◆ cbegin()

decltype(Signal::samples) const_iterator echomap::Signal::cbegin ( ) const
nodiscardnoexcept

Definition at line 68 of file Signal.cpp.

69{
70 return samples.cbegin();
71}

◆ cend()

decltype(Signal::samples) const_iterator echomap::Signal::cend ( ) const
nodiscardnoexcept

Definition at line 73 of file Signal.cpp.

74{
75 return samples.cend();
76}

◆ emplace_sample() [1/3]

void echomap::Signal::emplace_sample ( const Sample & sample)
private

Emplace a sample to the back of the channel sample data.

Parameters
sampleThe Sample to insert at the back of the stream.
Exceptions
std::runtime_errorThe time of the Sample violated the monotonically increasing invariant.
Precondition
The given amplitude is within the fixed normalised range.

Definition at line 157 of file Signal.cpp.

160{
161 emplace_sample(sample.time, sample.amplitude);
162}
void emplace_sample(Sample::AmplitudeT amplitude)
Emplace a sample to the back of the channel sample data.
Definition Signal.cpp:131
T sample(T... args)

◆ emplace_sample() [2/3]

void echomap::Signal::emplace_sample ( Sample::AmplitudeT amplitude)
private

Emplace a sample to the back of the channel sample data.

The inserted amplitude sample will be associated with the next timestep, computed automatically based on the defined sample rate and time offset.

Parameters
amplitudeAmplitude of the sample to insert.
Precondition
The given amplitude is within the fixed normalised range.

Definition at line 131 of file Signal.cpp.

134{
135 assert(amplitude >= normalised_range.first && amplitude <= normalised_range.second);
136 samples.emplace_back(amplitude);
137 if (time_offsets.has_value())
138 time_offsets->emplace_back(0);
139
140 if (fs_source.has_value())
141 fs_source->dirty = true;
142}
static constexpr std::pair< Sample::AmplitudeT, Sample::AmplitudeT > normalised_range
The range within which the amplitude values are normalised.
Definition Signal.hpp:74

◆ emplace_sample() [3/3]

void echomap::Signal::emplace_sample ( Sample::TimeT time,
Sample::AmplitudeT amplitude )
private

Emplace a sample to the back of the channel sample data.

Parameters
timeThe time to associate with the amplitude, in seconds.
amplitudeThe amplitude to associate with the time.
Exceptions
std::runtime_errorThe time of the Sample violated the monotonically increasing invariant.
Precondition
The given amplitude is within the fixed normalised range.

Definition at line 144 of file Signal.cpp.

148{
149 assert(amplitude >= normalised_range.first && amplitude <= normalised_range.second);
150 samples.emplace_back(amplitude);
151 emplace_time(time);
152
153 if (fs_source.has_value())
154 fs_source->dirty = true;
155}
void emplace_time(Sample::TimeT given_time)
Implementation helper to associate the latest amplitude sample with the given time.
Definition Signal.cpp:224

◆ emplace_sample_from_source() [1/3]

void echomap::Signal::emplace_sample_from_source ( const Sample & sample)
private

Emplace an externally sourced sample to the back of the channel sample data.

Parameters
sampleThe Sample to insert at the back of the stream.
Exceptions
std::runtime_errorThe time of the Sample violated the monotonically increasing invariant.
Precondition
The given amplitude is within the fixed normalised range.

Definition at line 184 of file Signal.cpp.

187{
188 return emplace_sample_from_source(sample.time, sample.amplitude);
189}
void emplace_sample_from_source(Sample::AmplitudeT amplitude)
Emplace an externally sourced sample to the back of the channel sample data.
Definition Signal.cpp:164

◆ emplace_sample_from_source() [2/3]

void echomap::Signal::emplace_sample_from_source ( Sample::AmplitudeT amplitude)
private

Emplace an externally sourced sample to the back of the channel sample data.

The inserted amplitude sample will be associated with the next timestep, computed automatically based on the defined sample rate and time offset.

Parameters
amplitudeAmplitude of the sample to insert.
Precondition
The given amplitude is within the fixed normalised range.

Definition at line 164 of file Signal.cpp.

167{
168 assert(amplitude >= normalised_range.first && amplitude <= normalised_range.second);
169 samples.emplace_back(amplitude);
170 if (time_offsets.has_value())
171 time_offsets->emplace_back(0);
172}

◆ emplace_sample_from_source() [3/3]

void echomap::Signal::emplace_sample_from_source ( Sample::TimeT time,
Sample::AmplitudeT amplitude )
private

Emplace an externally sourced sample to the back of the channel sample data.

Parameters
timeThe time to associate with the amplitude, in seconds.
amplitudeThe amplitude to associate with the time.
Exceptions
std::runtime_errorThe time of the Sample violated the monotonically increasing invariant.
Precondition
The given amplitude is within the fixed normalised range.

Definition at line 174 of file Signal.cpp.

178{
179 assert(amplitude >= normalised_range.first && amplitude <= normalised_range.second);
180 samples.emplace_back(amplitude);
181 emplace_time(time);
182}

◆ emplace_time()

void echomap::Signal::emplace_time ( Sample::TimeT given_time)
private

Implementation helper to associate the latest amplitude sample with the given time.

Parameters
given_timeThe time to associate with the latest sample.
Exceptions
std::runtime_errorThe time of the Sample violated the monotonically increasing invariant.

Definition at line 224 of file Signal.cpp.

227{
228 assert(!samples.empty());
229
230 constexpr Sample::TimeT epsilon = 1.0e-6f;
231 const auto expected =
232 timing_baseline.time_offset + static_cast<float>(samples.size() - 1) * timing_baseline.sample_rate_r;
233
234 // Check the time derived from the baseline, as if we're continuing with a uniformly sampled signal.
235 if (const auto offset = given_time - expected; std::abs(offset) <= epsilon) {
236 /*
237 * If the given time matches what we expect, we don't need to do anything. Only update the explicit offsets
238 * (with an offset of zero) if they're already there.
239 */
240 if (time_offsets.has_value())
241 time_offsets->emplace_back(0.0f);
242 } else {
243 /*
244 * If the given time doesn't match what we expect, then a variably sampled entry has been introduced. We need to
245 * consider:
246 *
247 * 1. if insertion of the time would violate the class invariant, such that time values are monotonically
248 * increasing, then an exception is due; or
249 *
250 * 2. if the time preserves the invariant, but is the first non-uniform entry, then we need to create offset
251 * entries (which will have offset of zero, since they followed the uniform pattern by construction), and
252 * emplace our offset on the end.
253 *
254 * 3. if the time preserves the invariant, and is being emplaced into an already-variable signal, then we
255 * simply note the offset.
256 */
257
258 if (samples.size() >= 2)
259 if (const auto previous_time = get_time_at_index(samples.size() - 2); given_time <= previous_time + epsilon)
260 // Case 1.
261 throw std::runtime_error(
263 "Signal {} rejected out-of-order sample at time {}s (previous sample was accepted at "
264 "time {}s).",
265 get_name(),
266 given_time,
267 previous_time
268 )
269 );
270
271 if (time_offsets.has_value())
272 // Case 3.
273 time_offsets->emplace_back(offset);
274 else {
275 // Case 2.
276 time_offsets.emplace(samples.size(), 0.0f);
277 time_offsets->back() = offset;
278 }
279 }
280}
Sample::TimeT get_time_at_index(std::size_t index) const noexcept
Determines the corresponding time of the amplitude appearing at the given index in the sample array.
Definition Signal.cpp:95
T format(T... args)
float TimeT
Type for sample times.
Definition Signal.hpp:47

◆ end()

decltype(Signal::samples) const_iterator echomap::Signal::end ( ) const
nodiscard

Definition at line 63 of file Signal.cpp.

64{
65 return samples.end();
66}

◆ get_sample_count()

std::uint64_t echomap::Signal::get_sample_count ( ) const
nodiscardnoexcept

Retrieves the total number of samples in the Signal stream.

Returns
The number of samples detained by the Signal.

Definition at line 38 of file Signal.cpp.

39{
40 return samples.size();
41}

◆ get_sample_rate()

std::size_t echomap::Signal::get_sample_rate ( ) const
nodiscardnoexcept

Definition at line 53 of file Signal.cpp.

54{
55 return timing_baseline.sample_rate;
56}

◆ get_time_at_index()

Signal::Sample::TimeT echomap::Signal::get_time_at_index ( std::size_t index) const
nodiscardnoexcept

Determines the corresponding time of the amplitude appearing at the given index in the sample array.

Parameters
indexThe index of the amplitude in the sample array.
Returns
The timestamp corresponding to the referenced sample.
Precondition
The index is within the bounds of the sample array.

Definition at line 95 of file Signal.cpp.

98{
99 assert(index < samples.size());
100 const Sample::TimeT baseline_time =
101 timing_baseline.time_offset + static_cast<Sample::TimeT>(index) * timing_baseline.sample_rate_r;
102 return time_offsets.has_value() ? baseline_time + (*time_offsets)[index] : baseline_time;
103}

◆ get_time_offset()

Signal::Sample::TimeT echomap::Signal::get_time_offset ( ) const
nodiscardnoexcept

Definition at line 48 of file Signal.cpp.

49{
50 return timing_baseline.time_offset;
51}

◆ is_uniformly_sampled()

bool echomap::Signal::is_uniformly_sampled ( ) const
nodiscardnoexcept

Definition at line 90 of file Signal.cpp.

91{
92 return !time_offsets.has_value();
93}

◆ observe_source()

const std::optional< Signal::Source > & echomap::Signal::observe_source ( ) const
nodiscardnoexcept

Retrieves the optional Source of the Signal.

The return value of this operation can be used to determine the origin of the Signal: if the optional is empty, the signal should be considered "embedded". Otherwise, it has an origin on the filesystem at the given SignalSource.

If the Signal has an external source, the stored samples in the Signal object do not necessarily match the external file, as callers may have invoked emplace_sample with arbitrary samples. The Source should only be considered a hint. This can be checked by inspecting the Source::dirty flag.

Returns
The Source of the Signal, or an empty optional if the Signal is not externally sourced.

Definition at line 43 of file Signal.cpp.

44{
45 return fs_source;
46}

◆ operator[]()

Signal::Sample::AmplitudeT echomap::Signal::operator[] ( std::size_t index) const
nodiscardnoexcept

Definition at line 78 of file Signal.cpp.

81{
82 return samples[index];
83}

◆ reserve_samples()

void echomap::Signal::reserve_samples ( std::size_t count)
private

Reserves memory to store the given number of total samples in the channel.

Parameters
countThe number of Sample objects to pre-allocate.

Definition at line 191 of file Signal.cpp.

194{
195 samples.reserve(count);
196
197 if (time_offsets.has_value())
198 time_offsets->reserve(count);
199}

◆ set_sample_rate()

void echomap::Signal::set_sample_rate ( std::size_t new_sample_rate)
privatenoexcept

Definition at line 216 of file Signal.cpp.

219{
220 timing_baseline.sample_rate = new_sample_rate;
221 timing_baseline.sample_rate_r = new_sample_rate == 0 ? 0 : 1.0f / static_cast<float>(new_sample_rate);
222}

◆ set_source()

void echomap::Signal::set_source ( const std::filesystem::path & path,
std::size_t channel )
private

Definition at line 201 of file Signal.cpp.

205{
206 fs_source = Source(path, channel);
207}
Indicates an external source of a Signal on the filesystem.
Definition Signal.hpp:62

◆ set_time_offset()

void echomap::Signal::set_time_offset ( Sample::TimeT new_time_offset)
privatenoexcept

Definition at line 209 of file Signal.cpp.

212{
213 timing_baseline.time_offset = new_time_offset;
214}

◆ timed_samples()

auto echomap::Signal::timed_samples ( ) const
inlinenodiscard

Definition at line 109 of file Signal.hpp.

110 {
111 return std::views::iota(std::size_t{0}, samples.size()) |
112 std::views::transform([this](const std::size_t index) -> Sample {
113 return {.time = get_time_at_index(index), .amplitude = samples[index]};
114 });
115 }
A PCM float-32 sampled audio point at an explicit time offset.
Definition Signal.hpp:46

◆ SignalFactory

friend class SignalFactory
friend

Definition at line 147 of file Signal.hpp.

Member Data Documentation

◆ fs_source

std::optional<Source> echomap::Signal::fs_source
private

External source, if any, of the Signal Sample stream.

Definition at line 257 of file Signal.hpp.

◆ normalised_range

std::pair<Sample::AmplitudeT, Sample::AmplitudeT> echomap::Signal::normalised_range = {-1.0f, 1.0f}
staticconstexpr

The range within which the amplitude values are normalised.

Definition at line 74 of file Signal.hpp.

74{-1.0f, 1.0f};

◆ samples

std::vector<Sample::AmplitudeT> echomap::Signal::samples
private

Amplitude sample stream.

Definition at line 55 of file Signal.hpp.

◆ time_offsets

std::optional<std::vector<Sample::TimeT> > echomap::Signal::time_offsets
private

An optional vector of explicit timestamps for each sample.

If provided, the timestamps provide a sample-by-sample indication of the time corresponding with amplitudes in the sample time series data. The scalars are specified as offsets relative to the baseline time, which is computed as \( b_i = t_0 + i \delta t \), where \( t_0 \) is the global time offset, \( \delta t \) is the reciprocal of the sample rate, and \( i \) is the integer zero-based index such that \( 0 < i \leq \vert S \vert \), where \( \vert S \vert \) denotes the size of the sample set \( S \).

This is useful to support variably sampled time series, such as in the result of LTTB downsampling. Whether explicit timestamps are provided is rather opaque to the user: they can request timing information, which may be inferred from the baseline or indexed from this array as appropriate.

When a new sample is emplaced, a time can be provided. If the given time is different than what would be expected from the baseline, this vector is updated with the suitable offset. If it doesn't exist, it is created.

Invariant
Timestamp elements are in bijective correspondence with the amplitude samples.

Definition at line 281 of file Signal.hpp.

◆ timing_baseline

Baseline echomap::Signal::timing_baseline
private

A baseline of timing parameters.

Definition at line 256 of file Signal.hpp.


The documentation for this class was generated from the following files: