ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
opus_codec.c
Go to the documentation of this file.
1
7#include "audio/opus_codec.h"
8#include "common.h"
9#include "asciichat_errno.h"
10#include <opus/opus.h>
11#include <stdlib.h>
12#include <string.h>
13
14/* ============================================================================
15 * Encoder Creation
16 * ============================================================================ */
17
18opus_codec_t *opus_codec_create_encoder(opus_application_t application, int sample_rate, int bitrate) {
19 if (sample_rate <= 0 || bitrate <= 0) {
20 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid codec parameters: sample_rate=%d, bitrate=%d", sample_rate, bitrate);
21 return NULL;
22 }
23
25 if (!codec) {
26 SET_ERRNO(ERROR_MEMORY, "Failed to allocate opus codec context");
27 return NULL;
28 }
29
30 // Create encoder
31 int error = OPUS_OK;
32 codec->encoder = opus_encoder_create(sample_rate, 1, (int)application, &error);
33 if (error != OPUS_OK || !codec->encoder) {
34 SET_ERRNO(ERROR_AUDIO, "Failed to create Opus encoder: %s", opus_strerror(error));
35 SAFE_FREE(codec);
36 return NULL;
37 }
38
39 // Set bitrate
40 error = opus_encoder_ctl(codec->encoder, OPUS_SET_BITRATE(bitrate));
41 if (error != OPUS_OK) {
42 SET_ERRNO(ERROR_AUDIO, "Failed to set Opus bitrate: %s", opus_strerror(error));
43 opus_encoder_destroy(codec->encoder);
44 SAFE_FREE(codec);
45 return NULL;
46 }
47
48 codec->decoder = NULL;
49 codec->sample_rate = sample_rate;
50 codec->bitrate = bitrate;
51 codec->tmp_buffer = NULL;
52
53 log_debug("Opus encoder created: sample_rate=%d, bitrate=%d bps", sample_rate, bitrate);
54
55 return codec;
56}
57
58/* ============================================================================
59 * Decoder Creation
60 * ============================================================================ */
61
63 if (sample_rate <= 0) {
64 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid sample rate: %d", sample_rate);
65 return NULL;
66 }
67
69 if (!codec) {
70 SET_ERRNO(ERROR_MEMORY, "Failed to allocate opus codec context");
71 return NULL;
72 }
73
74 // Create decoder
75 int error = OPUS_OK;
76 codec->decoder = opus_decoder_create(sample_rate, 1, &error);
77 if (error != OPUS_OK || !codec->decoder) {
78 SET_ERRNO(ERROR_AUDIO, "Failed to create Opus decoder: %s", opus_strerror(error));
79 SAFE_FREE(codec);
80 return NULL;
81 }
82
83 codec->encoder = NULL;
84 codec->sample_rate = sample_rate;
85 codec->bitrate = 0; // N/A for decoder
86 codec->tmp_buffer = NULL;
87
88 log_debug("Opus decoder created: sample_rate=%d", sample_rate);
89
90 return codec;
91}
92
93/* ============================================================================
94 * Encoding
95 * ============================================================================ */
96
97size_t opus_codec_encode(opus_codec_t *codec, const float *samples, int num_samples, uint8_t *out_data,
98 size_t out_size) {
99 if (!codec || !codec->encoder || !samples || num_samples <= 0 || !out_data || out_size == 0) {
101 "Invalid encode parameters: codec=%p, encoder=%p, samples=%p, num_samples=%d, out_data=%p, "
102 "out_size=%zu",
103 (void *)codec, (void *)(codec ? codec->encoder : NULL), (const void *)samples, num_samples,
104 (void *)out_data, out_size);
105 return 0;
106 }
107
108 // Encode frame
109 opus_int32 encoded_bytes = opus_encode_float(codec->encoder, samples, num_samples, out_data, (opus_int32)out_size);
110
111 if (encoded_bytes < 0) {
112 SET_ERRNO(ERROR_AUDIO, "Opus encoding failed: %s", opus_strerror((int)encoded_bytes));
113 return 0;
114 }
115
116 if (encoded_bytes == 0) {
117 // DTX frame (silence) - encoder produced zero bytes
118 log_debug_every(LOG_RATE_VERY_FAST, "Opus DTX frame (silence detected)");
119 }
120
121 return (size_t)encoded_bytes;
122}
123
124/* ============================================================================
125 * Decoding
126 * ============================================================================ */
127
128int opus_codec_decode(opus_codec_t *codec, const uint8_t *data, size_t data_len, float *out_samples,
129 int out_num_samples) {
130 if (!codec || !codec->decoder || !out_samples || out_num_samples <= 0) {
132 "Invalid decode parameters: codec=%p, decoder=%p, out_samples=%p, out_num_samples=%d", (void *)codec,
133 (void *)(codec ? codec->decoder : NULL), (void *)out_samples, out_num_samples);
134 return -1;
135 }
136
137 // If data is NULL, use PLC (Packet Loss Concealment)
138 if (!data || data_len == 0) {
139 log_debug_every(LOG_RATE_VERY_FAST, "Opus PLC (Packet Loss Concealment)");
140 int samples = opus_decode_float(codec->decoder, NULL, 0, out_samples, out_num_samples, 0);
141 if (samples < 0) {
142 SET_ERRNO(ERROR_AUDIO, "Opus PLC failed: %s", opus_strerror(samples));
143 return -1;
144 }
145 return samples;
146 }
147
148 // Decode frame
149 int samples = opus_decode_float(codec->decoder, data, (opus_int32)data_len, out_samples, out_num_samples, 0);
150
151 if (samples < 0) {
152 SET_ERRNO(ERROR_AUDIO, "Opus decoding failed: %s", opus_strerror(samples));
153 return -1;
154 }
155
156 return samples;
157}
158
159/* ============================================================================
160 * Configuration
161 * ============================================================================ */
162
164 if (!codec || !codec->encoder || bitrate <= 0) {
165 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid bitrate parameters: codec=%p, encoder=%p, bitrate=%d", (void *)codec,
166 (void *)(codec ? codec->encoder : NULL), bitrate);
167 }
168
169 int error = opus_encoder_ctl(codec->encoder, OPUS_SET_BITRATE(bitrate));
170 if (error != OPUS_OK) {
171 return SET_ERRNO(ERROR_AUDIO, "Failed to set Opus bitrate: %s", opus_strerror(error));
172 }
173
174 codec->bitrate = bitrate;
175 log_debug("Opus bitrate changed to %d bps", bitrate);
176
177 return ASCIICHAT_OK;
178}
179
181 if (!codec || !codec->encoder) {
182 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid codec for bitrate query");
183 return -1;
184 }
185
186 opus_int32 bitrate = 0;
187 int error = opus_encoder_ctl(codec->encoder, OPUS_GET_BITRATE(&bitrate));
188 if (error != OPUS_OK) {
189 SET_ERRNO(ERROR_AUDIO, "Failed to get Opus bitrate: %s", opus_strerror(error));
190 return -1;
191 }
192
193 return (int)bitrate;
194}
195
197 if (!codec || !codec->encoder) {
198 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid codec for DTX configuration");
199 }
200
201 int error = opus_encoder_ctl(codec->encoder, OPUS_SET_DTX(enable ? 1 : 0));
202 if (error != OPUS_OK) {
203 return SET_ERRNO(ERROR_AUDIO, "Failed to set Opus DTX: %s", opus_strerror(error));
204 }
205
206 log_debug("Opus DTX %s", enable ? "enabled" : "disabled");
207
208 return ASCIICHAT_OK;
209}
210
211/* ============================================================================
212 * Lifecycle
213 * ============================================================================ */
214
216 if (!codec) {
217 return;
218 }
219
220 if (codec->encoder) {
221 opus_encoder_destroy(codec->encoder);
222 codec->encoder = NULL;
223 }
224
225 if (codec->decoder) {
226 opus_decoder_destroy(codec->decoder);
227 codec->decoder = NULL;
228 }
229
230 if (codec->tmp_buffer) {
231 SAFE_FREE(codec->tmp_buffer);
232 codec->tmp_buffer = NULL;
233 }
234
235 SAFE_FREE(codec);
236}
⚠️‼️ Error and/or exit() when things go bad.
opus_codec_t * opus_codec_create_decoder(int sample_rate)
Create an Opus decoder.
Definition opus_codec.c:62
asciichat_error_t opus_codec_set_bitrate(opus_codec_t *codec, int bitrate)
Set encoder bitrate.
Definition opus_codec.c:163
opus_application_t
Application mode for opus encoder.
Definition opus_codec.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_codec.c:128
opus_codec_t * opus_codec_create_encoder(opus_application_t application, int sample_rate, int bitrate)
Create an Opus encoder.
Definition opus_codec.c:18
int opus_codec_get_bitrate(opus_codec_t *codec)
Get current encoder bitrate.
Definition opus_codec.c:180
asciichat_error_t opus_codec_set_dtx(opus_codec_t *codec, int enable)
Enable/disable DTX (Discontinuous Transmission)
Definition opus_codec.c:196
void opus_codec_destroy(opus_codec_t *codec)
Destroy an Opus codec instance.
Definition opus_codec.c:215
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_codec.c:97
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_AUDIO
Definition error_codes.h:64
@ ERROR_MEMORY
Definition error_codes.h:53
@ ASCIICHAT_OK
Definition error_codes.h:48
@ 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_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
#define log_debug(...)
Log a DEBUG message.
Opus audio codec wrapper for real-time encoding/decoding.
Opus codec context for encoding or decoding.
Definition opus_codec.h:95
uint8_t * tmp_buffer
Temporary buffer for internal use.
Definition opus_codec.h:100
int bitrate
Bitrate in bits per second (encoder only)
Definition opus_codec.h:99
int sample_rate
Sample rate in Hz (e.g., 44100)
Definition opus_codec.h:98
OpusEncoder * encoder
Encoder state (NULL if decoder)
Definition opus_codec.h:96
OpusDecoder * decoder
Decoder state (NULL if encoder)
Definition opus_codec.h:97