libaaruformat 1.0 Aaru Data Preservation Suite - Format Library

Loading...
Searching...
No Matches
flac.c
Go to the documentation of this file.
1/*
2 * This file is part of the Aaru Data Preservation Suite.
3 * Copyright (c) 2019-2025 Natalia Portillo.
4 *
5 * This library is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as
7 * published by the Free Software Foundation; either version 2.1 of the
8 * License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <stdint.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include "aaruformat.h"
24
25#include "flac.h"
26#include "FLAC/metadata.h"
27#include "FLAC/stream_decoder.h"
28#include "FLAC/stream_encoder.h"
29
30 static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[],
31 size_t *bytes, void *client_data);
32 static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
33 const FLAC__int32 *const buffer[], void *client_data);
34 static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status,
35 void *client_data);
36
48 AARU_EXPORT size_t AARU_CALL aaruf_flac_decode_redbook_buffer(uint8_t *dst_buffer, size_t dst_size,
49 const uint8_t *src_buffer, size_t src_size)
50{
51 FLAC__StreamDecoder *decoder = NULL;
52 FLAC__StreamDecoderInitStatus init_status = FLAC__STREAM_DECODER_INIT_STATUS_OK;
53 aaru_flac_ctx *ctx = (aaru_flac_ctx *)malloc(sizeof(aaru_flac_ctx));
54 size_t ret_size = 0;
55
56 memset(ctx, 0, sizeof(aaru_flac_ctx));
57
58 ctx->src_buffer = src_buffer;
59 ctx->src_len = src_size;
60 ctx->src_pos = 0;
61 ctx->dst_buffer = dst_buffer;
62 ctx->dst_len = dst_size;
63 ctx->dst_pos = 0;
64 ctx->error = 0;
65
66 decoder = FLAC__stream_decoder_new();
67
68 if(!decoder)
69 {
70 free(ctx);
71 return -1;
72 }
73
74 FLAC__stream_decoder_set_md5_checking(decoder, false);
75
76 init_status = FLAC__stream_decoder_init_stream(decoder, read_callback, NULL, NULL, NULL, NULL, write_callback, NULL,
77 error_callback, ctx);
78
79 if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
80 {
81 free(ctx);
82 return -1;
83 }
84
85 // TODO: Return error somehow
86 FLAC__stream_decoder_process_until_end_of_stream(decoder);
87
88 FLAC__stream_decoder_delete(decoder);
89
90 ret_size = ctx->dst_pos;
91
92 free(ctx);
93
94 return ret_size;
95}
96
97 static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[],
98 size_t *bytes, void *client_data)
99{
100 aaru_flac_ctx *ctx = (aaru_flac_ctx *)client_data;
101
102 if(ctx->src_len - ctx->src_pos < *bytes) *bytes = ctx->src_len - ctx->src_pos;
103
104 if(*bytes == 0) return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
105
106 memcpy(buffer, ctx->src_buffer + ctx->src_pos, *bytes);
107 ctx->src_pos += *bytes;
108
109 return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
110}
111
112 static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
113 const FLAC__int32 *const buffer[], void *client_data)
114{
115 aaru_flac_ctx *ctx = (aaru_flac_ctx *)client_data;
116 uint16_t *buffer16 = (uint16_t *)(ctx->dst_buffer + ctx->dst_pos);
117
118 // Why FLAC does not interleave the channels as PCM do, oh the mistery, we could use memcpy instead of looping
119 for(size_t i = 0; i < frame->header.blocksize && ctx->dst_pos < ctx->dst_len; i++)
120 {
121 // Left channel
122 *(buffer16++) = (FLAC__int16)buffer[0][i];
123 // Right channel
124 *(buffer16++) = (FLAC__int16)buffer[1][i];
125
126 ctx->dst_pos += 4;
127
128 /* TODO: Big-endian (use bswap?)
129 // Left channel
130 ctx->dst_buffer[ctx->dst_pos++] = (FLAC__uint16)(FLAC__int16)buffer[0][i];
131 ctx->dst_buffer[ctx->dst_pos++] = (FLAC__uint16)(FLAC__int16)buffer[0][i] >> 8;
132 // Right channel
133 ctx->dst_buffer[ctx->dst_pos++] = (FLAC__uint16)(FLAC__int16)buffer[1][i];
134 ctx->dst_buffer[ctx->dst_pos++] = (FLAC__uint16)(FLAC__int16)buffer[1][i] >> 8;
135 */
136 }
137
138 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
139}
140
141 static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
142{
143 aaru_flac_ctx *ctx = (aaru_flac_ctx *)client_data;
144
145 fprintf(stderr, "Got error callback: %s\n", FLAC__StreamDecoderErrorStatusString[status]);
146
147 ctx->error = 1;
148}
149
150 static FLAC__StreamEncoderWriteStatus encoder_write_callback(const FLAC__StreamEncoder *encoder,
151 const FLAC__byte buffer[], size_t bytes, uint32_t samples,
152 uint32_t current_frame, void *client_data);
153
175 AARU_EXPORT size_t AARU_CALL aaruf_flac_encode_redbook_buffer(
176 uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size, uint32_t blocksize,
177 int32_t do_mid_side_stereo, int32_t loose_mid_side_stereo, const char *apodization, uint32_t max_lpc_order,
178 uint32_t qlp_coeff_precision, int32_t do_qlp_coeff_prec_search, int32_t do_exhaustive_model_search,
179 uint32_t min_residual_partition_order, uint32_t max_residual_partition_order, const char *application_id,
180 uint32_t application_id_len)
181{
182 FLAC__StreamEncoder *encoder = NULL;
183 aaru_flac_ctx *ctx = (aaru_flac_ctx *)malloc(sizeof(aaru_flac_ctx));
184 FLAC__StreamEncoderInitStatus init_status = FLAC__STREAM_ENCODER_INIT_STATUS_OK;
185 size_t ret_size = 0;
186 FLAC__int32 *pcm = NULL;
187 int i = 0;
188 int16_t *buffer16 = (int16_t *)src_buffer;
189 FLAC__StreamMetadata *metadata[1];
190
191 memset(ctx, 0, sizeof(aaru_flac_ctx));
192
193 ctx->src_buffer = src_buffer;
194 ctx->src_len = src_size;
195 ctx->src_pos = 0;
196 ctx->dst_buffer = dst_buffer;
197 ctx->dst_len = dst_size;
198 ctx->dst_pos = 0;
199 ctx->error = 0;
200
201 encoder = FLAC__stream_encoder_new();
202
203 if(!encoder)
204 {
205 free(ctx);
206 return -1;
207 }
208
209 // TODO: Error detection here
210 FLAC__stream_encoder_set_verify(encoder, false);
211 FLAC__stream_encoder_set_streamable_subset(encoder, false);
212 FLAC__stream_encoder_set_channels(encoder, 2);
213 FLAC__stream_encoder_set_bits_per_sample(encoder, 16);
214 FLAC__stream_encoder_set_sample_rate(encoder, 44100);
215 FLAC__stream_encoder_set_blocksize(encoder, blocksize);
216 // true compresses more
217 FLAC__stream_encoder_set_do_mid_side_stereo(encoder, do_mid_side_stereo);
218 // false compresses more
219 FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, loose_mid_side_stereo);
220 // Apodization
221 FLAC__stream_encoder_set_apodization(encoder, apodization);
222 FLAC__stream_encoder_set_max_lpc_order(encoder, max_lpc_order);
223 FLAC__stream_encoder_set_qlp_coeff_precision(encoder, qlp_coeff_precision);
224 FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder, do_qlp_coeff_prec_search);
225 FLAC__stream_encoder_set_do_exhaustive_model_search(encoder, do_exhaustive_model_search);
226 FLAC__stream_encoder_set_min_residual_partition_order(encoder, min_residual_partition_order);
227 FLAC__stream_encoder_set_max_residual_partition_order(encoder, max_residual_partition_order);
228 FLAC__stream_encoder_set_total_samples_estimate(encoder, src_size / 4);
229
230 /* TODO: This is ignored by FLAC, need to replace it
231 if((metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT)) != NULL)
232 {
233 memset(&vorbis_entry, 0, sizeof(FLAC__StreamMetadata_VorbisComment_Entry));
234 vorbis_entry.entry = (unsigned char *)"Aaru.Compression.Native";
235 vorbis_entry.length = strlen("Aaru.Compression.Native");
236
237 FLAC__metadata_object_vorbiscomment_set_vendor_string(metadata[0], vorbis_entry, true);
238 }
239 */
240
241 if(application_id_len > 0 && application_id != NULL)
242 if((metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION)) != NULL)
243 FLAC__metadata_object_application_set_data(metadata[0], (unsigned char *)application_id, application_id_len,
244 true);
245
246 FLAC__stream_encoder_set_metadata(encoder, metadata, 1);
247
248 init_status = FLAC__stream_encoder_init_stream(encoder, encoder_write_callback, NULL, NULL, NULL, ctx);
249
250 if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
251 {
252 free(ctx);
253 return -1;
254 }
255
256 pcm = malloc((src_size / 2) * sizeof(FLAC__int32));
257
258 for(i = 0; i < src_size / 2; i++) pcm[i] = (FLAC__int32) * (buffer16++);
259
260 FLAC__stream_encoder_process_interleaved(encoder, pcm, src_size / 4);
261
262 FLAC__stream_encoder_finish(encoder);
263
264 FLAC__stream_encoder_delete(encoder);
265
266 ret_size = ctx->dst_pos;
267
268 free(ctx);
269 free(pcm);
270 FLAC__metadata_object_delete(metadata[0]);
271
272 return ret_size;
273}
274
275 static FLAC__StreamEncoderWriteStatus encoder_write_callback(const FLAC__StreamEncoder *encoder,
276 const FLAC__byte buffer[], size_t bytes, uint32_t samples,
277 uint32_t current_frame, void *client_data)
278{
279 aaru_flac_ctx *ctx = (aaru_flac_ctx *)client_data;
280
281 if(bytes > ctx->dst_len - ctx->dst_pos) bytes = ctx->dst_len - ctx->dst_pos;
282
283 memcpy(ctx->dst_buffer + ctx->dst_pos, buffer, bytes);
284
285 ctx->dst_pos += bytes;
286
287 return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
288}
aaruformat.h
AARU_CALL
#define AARU_CALL
Definition decls.h:45
AARU_EXPORT
#define AARU_EXPORT
Definition decls.h:54
aaruf_flac_encode_redbook_buffer
size_t aaruf_flac_encode_redbook_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size, uint32_t blocksize, int32_t do_mid_side_stereo, int32_t loose_mid_side_stereo, const char *apodization, uint32_t max_lpc_order, uint32_t qlp_coeff_precision, int32_t do_qlp_coeff_prec_search, int32_t do_exhaustive_model_search, uint32_t min_residual_partition_order, uint32_t max_residual_partition_order, const char *application_id, uint32_t application_id_len)
Encodes a Red Book audio buffer to FLAC format.
Definition flac.c:175
read_callback
static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
Definition flac.c:97
encoder_write_callback
static FLAC__StreamEncoderWriteStatus encoder_write_callback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t current_frame, void *client_data)
Definition flac.c:275
write_callback
static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
Definition flac.c:112
error_callback
static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
Definition flac.c:141
aaruf_flac_decode_redbook_buffer
size_t aaruf_flac_decode_redbook_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size)
Decodes a FLAC-compressed Red Book audio buffer.
Definition flac.c:48
flac.h
aaru_flac_ctx
Definition flac.h:23
aaru_flac_ctx::dst_pos
size_t dst_pos
Definition flac.h:29
aaru_flac_ctx::error
uint8_t error
Definition flac.h:30
aaru_flac_ctx::dst_len
size_t dst_len
Definition flac.h:28
aaru_flac_ctx::src_buffer
const uint8_t * src_buffer
Definition flac.h:24
aaru_flac_ctx::src_pos
size_t src_pos
Definition flac.h:26
aaru_flac_ctx::src_len
size_t src_len
Definition flac.h:25
aaru_flac_ctx::dst_buffer
uint8_t * dst_buffer
Definition flac.h:27