1
0
mirror of https://github.com/mon/2dxTools.git synced 2024-09-23 19:08:27 +02:00
2dxTools/2dxWav.c
2017-03-23 22:46:35 +10:00

170 lines
6.4 KiB
C

#include "sox/sox.h"
#include "2dxWav.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
// the assert in assert.h produces a crash, this just returns
#define assert(cond) if(!(cond)) {printf("Something went wrong: " #cond "\n"); return 1;}
/* Private data for .wav file, taken from wav.c so we can change
certain attributes to the 2dx specific format */
typedef struct {
uint64_t numSamples;
size_t dataLength;
unsigned short formatTag;
unsigned short samplesPerBlock;
unsigned short blockAlign;
size_t dataStart;
char * comment;
int ignoreSize;
unsigned short nCoefs; /* ADPCM: number of coef sets */
short *lsx_ms_adpcm_i_coefs; /* ADPCM: coef sets */
unsigned char *packet; /* Temporary buffer for packets */
short *samples; /* interleaved samples buffer */
short *samplePtr; /* Pointer to current sample */
short *sampleTop; /* End of samples-buffer */
unsigned short blockSamplesRemaining;/* Samples remaining per channel */
int state[16]; /* step-size info for *ADPCM writes */
/* there are more things in here but they're related to GSM encoding */
} priv_t;
/* For debugging */
void printwav(priv_t* wav) {
printf("\t numSamples: %d\n" , wav->numSamples );
printf("\t dataLength: %zu\n" , wav->dataLength );
printf("\t formatTag: %hu\n" , wav->formatTag );
printf("\t samplesPerBlock: %hu\n" , wav->samplesPerBlock );
printf("\t blockAlign: %hu\n" , wav->blockAlign );
printf("\t dataStart: %zu\n" , wav->dataStart );
printf("\t ignoreSize: %d\n" , wav->ignoreSize );
printf("\t nCoefs: %hu\n" , wav->nCoefs );
printf("\t lsx_ms_adpcm_i_coefs: %d\n" , wav->lsx_ms_adpcm_i_coefs );
printf("\t packet: %d\n:" , wav->packet );
printf("\t samples: %d\n" , wav->samples );
printf("\t samplePtr: %d\n" , wav->samplePtr );
printf("\t sampleTop: %d\n" , wav->sampleTop );
printf("\t blockSamplesRemaining: %d\n" , wav->blockSamplesRemaining );
}
int convert_wav(char* inFile, char* outWav, int trimPreview) {
static sox_format_t *in, *out;
sox_effects_chain_t *chain;
sox_effect_t *e;
sox_signalinfo_t interm_signal;
char *args[10];
assert(sox_init() == SOX_SUCCESS);
assert(in = sox_open_read(inFile, NULL, NULL, NULL));
// 2dx specific format
sox_signalinfo_t out_signal = {
.rate = 44100,
.channels = 2, // have to be 2 for later hacks to work
.precision = 0,
.length = 0,
.mult = NULL
};
sox_encodinginfo_t out_encoding;
memset(&out_encoding, 0, sizeof(out_encoding));
out_encoding.encoding = SOX_ENCODING_MS_ADPCM;
out_encoding.bits_per_sample = 4;
assert(out = sox_open_write(outWav, &out_signal, &out_encoding, "wav", NULL, NULL));
// Forcibly set the values we want
priv_t *wav = (priv_t *) out->priv;
wav->samplesPerBlock = 244;
wav->blockAlign = 256;
// taken from wav.c, with locked 2 channels
// The buffers become the wrong size, but we use smaller buffers so it's ok
size_t sbsize = 2 * wav->samplesPerBlock;
wav->sampleTop = wav->samples + sbsize;
// we'll need these later to fix the headers
uint16_t blockAlign = wav->blockAlign;
uint16_t samplesPerBlock = wav->samplesPerBlock;
uint32_t avgBytes = (double)blockAlign*out_signal.rate / (double)samplesPerBlock + 0.5;
// Debugging
/*printf("\n\nOutput data:\n");
printwav(wav);
wav = (priv_t *) in->priv;
printf("\n\nInput data:\n");
printwav(wav);*/
chain = sox_create_effects_chain(&in->encoding, &out->encoding);
interm_signal = in->signal; /* NB: deep copy */
e = sox_create_effect(sox_find_effect("input"));
args[0] = (char *)in;
assert(sox_effect_options(e, 1, args) == SOX_SUCCESS);
assert(sox_add_effect(chain, e, &in->signal, &in->signal) == SOX_SUCCESS);
free(e);
if (in->signal.rate != out->signal.rate) {
e = sox_create_effect(sox_find_effect("rate"));
assert(sox_effect_options(e, 0, NULL) == SOX_SUCCESS);
assert(sox_add_effect(chain, e, &interm_signal, &out->signal) == SOX_SUCCESS);
free(e);
}
// Only use 10 seconds of audio for previews
if(trimPreview) {
e = sox_create_effect(sox_find_effect("trim"));
args[0] = "0";
args[1] = "10";
assert(sox_effect_options(e, 2, args) == SOX_SUCCESS);
assert(sox_add_effect(chain, e, &interm_signal, &in->signal) == SOX_SUCCESS);
free(e);
}
if (in->signal.channels != out->signal.channels) {
e = sox_create_effect(sox_find_effect("channels"));
assert(sox_effect_options(e, 0, NULL) == SOX_SUCCESS);
assert(sox_add_effect(chain, e, &interm_signal, &out->signal) == SOX_SUCCESS);
free(e);
}
e = sox_create_effect(sox_find_effect("output"));
args[0] = (char *)out;
assert(sox_effect_options(e, 1, args) == SOX_SUCCESS);
assert(sox_add_effect(chain, e, &in->signal, &in->signal) == SOX_SUCCESS);
free(e);
/* Flow samples through the effects processing chain until EOF is reached */
sox_flow_effects(chain, NULL, NULL);
/* All done; tidy up: */
sox_delete_effects_chain(chain);
sox_close(out);
sox_close(in);
sox_quit();
// Fix the header, since sox wrote it before we changed our block stuff
FILE* outFile = fopen(outWav, "r+b");
// AvgBytesPerSec
fseek(outFile, 28, SEEK_SET);
fwrite(&avgBytes, sizeof(uint32_t), 1, outFile);
fwrite(&blockAlign, sizeof(uint16_t), 1, outFile);
// samples per block
fseek(outFile, 38, SEEK_SET);
fwrite(&samplesPerBlock, sizeof(uint16_t), 1, outFile);
// RIFF size
// minus 4 byte "RIFF" and 4 bytes for this number
size_t wavSize = file_size(outWav) - 8;
fseek(outFile, 4, SEEK_SET);
fwrite(&wavSize, sizeof(uint32_t), 1, outFile);
//// data size
// minus the RIFF header size
wavSize -= 82;
fseek(outFile, 86, SEEK_SET);
fwrite(&wavSize, sizeof(uint32_t), 1, outFile);
fclose(outFile);
return 0;
}