VLink  2.0.0
A high-performance communication middleware
audio_frame.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file audio_frame.h
26  * @brief Zero-copy audio frame container carrying one PCM or codec-encoded packet.
27  *
28  * @details
29  * @c AudioFrame is the canonical conduit for in-vehicle and embedded audio
30  * pipelines: microphone capture, in-cabin voice command, TTS playback, alarm
31  * tones, and intercom routing. Each frame carries one acquisition window of
32  * audio together with sample-format metadata (sample rate, channels, bit
33  * depth, layout, codec hint, language tag), a capture timestamp, the
34  * advertised frame duration, plus a 40-byte @c Header for sequencing and
35  * dual-timestamp latency measurement.
36  *
37  * @par Audio formats
38  * | Enum | Encoding | Typical bit depth | Compression |
39  * | ---------------- | ------------------------ | ----------------- | ----------- |
40  * | @c kFormatPcmS16 | Signed linear PCM | 16 | None |
41  * | @c kFormatPcmS24 | Signed linear PCM packed | 24 | None |
42  * | @c kFormatPcmS32 | Signed linear PCM | 32 | None |
43  * | @c kFormatPcmF32 | IEEE-754 float PCM | 32 | None |
44  * | @c kFormatPcmU8 | Unsigned linear PCM | 8 | None |
45  * | @c kFormatOpus | Opus | N/A | Lossy |
46  * | @c kFormatAac | AAC | N/A | Lossy |
47  * | @c kFormatMp3 | MP3 | N/A | Lossy |
48  * | @c kFormatFlac | FLAC | N/A | Lossless |
49  *
50  * Common sample rates: 8 kHz / 16 kHz / 32 kHz / 44.1 kHz / 48 kHz / 96 kHz.
51  * Channel layouts: @c kLayoutInterleaved (L R L R ...) or @c kLayoutPlanar
52  * (separate per-channel planes).
53  *
54  * @par Wire format
55  * @c AudioFrame is POD; @c memcpy of the struct snapshot plus the audio buffer
56  * forms the wire payload. The @c sizeof value is locked by @c static_assert
57  * and forms a permanent contract: @c vlink::zerocopy::* containers have NO
58  * forward and NO backward binary compatibility -- every field, including
59  * reserved bytes, is part of the wire contract.
60  * @code
61  * static_assert(sizeof(AudioFrame) == 128, "Sizeof must be 128 bytes.");
62  * @endcode
63  *
64  * @par Memory layout
65  * @code
66  * Offset Size Field
67  * ------ ---- --------------------------
68  * 0 40 Header header
69  * 40 8 uint8_t* data_
70  * 48 8 size_t size_
71  * 56 8 uint64_t update_time_ns_
72  * 64 8 uint64_t duration_ns_
73  * 72 16 char codec_[16]
74  * 88 8 char language_[8]
75  * 96 4 uint32_t channel_
76  * 100 4 uint32_t freq_
77  * 104 4 uint32_t sample_rate_
78  * 108 4 uint32_t num_samples_
79  * 112 4 uint32_t bitrate_
80  * 116 2 uint16_t num_channels_
81  * 118 2 uint16_t bit_depth_
82  * 120 1 Format format_
83  * 121 1 Layout layout_
84  * 122 1 bool is_owner_
85  * 123 1 uint8_t reserved_buf_
86  * 124 4 uint32_t reserved_buf2_
87  * ------ ---- --------------------------
88  * Total 128 bytes (alignas 8)
89  *
90  * Wire envelope:
91  * [ magic_begin (4) | version (4) | AudioFrame struct (128) | audio bytes (size_) | magic_end (4) ]
92  * @endcode
93  *
94  * @par Reserved bytes
95  * @c reserved_buf_ and @c reserved_buf2_ are exposed through the @c get_reserved* helpers.
96  * They travel through the wire format and are deliberately preserved by
97  * @c clear() and the copy/move helpers. They MUST NOT be repurposed by
98  * application code: a future library revision may bind them to real fields,
99  * which would silently break peers that abused the slot.
100  *
101  * @par Example
102  * @code
103  * vlink::zerocopy::AudioFrame frame;
104  * frame.set_sample_rate(48000);
105  * frame.set_num_channels(2);
106  * frame.set_num_samples(960);
107  * frame.set_bit_depth(16);
108  * frame.set_format(vlink::zerocopy::AudioFrame::kFormatPcmS16);
109  * frame.set_layout(vlink::zerocopy::AudioFrame::kLayoutInterleaved);
110  * frame.set_codec("PCM");
111  * frame.create(960 * 2 * sizeof(int16_t));
112  *
113  * vlink::Bytes wire;
114  * frame >> wire;
115  * @endcode
116  */
117 
118 #pragma once
119 
120 #include <cstdint>
121 #include <string_view>
122 
123 #include "../base/bytes.h"
124 #include "./header.h"
125 
126 namespace vlink {
127 
128 namespace zerocopy {
129 
130 /**
131  * @struct AudioFrame
132  * @brief 128-byte POD container holding one audio packet with full sample-format metadata.
133  *
134  * @details
135  * The struct size is locked at 128 bytes on 64-bit targets via @c static_assert;
136  * 32-bit toolchains emit a compile-time warning. The struct embeds a @c Header
137  * prefix and exposes both PCM-specific (sample rate, channels, bit depth) and
138  * codec-specific (codec name, bitrate) metadata.
139  */
140 struct VLINK_EXPORT_AND_ALIGNED(8) AudioFrame final {
141  /**
142  * @brief Encoded sample format of the audio payload.
143  */
144  enum Format : uint8_t {
145  kFormatUnknown = 0, ///< Uninitialised / unspecified format.
146 
147  kFormatPcmS16 = 1, ///< Signed 16-bit linear PCM.
148  kFormatPcmS24 = 2, ///< Signed 24-bit linear PCM packed into 3 bytes.
149  kFormatPcmS32 = 3, ///< Signed 32-bit linear PCM.
150  kFormatPcmF32 = 4, ///< IEEE-754 single-precision linear PCM.
151  kFormatPcmU8 = 5, ///< Unsigned 8-bit linear PCM.
152 
153  kFormatOpus = 100, ///< Opus codec payload.
154  kFormatAac = 101, ///< AAC codec payload.
155  kFormatMp3 = 102, ///< MP3 codec payload.
156  kFormatFlac = 103, ///< FLAC codec payload.
157  };
158 
159  /**
160  * @brief Arrangement of multi-channel samples within the payload buffer.
161  */
162  enum Layout : uint8_t {
163  kLayoutUnknown = 0, ///< Uninitialised / unspecified layout.
164  kLayoutInterleaved = 1, ///< Channels interleaved per-sample (L R L R ...).
165  kLayoutPlanar = 2, ///< Channels stored in consecutive contiguous planes.
166  };
167 
168  /**
169  * @brief Default-constructs an empty frame and asserts the 128-byte contract.
170  */
171  AudioFrame() noexcept;
172 
173  /**
174  * @brief Frees the owned audio buffer when @c is_owner() is @c true.
175  */
176  ~AudioFrame() noexcept;
177 
178  /**
179  * @brief Deep-copies @p target into a freshly allocated frame.
180  *
181  * @param target Source frame to clone.
182  */
183  AudioFrame(const AudioFrame& target) noexcept;
184 
185  /**
186  * @brief Steals @p target's allocation and metadata; @p target ends empty.
187  *
188  * @param target Source frame moved from.
189  */
190  AudioFrame(AudioFrame&& target) noexcept;
191 
192  /**
193  * @brief Deep-copy-assigns @p target; self-assignment is a no-op.
194  *
195  * @param target Source frame to clone.
196  * @return Reference to @c *this.
197  */
198  AudioFrame& operator=(const AudioFrame& target) noexcept;
199 
200  /**
201  * @brief Move-assigns @p target; self-assignment is a no-op.
202  *
203  * @param target Source frame moved from.
204  * @return Reference to @c *this.
205  */
206  AudioFrame& operator=(AudioFrame&& target) noexcept;
207 
208  /**
209  * @brief Deserialises an @c AudioFrame from @p bytes with zero-copy borrowing semantics.
210  *
211  * @param bytes Wire buffer previously produced by @c operator>>.
212  * @return @c true on success; @c false on magic mismatch or size mismatch.
213  */
214  bool operator<<(const Bytes& bytes) noexcept;
215 
216  /**
217  * @brief Serialises the struct snapshot plus audio bytes into @p bytes.
218  *
219  * @param bytes Output buffer; resized automatically when too small.
220  * @return Always @c true.
221  */
222  bool operator>>(Bytes& bytes) const noexcept;
223 
224  /**
225  * @brief Validates that @p bytes carries a well-formed @c AudioFrame envelope.
226  *
227  * @param bytes Wire buffer to inspect.
228  * @return @c true when the magic sentinels match and the minimum length holds.
229  */
230  [[nodiscard]] static bool check_valid(const Bytes& bytes) noexcept;
231 
232  /**
233  * @brief Total bytes that @c operator>> would write for this frame.
234  *
235  * @return @c sizeof(magic_begin) + @c sizeof(version) + @c sizeof(AudioFrame) + @c size() + @c sizeof(magic_end).
236  */
237  [[nodiscard]] size_t get_serialized_size() const noexcept;
238 
239  /**
240  * @brief Whether the audio buffer pointer is non-null and its size is positive.
241  *
242  * @return @c true when the frame holds usable audio data.
243  */
244  [[nodiscard]] bool is_valid() const noexcept;
245 
246  /**
247  * @brief Borrows @p target's audio buffer without copying.
248  *
249  * @param target Source frame whose buffer must outlive @c *this.
250  * @return @c false on self-borrow, otherwise @c true.
251  */
252  bool shallow_copy(const AudioFrame& target) noexcept;
253 
254  /**
255  * @brief Allocates (or reuses) an owned buffer and copies @p target's audio.
256  *
257  * @param target Source frame to clone.
258  * @return @c false on self-copy, otherwise @c true.
259  */
260  bool deep_copy(const AudioFrame& target) noexcept;
261 
262  /**
263  * @brief Transfers ownership from @p target; @p target ends empty.
264  *
265  * @param target Source frame moved from.
266  * @return @c false on self-move, otherwise @c true.
267  */
268  bool move_copy(AudioFrame& target) noexcept;
269 
270  /**
271  * @brief Allocates an uninitialised owned audio buffer of @p size bytes.
272  *
273  * @param size Byte count; must be non-zero.
274  * @return @c false when @p size is zero, otherwise @c true.
275  */
276  bool create(size_t size) noexcept;
277 
278  /**
279  * @brief Releases the owned buffer (if any) and resets metadata except reserved fields.
280  */
281  void clear() noexcept;
282 
283  /**
284  * @brief Borrows an externally owned audio buffer without copying.
285  *
286  * @param data Non-null source pointer that must outlive @c *this.
287  * @param size Buffer length in bytes; must be non-zero.
288  * @return @c false on invalid arguments or unchanged pointer, otherwise @c true.
289  */
290  bool shallow_copy(uint8_t* data, size_t size) noexcept;
291 
292  /**
293  * @brief Copies @p size bytes from @p data into an owned buffer.
294  *
295  * @param data Non-null source pointer.
296  * @param size Number of bytes to copy; must be non-zero.
297  * @return @c false on invalid arguments or aliasing, otherwise @c true.
298  */
299  bool deep_copy(uint8_t* data, size_t size) noexcept;
300 
301  /**
302  * @brief Compatibility alias for @c deep_copy(uint8_t*, size_t).
303  *
304  * @param data Source pointer.
305  * @param size Number of bytes.
306  * @return Result of the delegated @c deep_copy call.
307  */
308  bool fill_data(uint8_t* data, size_t size) noexcept;
309 
310  /**
311  * @brief Capture-side timestamp in nanoseconds.
312  *
313  * @return Stored value.
314  */
315  [[nodiscard]] uint64_t update_time_ns() const noexcept;
316 
317  /**
318  * @brief Advertised frame duration in nanoseconds.
319  *
320  * @return Stored value.
321  */
322  [[nodiscard]] uint64_t duration_ns() const noexcept;
323 
324  /**
325  * @brief Codec hint string (e.g. @c "PCM", @c "OPUS").
326  *
327  * @return Non-owning view into the embedded buffer.
328  */
329  [[nodiscard]] std::string_view codec() const noexcept;
330 
331  /**
332  * @brief Language tag used by speech-to-text consumers (e.g. @c "en", @c "zh").
333  *
334  * @return Non-owning view into the embedded buffer.
335  */
336  [[nodiscard]] std::string_view language() const noexcept;
337 
338  /**
339  * @brief Microphone / sensor channel identifier.
340  *
341  * @return Stored value.
342  */
343  [[nodiscard]] uint32_t channel() const noexcept;
344 
345  /**
346  * @brief Nominal publish frequency in Hz.
347  *
348  * @return Stored value.
349  */
350  [[nodiscard]] uint32_t freq() const noexcept;
351 
352  /**
353  * @brief PCM sample rate in Hz (e.g. 48 000).
354  *
355  * @return Stored value.
356  */
357  [[nodiscard]] uint32_t sample_rate() const noexcept;
358 
359  /**
360  * @brief Number of samples per channel within the buffer.
361  *
362  * @return Stored value.
363  */
364  [[nodiscard]] uint32_t num_samples() const noexcept;
365 
366  /**
367  * @brief Compressed bitrate in bits per second (zero for uncompressed PCM).
368  *
369  * @return Stored value.
370  */
371  [[nodiscard]] uint32_t bitrate() const noexcept;
372 
373  /**
374  * @brief Channel count (1 = mono, 2 = stereo, ...).
375  *
376  * @return Stored value.
377  */
378  [[nodiscard]] uint16_t num_channels() const noexcept;
379 
380  /**
381  * @brief Bit depth per PCM sample (16, 24, 32, ...).
382  *
383  * @return Stored value.
384  */
385  [[nodiscard]] uint16_t bit_depth() const noexcept;
386 
387  /**
388  * @brief Encoded sample format tag.
389  *
390  * @return @c Format enum value.
391  */
392  [[nodiscard]] Format format() const noexcept;
393 
394  /**
395  * @brief Multi-channel layout tag.
396  *
397  * @return @c Layout enum value.
398  */
399  [[nodiscard]] Layout layout() const noexcept;
400 
401  /**
402  * @brief Read-only pointer to the audio bytes.
403  *
404  * @return Pointer to payload start.
405  */
406  [[nodiscard]] const uint8_t* data() const noexcept;
407 
408  /**
409  * @brief Audio buffer size in bytes.
410  *
411  * @return Byte count.
412  */
413  [[nodiscard]] size_t size() const noexcept;
414 
415  /**
416  * @brief Whether this frame owns its audio buffer.
417  *
418  * @return @c true when the destructor would free the buffer.
419  */
420  [[nodiscard]] bool is_owner() const noexcept;
421 
422  /**
423  * @brief Stores the capture-side timestamp.
424  *
425  * @param update_time_ns Timestamp in nanoseconds.
426  */
427  void set_update_time_ns(uint64_t update_time_ns) noexcept;
428 
429  /**
430  * @brief Stores the advertised frame duration.
431  *
432  * @param duration_ns Duration in nanoseconds.
433  */
434  void set_duration_ns(uint64_t duration_ns) noexcept;
435 
436  /**
437  * @brief Stores the codec hint, truncated to @c sizeof(codec) - 1 bytes.
438  *
439  * @param codec Codec label.
440  */
441  void set_codec(std::string_view codec) noexcept;
442 
443  /**
444  * @brief Stores the language tag, truncated to @c sizeof(language) - 1 bytes.
445  *
446  * @param language Language identifier.
447  */
448  void set_language(std::string_view language) noexcept;
449 
450  /**
451  * @brief Stores the microphone / sensor channel identifier.
452  *
453  * @param channel Channel id.
454  */
455  void set_channel(uint32_t channel) noexcept;
456 
457  /**
458  * @brief Stores the nominal publish frequency.
459  *
460  * @param freq Frequency in Hz.
461  */
462  void set_freq(uint32_t freq) noexcept;
463 
464  /**
465  * @brief Stores the PCM sample rate.
466  *
467  * @param sample_rate Sample rate in Hz.
468  */
469  void set_sample_rate(uint32_t sample_rate) noexcept;
470 
471  /**
472  * @brief Stores the number of samples per channel.
473  *
474  * @param num_samples Sample count.
475  */
476  void set_num_samples(uint32_t num_samples) noexcept;
477 
478  /**
479  * @brief Stores the compressed bitrate.
480  *
481  * @param bitrate Bitrate in bits per second.
482  */
483  void set_bitrate(uint32_t bitrate) noexcept;
484 
485  /**
486  * @brief Stores the channel count.
487  *
488  * @param num_channels Channel count.
489  */
490  void set_num_channels(uint16_t num_channels) noexcept;
491 
492  /**
493  * @brief Stores the PCM bit depth.
494  *
495  * @param bit_depth Bit depth.
496  */
497  void set_bit_depth(uint16_t bit_depth) noexcept;
498 
499  /**
500  * @brief Stores the sample format tag.
501  *
502  * @param format @c Format enum value.
503  */
504  void set_format(Format format) noexcept;
505 
506  /**
507  * @brief Stores the channel layout tag.
508  *
509  * @param layout @c Layout enum value.
510  */
511  void set_layout(Layout layout) noexcept;
512 
513  /**
514  * @brief Mutable accessor for the first wire-locked reserved field (@c uint8_t).
515  *
516  * @return Reference to @c reserved_buf_.
517  */
518  uint8_t& get_reserved() noexcept { return reserved_buf_; }
519 
520  /**
521  * @brief Mutable accessor for the second wire-locked reserved field (@c uint32_t).
522  *
523  * @return Reference to @c reserved_buf2_.
524  */
525  uint32_t& get_reserved2() noexcept { return reserved_buf2_; }
526 
527  Header header; ///< Sequencing and timestamp metadata prefix.
528 
529  static constexpr bool kZerocopyTypes{true}; ///< Marker probed by the VLink type-trait machinery.
530 
531  private:
532  uint8_t* data_{nullptr};
533  size_t size_{0};
534  uint64_t update_time_ns_{0};
535  uint64_t duration_ns_{0};
536  char codec_[16]{0};
537  char language_[8]{0};
538  uint32_t channel_{0};
539  uint32_t freq_{0};
540  uint32_t sample_rate_{0};
541  uint32_t num_samples_{0};
542  uint32_t bitrate_{0};
543  uint16_t num_channels_{0};
544  uint16_t bit_depth_{0};
545  Format format_{kFormatUnknown};
546  Layout layout_{kLayoutUnknown};
547  bool is_owner_{false};
548  uint8_t reserved_buf_{0};
549  uint32_t reserved_buf2_{0};
550 
551  static constexpr uint32_t kMagicNumberBegin{0x98B7F1AA};
552  static constexpr uint32_t kMagicNumberEnd{0x98B7F1AF};
553 };
554 
555 } // namespace zerocopy
556 
557 } // namespace vlink
Fixed-size timestamp and sequencing prefix embedded by every VLink zero-copy container.
128-byte POD container holding one audio packet with full sample-format metadata.
40-byte timestamp / sequencing metadata prefix shared by all zero-copy containers.