A
The audio file is typically structured as a schedule for the relationship between the average or peak absolute amplitudes from time to time (for stereo recordings, the top half of the schedule represents one channel and the second, downstream, second). Algorithm looks like:Make instantaneous sound values (samples) available from the WAV-data in a format that is convenient (e.g. float values between -1.0 and 1.0). Information on how this is done can be found, for example https://ru.stackoverflow.com/questions/760083/wav-data-%D0%BA%D0%B0%D0%BA-%D1%81%D0%B8%D0%B4%D1%8F%D1%82-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5?rq=1 ♪Break the mass at intervals, roughly corresponding to the picsel on the chart. Interval length formula:interval = (N_SAMPLES) / (K * W_WINDOW);where N_SAMPLES Number of samples (one channel);W_WINDOW - Width of the window to display the schedule, in the pickles;K - the coefficient of detailing the schedule, usually in range (1.0; 2.0). Determine the value of the amplitude module (average, peak) at each intervalMake a schedule with any graphical libraryExample of the code for the scheduling of one channel (i.e. the upper half of the figure shown):#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <memory.h>
#include <math.h>
#include <glut.h> //Для рисования
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glut32.lib")
struct WAV_HEADER
{
/* RIFF Chunk Descriptor /
uint8_t RIFF[4]; // RIFF Header Magic header
uint32_t ChunkSize; // RIFF Chunk Size
uint8_t WAVE[4]; // WAVE Header
/ "fmt" sub-chunk /
uint8_t fmt[4]; // FMT header
uint32_t Subchunk1Size; // Size of the fmt chunk
uint16_t AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
uint16_t NumOfChan; // Number of channels 1=Mono 2=Sterio
uint32_t SamplesPerSec; // Sampling Frequency in Hz
uint32_t bytesPerSec; // bytes per second
uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo
uint16_t bitsPerSample; // Number of bits per sample
/ "data" sub-chunk */
uint32_t Subchunk2ID; // "data" string
uint32_t Subchunk2Size; // Sampled data length
};
//Возвращает следующий семпл начиная с указанной позиции в массиве байт
float ReadNextSample (
/IN/ WAV_HEADER* hdr, //заголовок WAV
/IN/ unsigned char* rawdata, //данные WAV
/IN,OUT/ uint64_t * startindex //начальный индекс (после вызова функции устанавливается в индекс следующего семпла)
){
float res = 0.0f;
unsigned char byte = 0;
int16_t val16 = 0;
switch (hdr->AudioFormat)
{
case 1://PCM
if (hdr->bitsPerSample == 8)
{
byte = rawdata[*startindex];
*startindex += 1;
res = (byte - 128.0f) / 255.0f;
}
else if (hdr->bitsPerSample == 16)
{
memcpy(&val16,rawdata+(*startindex),2);
*startindex += 2;
res = (val16) / 32767.0f;
}
if (res > 1.0f) res = 1.0f;
if (res < -1.0f) res = -1.0f;
break;
case 3: //IEEE Float
if (hdr->bitsPerSample == 32) {
memcpy(&res,rawdata+(*startindex),sizeof(float));
*startindex += sizeof(float);
}
break;
}
return res;
}
//Преобразует данные WAV в массив нормализованных Float-значений в интервале (-1.0;1.0). Возвращает размер массива.
uint64_t GetSamples(
/* IN / WAV_HEADER hdr, //заголовок WAV
/* IN / unsigned char data, //данные WAV
/* OUT / float* psamples //выходной массив
){
float* samples = NULL;
uint64_t samples_count = hdr->Subchunk2Size / ((int)hdr->bitsPerSample / 8);
samples = (float*)malloc(samples_count * sizeof(float));
uint64_t i_data;
uint64_t i_sample=0;
for(i_data=0; i_data<hdr->Subchunk2Size; ){
if(i_sample>=samples_count)break;
samples[i_sample] = ReadNextSample(hdr,data,&i_data);
i_sample++;
}
*psamples = samples;
return i_sample;
}
//Считывает заголовок и данные WAV из файла
uint64_t ReadWav(
/* IN / wchar_t file, //путь к файлу
/* OUT / WAV_HEADER hdr, //указатель на переменную для записи заголовка
/* OUT / unsigned char* pdata //указатель на переменную для записи данных
){
memset(hdr,0,sizeof(WAV_HEADER));
FILE* f = _wfopen(file,L"rb");
if(f == NULL){
printf("Cannot open file!\n");
return 0;
}
//считываем заголовок
fread(&(hdr->RIFF),4,1,f);
fread(&(hdr->ChunkSize),4,1,f);
fread(&(hdr->WAVE),4,1,f);
fread(&(hdr->fmt),4,1,f);
fread(&(hdr->Subchunk1Size),4,1,f);
if(!(hdr->RIFF[0] == 'R' && hdr->RIFF[1] == 'I' && hdr->RIFF[2] == 'F' && hdr->RIFF[3] == 'F') ||
!(hdr->WAVE[0] == 'W' && hdr->WAVE[1] == 'A' && hdr->WAVE[2] == 'V' && hdr->WAVE[3] == 'E')){
printf("File is not RIFF/WAV!\n");
fclose(f);
return 0;
}
fread(&(hdr->AudioFormat),2,1,f);
fread(&(hdr->NumOfChan),2,1,f);
fread(&(hdr->SamplesPerSec),4,1,f);
fread(&(hdr->bytesPerSec),4,1,f);
fread(&(hdr->blockAlign),2,1,f);
fread(&(hdr->bitsPerSample),2,1,f);
uint16_t fmtExtraSize = 0;
if (hdr->Subchunk1Size == 18) {
fread(&(fmtExtraSize),2,1,f);
fseek(f,fmtExtraSize,SEEK_CUR);
}
unsigned char* data=NULL;
size_t data_size;
size_t res;
//пытаемся считать данные
fread(&(hdr->Subchunk2ID),4,1,f);
fread(&(hdr->Subchunk2Size),4,1,f);
while(1){
data = (unsigned char*)malloc(hdr->Subchunk2Size);
data_size = fread(data,1,hdr->Subchunk2Size,f);
if(hdr->Subchunk2ID == 0x61746164) break;//данные найдены
//если Subchunk2Id нет тот, что ожидался, пропускаем и пробуем снова
free(data);
data = NULL;
hdr->Subchunk2ID = 0;
hdr->Subchunk2Size = 0;
res=fread(&(hdr->Subchunk2ID),4,1,f);
if(res < 4)break;
res=fread(&(hdr->Subchunk2Size),4,1,f);
if(res < 4)break;
}
fclose(f);
*pdata = data;
if(data == NULL || hdr->Subchunk2Size == 0) return 0;
if(data_size < hdr->Subchunk2Size) {
printf("Warning: data size is lower then expected!\n");
hdr->Subchunk2Size = res;
}
return hdr->Subchunk2Size;
}
float* values = NULL; //массив значений Y для графика
uint64_t interval=0; //размер интервала усреднения
uint64_t intervals = 0; //количество интервалов
int width = 500; //ширина окна
void Initialize()
{
//Выбрать фоновый цвет
glClearColor(1.0,1.0,1.0,1.0);
//Установить проекцию
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0);
}
void DrawGraph(float* values,uint64_t count) //отрисовка графика по массиву значений
{
glColor3f(0.0,1.0,0.0); //Выбираем цвет
glBegin(GL_LINES);
float x0,y0;
float x,y;
//Задаем точки
for(int i=0;i<intervals;i++){
x0=i * 1.0f/(intervals);
y0=values[i];
glVertex3f(x0,y0,0.0);
x=(i+1) * 1.0f/(intervals);
y=values[i+1];
glVertex3f(x,y,0.0);
}
glEnd();
glFlush();
}
void Draw(){
DrawGraph(values,intervals);
}
int main(int argc, char **argv)
{
WAV_HEADER hdr={0}; //заголовок WAV
unsigned char* bytes = NULL; //данные WAV
float* data = NULL; //массив семплов
uint64_t count = 0; //число семплов
//считываем файл
if(ReadWav(L"c:\\Media\\File1.wav",&hdr,&bytes)==0){
getchar();
return 0;
}
//получаем массив семплов
count = GetSamples(&hdr,bytes,&data);
free(bytes);
//определяем интервал для усреднения
int chans = hdr.NumOfChan;
interval = (count/ chans) / (1.5f* width);
if(interval==0) interval=1;
intervals = (count/ chans) / interval;
//формируем массив значений для графика
values = (float*)malloc(intervals*sizeof(float));
float y;
for(int i=0;i<intervals;i++){
y=0.0f;
for(int j=i*interval;j<(i+1)*interval;j++){
if(j*chans>=count)break;
y += abs(data[j * chans]);
}
y = y/(float)interval; //находим среднее значение для каждого интервала
values[i]=y;
}
free(data);
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(width,400); //Размер окна
glutInitWindowPosition(100,100); //Позиция окна
glutCreateWindow("Graph");
Initialize();
glutDisplayFunc(Draw); //Задаем функцию отрисовки
glutMainLoop();
return 0;
}
Supports PCM coding formats (8 or 16 bats) and IEEE Float (32 bats). Visual C+ 2010+ and library https://www.opengl.org/resources/libraries/glut/ ♪Sources: https://manual.audacityteam.org/man/audacity_waveform.html https://stackoverflow.com/questions/26663494/algorithm-to-draw-waveform-from-audio http://www.supermegaultragroovy.com/2009/10/06/drawing-waveforms/