123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /*
- * encode.c
- *
- * Copyright (C) 2023 bzt
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * @brief Binary to audio converter
- *
- */
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include "wav.h"
- #ifndef M_PI
- #define M_PI 3.14159265358979323846
- #endif
- extern int verbose;
- /* mp3 encoder */
- #include "libmp3lame/lame.h"
- /**
- * Encode binary into audio
- */
- uint8_t *encode(uint8_t *data, size_t size, int fmt, int en, int nb, int sy, int fm, int ng, int length, int hdr,
- int rate, int ol, int zl, int bitrate, int quality, size_t *outlen)
- {
- lame_t gf;
- uint8_t *out, *ptr, *buf = NULL;
- size_t sofar;
- int16_t *intbuf;
- int i, j, k, l, m, b, set, stop, len, in_limit, ret;
- uint64_t ulen, glen, us;
- wav_header_t *wav;
- *outlen = 0;
- /* check arguments */
- if(length < 1) length = 1;
- if(length > 4096) length = 4096;
- if(ng < 0) ng = 0;
- if(ng > 4096) ng = 4096;
- if(rate < 22050) rate = 22050;
- if(rate > 48000) rate = 48000;
- if(ol > 127) ol = 127;
- if(ol < -128) ol = -128;
- if(zl > 127) zl = 127;
- if(zl < -128) zl = -128;
- if(bitrate < 96) bitrate = 96;
- if(bitrate > 256) bitrate = 256;
- if(quality < 0) quality = 0;
- if(quality > 9) quality = 9;
- /* convert lengths in usec to samples */
- ulen = length; length = (uint64_t)length * (uint64_t)rate / 1000000;
- glen = ng; ng = (uint64_t)ng * (uint64_t)rate / 1000000;
- if(ulen < 4000000 / rate) ulen = 4000000 / rate;
- if(length < 4) length = 4;
- if(length > 4096) length = 4096;
- if(ng < 0) ng = 0;
- if(ng > 4096) ng = 4096;
- if(verbose)
- printf("%s samples, freq %d, length %d usec, gap %d usec\n", fm ? "FM" : "AM", rate, (int)ulen, (int)glen);
- if(!fm) {
- /* amplitude modulation */
- /* calculate how many samples make up a bit */
- set = length / 4;
- if(set < 2) set = 2;
- stop = length - set;
- /* create wave from bits */
- j = (sy == 3 ? 3 : (sy ? 2 : 1)); l = nb * j;
- len = (uint64_t)size * ((uint64_t)l * ulen + glen) * (uint64_t)rate / 1000000;
- if(verbose) {
- printf("number of bits %d, 1 bit %d samples (%d data %d stop, %d with %d%d sync), %d %s endian bits per byte, gap %d samples (total %d samples)\n",
- (int)(size * nb), length + ng, set, stop, length * j, sy & 1, sy & 2 ? 1 : 0, nb, en ? "little" : "big", ng, len);
- printf("sample rate %d, one level %d, zero level %d, hdr %d, fmt %d, bitrate %d, quality %d\n", rate, ol, zl, hdr, fmt, bitrate, quality);
- }
- out = (uint8_t*)malloc(sizeof(wav_header_t) + len + 8 * length);
- if(!out) return NULL;
- memset(out, zl, sizeof(wav_header_t) + len + 8 * length);
- ptr = out + sizeof(wav_header_t);
- /* start at length because we add a leader */
- for(i = 0, us = 4 * ulen; i < size; i++) {
- if(i && verbose > 1) printf("--------\n");
- for(j = 0; j < nb; j++) {
- /* recalculate sample position to counteract rounding errors */
- l = us * rate / 1000000;
- if(sy & 1) { memset(ptr + l, ol, set); l += length; us += ulen; }
- b = j < 8 && data[i] & (1 << (en ? j : (nb - 1) - j)) ? 1 : 0;
- if(verbose > 1) printf(" pos %8d byte %8d bit %d\n", (int)l, (int)i, b);
- if(b) memset(ptr + l, ol, set);
- l += length; us += ulen;
- if(sy & 2) { memset(ptr + l, hdr && !i && j < 7 ? zl : ol, set); l += length; us += ulen; }
- }
- if(ng) { memset(ptr + l, zl, ng); l += ng; us += glen; }
- }
- /* get actual length, add trailer */
- len = l + 4 * length;
- } else {
- /* frequency modulation */
- if(length < 8) length = 8;
- if(ng < 1) ng = length * 3 / 4;
- if(ng < 2) ng = 2;
- if((ol >= 0 && zl >= 0) || (ol < 0 && zl < 0)) zl = -ol;
- len = size * nb * (length + ng);
- if(verbose) {
- printf("number of bits %d, 1 bit %d - %d samples, %d %s endian bits per byte (max %d samples)\n",
- (int)(size * nb), length, length + ng, nb, en ? "little" : "big", len);
- printf("sample rate %d, fm %d, one level %d, zero level %d, fmt %d, bitrate %d, quality %d\n", rate, fm, ol, zl, fmt, bitrate, quality);
- }
- out = (uint8_t*)malloc(sizeof(wav_header_t) + len + length);
- if(!out) return NULL;
- memset(out, 0, sizeof(wav_header_t) + len + length);
- ptr = out + sizeof(wav_header_t);
- /* leader */
- m = fm == 1 ? ol : zl;
- for(k = 0, set = length / 2; k < set; k++)
- ptr[k] = m * sinf(M_PI*(float)(k+set)/(float)length) / 4;
- /* bits, either length or length+ng long */
- for(i = 0, m = ol, l = length / 2; i < size; i++) {
- if(i && verbose > 1) printf("--------\n");
- for(j = 0; j < nb; j++, l += b) {
- b = length + (j < 8 && data[i] & (1 << (en ? j : (nb - 1) - j)) ? ng : 0);
- if(verbose > 1) printf(" pos %8d byte %8d bit %d\n", (int)l, (int)i, b > length ? 1 : 0);
- set = b / 2; stop = b - set;
- switch(fm) {
- case 1:
- for(k = 0; k < set; k++)
- ptr[l + k] = zl * sinf(M_PI*(float)k/(float)set);
- for(k = 0; k < stop; k++)
- ptr[l + set + k] = ol * sinf(M_PI*(float)k/(float)stop);
- break;
- case 2:
- for(k = 0; k < set; k++)
- ptr[l + k] = ol * sinf(M_PI*(float)k/(float)set);
- for(k = 0; k < stop; k++)
- ptr[l + set + k] = zl * sinf(M_PI*(float)k/(float)stop);
- break;
- default:
- for(k = 0; k < b; k++)
- ptr[l + k] = m * sinf(M_PI*(float)k/(float)b);
- m = (m == ol ? zl : ol);
- break;
- }
- }
- }
- /* trailer */
- if(fm < 3) m = fm == 1 ? zl : ol;
- for(k = 0, set = length / 2; k < set; k++)
- ptr[l + k] = m * sinf(M_PI*(float)k/(float)length) / 4;
- /* get actual length */
- len = l + length / 2;
- }
- /* do we need mp3 compression? */
- if(fmt) {
- if(!(gf = lame_init())) { if(verbose) printf("lame init error\n"); free(out); return NULL; }
- lame_set_write_id3tag_automatic(gf, 0);
- lame_set_num_channels(gf, 1);
- lame_set_mode(gf, MONO);
- lame_set_num_samples(gf, len);
- lame_set_in_samplerate(gf, rate);
- lame_set_out_samplerate(gf, rate);
- lame_set_quality(gf, quality);
- lame_set_brate(gf, bitrate);
- if(lame_init_params(gf) < 0) { free(out); return NULL; }
- l = 0;
- in_limit = lame_get_maximum_number_of_samples(gf, LAME_MAXMP3BUFFER);
- if(in_limit < 1) in_limit = 1;
- if(verbose) printf("encoding mp3 with lame, in_limit %d bufmax %d\n", in_limit, LAME_MAXMP3BUFFER);
- intbuf = (int16_t*)malloc(in_limit * sizeof(int16_t));
- if(!intbuf) { free(out); return NULL; }
- for(sofar = 0; len > 0;) {
- l = len > in_limit ? in_limit : len;
- buf = (uint8_t*)realloc(buf, sofar + LAME_MAXMP3BUFFER);
- if(!buf) { free(intbuf); free(out); return NULL; }
- for(j = 0; j < l; j++) intbuf[j] = ptr[j] * 256;
- ret = lame_encode_buffer(gf, intbuf, NULL, l, buf + sofar, LAME_MAXMP3BUFFER);
- if(ret <= 0) break; else sofar += ret;
- ptr += l; len -= l;
- }
- buf = (uint8_t*)realloc(buf, sofar + LAME_MAXMP3BUFFER);
- if(!buf) { free(intbuf); free(out); return NULL; }
- ret = lame_encode_flush(gf, buf + sofar, LAME_MAXMP3BUFFER);
- if(ret > 0) sofar += ret;
- buf = (uint8_t*)realloc(buf, sofar + LAME_MAXMP3BUFFER);
- if(!buf) { free(intbuf); free(out); return NULL; }
- ret = lame_get_lametag_frame(gf, buf + sofar, LAME_MAXMP3BUFFER);
- if(ret > 0) memcpy(buf, buf + sofar, ret);
- free(intbuf);
- free(out);
- *outlen = sofar;
- out = buf;
- } else {
- /* no mp3, just return wave */
- wav = (wav_header_t*)out;
- memcpy(&wav->str_riff, "RIFF", 4);
- wav->wav_size = sizeof(wav_header_t) + len;
- memcpy(&wav->str_wave, "WAVEfmt ", 8);
- wav->fmt_chunk_size = 16;
- wav->audio_format = wav->channels = wav->frame_size = 1;
- wav->sample_rate = wav->byte_rate = rate;
- wav->bit_depth = 8;
- memcpy(&wav->str_data, "data", 4);
- wav->data_bytes = len;
- for(i = 0; i < len; i++) ptr[i] += 128;
- *outlen = wav->wav_size;
- }
- if(verbose) printf("returning audiofile %d bytes\n", (int)*outlen);
- return out;
- }
- /**
- * WASM helper
- */
- int doencode(uint8_t *data, size_t size, int fmt, int en, int nb, int sy, int fm, int ng, int length, int hdr,
- int rate, int ol, int zl, int bitrate, int quality, uint8_t *out, int outmax)
- {
- size_t outlen = 0;
- uint8_t *buf;
- verbose = 1;
- printf("-------- encode --------\n");
- buf = encode(data, size, fmt, en, nb, sy, fm, ng, length, hdr, rate, ol, zl, bitrate, quality, &outlen);
- if(outlen > (size_t)outmax) outlen = outmax;
- if(buf && outlen > 0) { memcpy(out, buf, outlen); free(buf); }
- return outlen;
- }
|