ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
opus.c
Go to the documentation of this file.
1
8#include <ascii-chat/common.h>
11#include <opus/opus.h>
12#include <stdlib.h>
13#include <string.h>
14
15/* ============================================================================
16 * Encoder Creation
17 * ============================================================================ */
18
19opus_codec_t *opus_codec_create_encoder(opus_application_t application, int sample_rate, int bitrate) {
20 if (sample_rate <= 0 || bitrate <= 0) {
21 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid codec parameters: sample_rate=%d, bitrate=%d", sample_rate, bitrate);
22 return NULL;
23 }
24
26 if (!codec) {
27 SET_ERRNO(ERROR_MEMORY, "Failed to allocate opus codec context");
28 return NULL;
29 }
30
31 // Create encoder
32 int error = OPUS_OK;
33 codec->encoder = opus_encoder_create(sample_rate, 1, (int)application, &error);
34 if (error != OPUS_OK || !codec->encoder) {
35 SET_ERRNO(ERROR_AUDIO, "Failed to create Opus encoder: %s", opus_strerror(error));
36 SAFE_FREE(codec);
37 return NULL;
38 }
39
40 // Set bitrate
41 error = opus_encoder_ctl(codec->encoder, OPUS_SET_BITRATE(bitrate));
42 if (error != OPUS_OK) {
43 SET_ERRNO(ERROR_AUDIO, "Failed to set Opus bitrate: %s", opus_strerror(error));
44 opus_encoder_destroy(codec->encoder);
45 SAFE_FREE(codec);
46 return NULL;
47 }
48
49 // Enable DTX (Discontinuous Transmission) for efficient silence frame encoding
50 // DTX allows the encoder to produce very small packets (3-4 bytes) for silence,
51 // reducing bandwidth usage while maintaining audio quality for voice/music
52 error = opus_encoder_ctl(codec->encoder, OPUS_SET_DTX(1));
53 if (error != OPUS_OK) {
54 log_warn("Failed to enable Opus DTX: %s (continuing without DTX)", opus_strerror(error));
55 }
56
57 codec->decoder = NULL;
58 codec->sample_rate = sample_rate;
59 codec->bitrate = bitrate;
60 codec->tmp_buffer = NULL;
61
62 log_debug("Opus encoder created: sample_rate=%d, bitrate=%d bps, DTX enabled", sample_rate, bitrate);
63
64 // Register Opus encoder for debugging
65 NAMED_REGISTER_CONTEXT(codec, "opus_encoder", "opus_codec", NULL);
66
67 return codec;
68}
69
70/* ============================================================================
71 * Decoder Creation
72 * ============================================================================ */
73
75 if (sample_rate <= 0) {
76 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid sample rate: %d", sample_rate);
77 return NULL;
78 }
79
81 if (!codec) {
82 SET_ERRNO(ERROR_MEMORY, "Failed to allocate opus codec context");
83 return NULL;
84 }
85
86 // Create decoder
87 int error = OPUS_OK;
88 codec->decoder = opus_decoder_create(sample_rate, 1, &error);
89 if (error != OPUS_OK || !codec->decoder) {
90 SET_ERRNO(ERROR_AUDIO, "Failed to create Opus decoder: %s", opus_strerror(error));
91 SAFE_FREE(codec);
92 return NULL;
93 }
94
95 codec->encoder = NULL;
96 codec->sample_rate = sample_rate;
97 codec->bitrate = 0; // N/A for decoder
98 codec->tmp_buffer = NULL;
99
100 log_debug("Opus decoder created: sample_rate=%d", sample_rate);
101
102 // Register Opus decoder for debugging
103 NAMED_REGISTER_CONTEXT(codec, "opus_decoder", "opus_codec", NULL);
104
105 return codec;
106}
107
108/* ============================================================================
109 * Encoding
110 * ============================================================================ */
111
112size_t opus_codec_encode(opus_codec_t *codec, const float *samples, int num_samples, uint8_t *out_data,
113 size_t out_size) {
114 if (!codec || !codec->encoder || !samples || num_samples <= 0 || !out_data || out_size == 0) {
116 "Invalid encode parameters: codec=%p, encoder=%p, samples=%p, num_samples=%d, out_data=%p, "
117 "out_size=%zu",
118 (void *)codec, (void *)(codec ? codec->encoder : NULL), (const void *)samples, num_samples,
119 (void *)out_data, out_size);
120 return 0;
121 }
122
123 // Encode frame
124 opus_int32 encoded_bytes = opus_encode_float(codec->encoder, samples, num_samples, out_data, (opus_int32)out_size);
125
126 if (encoded_bytes < 0) {
127 SET_ERRNO(ERROR_AUDIO, "Opus encoding failed: %s", opus_strerror((int)encoded_bytes));
128 return 0;
129 }
130
131 if (encoded_bytes == 0) {
132 // DTX frame (silence) - encoder produced zero bytes
133 log_debug_every(LOG_RATE_VERY_FAST, "Opus DTX frame (silence detected)");
134 }
135
136 return (size_t)encoded_bytes;
137}
138
139/* ============================================================================
140 * Decoding
141 * ============================================================================ */
142
143int opus_codec_decode(opus_codec_t *codec, const uint8_t *data, size_t data_len, float *out_samples,
144 int out_num_samples) {
145 if (!codec || !codec->decoder || !out_samples || out_num_samples <= 0) {
147 "Invalid decode parameters: codec=%p, decoder=%p, out_samples=%p, out_num_samples=%d", (void *)codec,
148 (void *)(codec ? codec->decoder : NULL), (void *)out_samples, out_num_samples);
149 return -1;
150 }
151
152 // If data is NULL, use PLC (Packet Loss Concealment)
153 if (!data || data_len == 0) {
154 log_debug_every(LOG_RATE_VERY_FAST, "Opus PLC (Packet Loss Concealment)");
155 int samples = opus_decode_float(codec->decoder, NULL, 0, out_samples, out_num_samples, 0);
156 if (samples < 0) {
157 SET_ERRNO(ERROR_AUDIO, "Opus PLC failed: %s", opus_strerror(samples));
158 return -1;
159 }
160 return samples;
161 }
162
163 // Decode frame
164 int samples = opus_decode_float(codec->decoder, data, (opus_int32)data_len, out_samples, out_num_samples, 0);
165
166 if (samples < 0) {
167 SET_ERRNO(ERROR_AUDIO, "Opus decoding failed: %s", opus_strerror(samples));
168 return -1;
169 }
170
171 return samples;
172}
173
174/* ============================================================================
175 * Configuration
176 * ============================================================================ */
177
179 if (!codec || !codec->encoder || bitrate <= 0) {
180 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid bitrate parameters: codec=%p, encoder=%p, bitrate=%d", (void *)codec,
181 (void *)(codec ? codec->encoder : NULL), bitrate);
182 }
183
184 int error = opus_encoder_ctl(codec->encoder, OPUS_SET_BITRATE(bitrate));
185 if (error != OPUS_OK) {
186 return SET_ERRNO(ERROR_AUDIO, "Failed to set Opus bitrate: %s", opus_strerror(error));
187 }
188
189 codec->bitrate = bitrate;
190 log_debug("Opus bitrate changed to %d bps", bitrate);
191
192 return ASCIICHAT_OK;
193}
194
196 if (!codec || !codec->encoder) {
197 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid codec for bitrate query");
198 return -1;
199 }
200
201 opus_int32 bitrate = 0;
202 int error = opus_encoder_ctl(codec->encoder, OPUS_GET_BITRATE(&bitrate));
203 if (error != OPUS_OK) {
204 SET_ERRNO(ERROR_AUDIO, "Failed to get Opus bitrate: %s", opus_strerror(error));
205 return -1;
206 }
207
208 return (int)bitrate;
209}
210
212 if (!codec || !codec->encoder) {
213 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid codec for DTX configuration");
214 }
215
216 int error = opus_encoder_ctl(codec->encoder, OPUS_SET_DTX(enable ? 1 : 0));
217 if (error != OPUS_OK) {
218 return SET_ERRNO(ERROR_AUDIO, "Failed to set Opus DTX: %s", opus_strerror(error));
219 }
220
221 log_debug("Opus DTX %s", enable ? "enabled" : "disabled");
222
223 return ASCIICHAT_OK;
224}
225
226/* ============================================================================
227 * Lifecycle
228 * ============================================================================ */
229
231 if (!codec) {
232 return;
233 }
234
235 NAMED_UNREGISTER(codec);
236
237 if (codec->encoder) {
238 opus_encoder_destroy(codec->encoder);
239 codec->encoder = NULL;
240 }
241
242 if (codec->decoder) {
243 opus_decoder_destroy(codec->decoder);
244 codec->decoder = NULL;
245 }
246
247 if (codec->tmp_buffer) {
248 SAFE_FREE(codec->tmp_buffer);
249 codec->tmp_buffer = NULL;
250 }
251
252 SAFE_FREE(codec);
253}
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
⚙️ Common definitions, error codes, macros, and types shared throughout the application
Named object registry for debugging — log identifiable resource names.
opus_codec_t * opus_codec_create_decoder(int sample_rate)
Create an Opus decoder.
Definition opus.c:74
asciichat_error_t opus_codec_set_bitrate(opus_codec_t *codec, int bitrate)
Set encoder bitrate.
Definition opus.c:178
opus_application_t
Application mode for opus encoder.
Definition opus.h:76
int opus_codec_decode(opus_codec_t *codec, const uint8_t *data, size_t data_len, float *out_samples, int out_num_samples)
Decode Opus audio frame.
Definition opus.c:143
opus_codec_t * opus_codec_create_encoder(opus_application_t application, int sample_rate, int bitrate)
Create an Opus encoder.
Definition opus.c:19
int opus_codec_get_bitrate(opus_codec_t *codec)
Get current encoder bitrate.
Definition opus.c:195
asciichat_error_t opus_codec_set_dtx(opus_codec_t *codec, int enable)
Enable/disable DTX (Discontinuous Transmission)
Definition opus.c:211
void opus_codec_destroy(opus_codec_t *codec)
Destroy an Opus codec instance.
Definition opus.c:230
size_t opus_codec_encode(opus_codec_t *codec, const float *samples, int num_samples, uint8_t *out_data, size_t out_size)
Encode audio frame with Opus.
Definition opus.c:112
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
unsigned char uint8_t
Definition common.h:56
#define NAMED_REGISTER_CONTEXT(context, context_type, name, parent_ptr)
Register a generic context with automatic format specifier.
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_AUDIO
Definition error_codes.h:67
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define LOG_RATE_VERY_FAST
Log rate limit: 0.1 seconds (100,000 microseconds) - for very high frequency operations.
Definition log_rates.h:23
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:702
Opus audio codec wrapper for real-time encoding/decoding.
Opus codec context for encoding or decoding.
Definition opus.h:95
uint8_t * tmp_buffer
Temporary buffer for internal use.
Definition opus.h:100
int bitrate
Bitrate in bits per second (encoder only)
Definition opus.h:99
int sample_rate
Sample rate in Hz (e.g., 44100)
Definition opus.h:98
OpusEncoder * encoder
Encoder state (NULL if decoder)
Definition opus.h:96
OpusDecoder * decoder
Decoder state (NULL if encoder)
Definition opus.h:97