MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Как нарисовать графическое представление wav-файла?"
Answer 879984
График аудиофайла типично строится как график зависимости средних или пиковых абсолютных амплитуд от времени (для стерео-записи, верхняя половина графика представляет один канал, а вторая, направленная вниз - второй).
Алгоритм выглядит так:
Выделить из WAV-данных массив мгновенных значений звука (семплов) в формате, с которым удобно работать (например, значения float в диапазоне от -1.0 до 1.0). Информацию о том, как это делается, можно найти например здесь.
Разбить массив на интервалы, примерно соответствующие пикселю на изображении графика. Формула для вычисления длины интервала:
interval = (N_SAMPLES) / (K * W_WINDOW);
где
N_SAMPLES - число семплов (одного канала);
W_WINDOW - ширина окна для отображения графика, в пикселах;
K - коэффициент, задающий "детализированность" графика, обычно в диапазоне (1.0; 2.0).
Определить значение модуля амплитуды (среднее, пиковое) на каждом интервале
Нарисовать график с помощью любой графической библиотеки
Пример кода для построения графика средних амплитуд одного канала (т.е., верхней половины приведенного рисунка):
#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; }
Поддерживает форматы кодирования PCM (8 или 16 бит) и IEEE Float (32 бит). Требует Visual C++ 2010+ и библиотеку Glut.
Источники:
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.