Last active
May 2, 2025 03:17
-
-
Save ExternPointer/08455b041acdd9f5d9ab830718e203d4 to your computer and use it in GitHub Desktop.
Simply audio resampler, which allow to work in realtime (resample stream)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* File format: raw, PCM s16le | |
* Resample 44100 Hz ./audio.raw to 16000 Hz ./audio_resampled.raw: | |
* gcc -g resampler.c -o resampler | |
* ./resampler | |
*/ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <memory.h> | |
#include <math.h> | |
typedef float float32; | |
typedef short int int16; | |
typedef int BOOL; | |
#define FALSE 0 | |
#define TRUE 1 | |
typedef struct pcm_s16le_chunk_s | |
{ | |
int16 *samples; | |
int size; | |
} pcm_chunk_t; | |
pcm_chunk_t *PcmChunk_Create( char *buf, int sz ); | |
void PcmChunk_Concat( pcm_chunk_t *dst, pcm_chunk_t *src ); | |
void PcmChunk_Destroy( pcm_chunk_t *chunk ); | |
pcm_chunk_t *PcmChunk_Create( char *buf, int sz ) | |
{ | |
if( sz <= 0 ) | |
return NULL; | |
pcm_chunk_t *chunk = ( pcm_chunk_t * )malloc( sizeof( pcm_chunk_t ) ); | |
chunk->size = sz / sizeof( int16 ); | |
chunk->samples = ( int16 * )malloc( chunk->size * sizeof( int16 ) ); | |
memcpy( chunk->samples, buf, sz ); | |
return chunk; | |
} | |
pcm_chunk_t *PcmChunk_Copy( pcm_chunk_t *chunk ) | |
{ | |
return PcmChunk_Create( ( char * )chunk->samples, chunk->size * sizeof( int16 ) ); | |
} | |
/* Write samples from src to the end of dst chunk */ | |
void PcmChunk_Concat( pcm_chunk_t *dst, pcm_chunk_t *src ) | |
{ | |
dst->size += src->size; | |
dst->samples = ( int16 * )realloc( dst->samples, dst->size * sizeof( int16 ) ); | |
memcpy( &dst->samples[dst->size - src->size], &src->samples[0], src->size * sizeof( int16 ) ); | |
} | |
void PcmChunk_Destroy( pcm_chunk_t *chunk ) | |
{ | |
if( chunk == NULL ) | |
return; | |
free( chunk->samples ); | |
free( chunk ); | |
} | |
typedef struct resampler_s | |
{ | |
int srcSampleRate; | |
int dstSampleRate; | |
pcm_chunk_t *prevChunk; | |
float pos; | |
} resampler_t; | |
resampler_t *Resampler_Create( int srcSampleRate, int dstSampleRate ) | |
{ | |
resampler_t *r = ( resampler_t * )malloc( sizeof( resampler_t ) ); | |
r->srcSampleRate = srcSampleRate; | |
r->dstSampleRate = dstSampleRate; | |
r->prevChunk = NULL; | |
r->pos = 1.0f; | |
return r; | |
} | |
/* Resample input chunk. Return the new object of pcm_chunk_t. You need to free memory */ | |
/* This can return NULL if needs more chunks */ | |
pcm_chunk_t *Resampler_Proc( resampler_t *r, pcm_chunk_t *input ) | |
{ | |
if( r->prevChunk == NULL ) | |
r->prevChunk = PcmChunk_Copy( input ); | |
else | |
PcmChunk_Concat( r->prevChunk, input ); | |
input = r->prevChunk; | |
float ratio = (float)r->srcSampleRate / (float)r->dstSampleRate; | |
int16 *outputSamples = ( int16 * )malloc( input->size * sizeof( int16 ) ); | |
int cnt = 0; | |
while( r->pos < input->size - 1 ) | |
{ | |
float w = 1.0f - (r->pos - (int)r->pos); | |
outputSamples[cnt++] = input->samples[ (int)r->pos ] * w + | |
input->samples[ (int)r->pos + 1 ] * (1.0f - w); | |
r->pos += ratio; | |
} | |
int lastIdx = (int)r->pos; | |
if( lastIdx >= input->size ) | |
{ | |
r->pos -= r->prevChunk->size; | |
PcmChunk_Destroy( r->prevChunk ); | |
r->prevChunk = NULL; | |
} | |
else | |
{ | |
r->pos -= lastIdx; | |
pcm_chunk_t *prev = PcmChunk_Create( ( char * )&input->samples[lastIdx], | |
(input->size - lastIdx)*sizeof(int16) ); | |
PcmChunk_Destroy( r->prevChunk ); | |
r->prevChunk = prev; | |
} | |
pcm_chunk_t *resampled = PcmChunk_Create( ( char * )outputSamples, cnt * sizeof( int16 ) ); | |
free( outputSamples ); | |
return resampled; | |
} | |
void Resampler_Destroy( resampler_t *r ) | |
{ | |
PcmChunk_Destroy( r->prevChunk ); | |
free( r ); | |
} | |
int main() | |
{ | |
int i; | |
FILE *f = fopen( "audio.raw", "r" ); | |
fseek( f, 0, SEEK_END ); | |
int len = ftell( f ); | |
fseek( f, 0, SEEK_SET ); | |
char *audio = ( char * )malloc( len ); | |
fread( audio, len, 1, f ); | |
fclose( f ); | |
f = fopen( "audio_resampled.raw", "w" ); | |
const int chunkSize = 32; | |
resampler_t *resampler = Resampler_Create( 44100, 16000 ); | |
/* Simulation of stream. We proccessing chunks of chunkSize bytes */ | |
for( i = 0; i < len; i += chunkSize ) | |
{ | |
int size = chunkSize; | |
if( size + i > len ) | |
size = len - i; | |
pcm_chunk_t *chunk = PcmChunk_Create( audio + i, size ); /* Create chunk from bytes */ | |
pcm_chunk_t *r = Resampler_Proc( resampler, chunk ); /* Resample current chunk */ | |
if( r ) /* Check result for NULL */ | |
{ /* r == NULL if needs next chunk for resampling */ | |
fwrite( ( char * )r->samples, r->size * sizeof( int16 ), 1, f ); /* */ | |
PcmChunk_Destroy( r ); /* free result of Resamole_Proc */ | |
} | |
PcmChunk_Destroy( chunk ); | |
} | |
Resampler_Destroy( resampler ); | |
fclose( f ); | |
free( audio ); | |
return 0; | |
} |
Thanks a lot, it is fast and useful
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very helpful. Thanks for this!