xref: /btstack/test/lc3/lc3_encoder.c (revision e40ee29a889d72d91eb40220df189fbf1d624a43)
1 /*
2  * Copyright (C) 2022 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 // *****************************************************************************
39 //
40 // LC3 decoder EHIMA
41 //
42 // *****************************************************************************
43 
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 
51 #include "wav_util.h"
52 #include "btstack_util.h"
53 #include "btstack_debug.h"
54 
55 #include "btstack_lc3.h"
56 #include "btstack_lc3_google.h"
57 
58 #define MAX_NUM_CHANNELS 2
59 #define MAX_SAMPLES_PER_FRAME 480
60 
61 static uint8_t  write_buffer[200];
62 static int16_t  samples_buffer[MAX_SAMPLES_PER_FRAME + MAX_NUM_CHANNELS];
63 static int16_t  frame_buffer[MAX_SAMPLES_PER_FRAME];
64 
65 static uint32_t frame_count = 0;
66 
67 static void show_usage(const char * path){
68     printf("Usage: %s input.wav output.lc3 frame_duration_ms octets_per_frame\n", path);
69     printf("- frame_duration_ms: 7.5 or 10\n");
70     printf("- octects_per_frame: 26..155\n");
71     printf("\n\n");
72 }
73 
74 int main (int argc, const char * argv[]){
75     if (argc < 4){
76         show_usage(argv[0]);
77         return -1;
78     }
79 
80     uint8_t argv_pos = 1;
81     const char * wav_filename = argv[argv_pos++];
82     const char * lc3_filename = argv[argv_pos++];
83 
84     btstack_lc3_frame_duration_t frame_duration;
85     if (strcmp(argv[argv_pos], "10") == 0){
86         frame_duration = BTSTACK_LC3_FRAME_DURATION_10000US;
87     } else if (strcmp(argv[argv_pos], "7.5") == 0){
88         frame_duration = BTSTACK_LC3_FRAME_DURATION_7500US;
89     } else {
90         printf("Invalid frame duration %s, must be either 7.5 or 10\n", argv[2]);
91         return -10;
92     }
93     argv_pos++;
94 
95     uint16_t bytes_per_frame = atoi(argv[argv_pos++]);
96     if ((bytes_per_frame < 26) || (bytes_per_frame > 155)){
97         printf("Octets per Frame %u out of range [26..155]\n", bytes_per_frame);
98         return -10;
99     }
100 
101     int status = wav_reader_open(wav_filename);
102     if (status != 0){
103         printf("Could not open wav file %s\n", wav_filename);
104         return -10;
105     }
106 
107     // get wav config
108     uint32_t sampling_frequency_hz = wav_reader_get_sampling_rate();
109     uint8_t  num_channels = wav_reader_get_num_channels();
110 
111     // init decoder
112     uint8_t channel;
113     btstack_lc3_encoder_google_t encoder_contexts[MAX_NUM_CHANNELS];
114     const btstack_lc3_encoder_t * lc3_encoder;
115     for (channel = 0 ; channel < num_channels ; channel++){
116         btstack_lc3_encoder_google_t * encoder_context = &encoder_contexts[channel];
117         lc3_encoder = btstack_lc3_encoder_google_init_instance(encoder_context);
118         lc3_encoder->configure(encoder_context, sampling_frequency_hz, frame_duration);
119     }
120     uint32_t bitrate_per_channel = lc3_encoder->get_bitrate_for_number_of_octets(&encoder_contexts[0], bytes_per_frame);
121     uint32_t bitrate = bitrate_per_channel * num_channels;
122     uint16_t number_samples_per_frame = lc3_encoder->get_number_samples_per_frame(&encoder_contexts[0]);
123 
124     if (number_samples_per_frame > MAX_SAMPLES_PER_FRAME) return -10;
125 
126     // create lc3 file and write header for floating point implementation
127     FILE * lc3_file = fopen(lc3_filename, "wb");
128     if (!lc3_file) return 1;
129 
130     uint16_t frame_duration_100us = (frame_duration == BTSTACK_LC3_FRAME_DURATION_10000US) ? 100 : 75;
131 
132     uint8_t header[18];
133     little_endian_store_16(header, 0, 0xcc1c);
134     little_endian_store_16(header, 2, sizeof(header));
135     little_endian_store_16(header, 4, sampling_frequency_hz / 100);
136     little_endian_store_16(header, 6, bitrate / 100);
137     little_endian_store_16(header, 8, num_channels);
138     little_endian_store_16(header, 10, frame_duration_100us * 10);
139     little_endian_store_16(header, 12, 0);
140     little_endian_store_32(header, 14, 0); // num samples need to set later
141     fwrite(header, 1, sizeof(header), lc3_file);
142 
143     // print format
144     printf("WAC file:          %s\n", wav_filename);
145     printf("LC3 file:          %s\n", lc3_filename);
146     printf("Samplerate:        %u Hz\n", sampling_frequency_hz);
147     printf("Channels:          %u\n", num_channels);
148     printf("Frame duration:    %s ms\n", (frame_duration == BTSTACK_LC3_FRAME_DURATION_10000US) ? "10" : "7.5");
149     printf("Bitrate:           %u\n", bitrate);
150     printf("Samples per Frame: %u\n", number_samples_per_frame);
151 
152     while (true){
153         // process file frame by frame
154         memset(samples_buffer, 0, sizeof(samples_buffer));
155         // read samples per frame * num channels
156         status = wav_reader_read_int16(number_samples_per_frame * num_channels, samples_buffer);
157 
158         if (status != 0) break;
159 
160         // write len of complete frame
161         uint8_t len[2];
162         little_endian_store_16(len, 0, num_channels * bytes_per_frame);
163         fwrite(len, 1, sizeof(len), lc3_file);
164 
165         // encode frame by frame
166         for (channel = 0; channel < num_channels ; channel++){
167             uint16_t sample;
168             for (sample = 0 ; sample < number_samples_per_frame ; sample++){
169                 frame_buffer[sample] = samples_buffer[ sample * num_channels + channel];
170             }
171             status = lc3_encoder->encode_signed_16(&encoder_contexts[channel], frame_buffer, 1, write_buffer, bytes_per_frame);
172             if (status != ERROR_CODE_SUCCESS){
173                 printf("Error %u\n", status);
174                 break;
175             }
176             fwrite(write_buffer, 1, bytes_per_frame, lc3_file);
177         }
178 
179         if (status != 0) break;
180 
181         frame_count++;
182     }
183 
184     uint32_t total_samples = frame_count * number_samples_per_frame;
185     printf("Total samples: %u\n", total_samples);
186 
187     // rewind and store num samples
188     little_endian_store_32(header, 14, total_samples); // num samples need to set later
189     rewind(lc3_file);
190     fwrite(header, 1, sizeof(header), lc3_file);
191     fclose(lc3_file);
192 }
193