Пример простейшей обработки WAV-файла |
![]() |
Добавил(а) microsin |
В статье описывается открытие WAV-файла с карты SD/MMC и воспроизведение его с помощью ШИМ (PWM, Pulse-Wide Modulation). Для упрощения допускаются только несжатые файлы звука (uncompressed PCM), 8 бит, моно, частота выборок 16000 Гц. Проект работает на макетной плате AT91SAM7X (микроконтроллер ARM Atmel AT91SAM7X256 или AT91SAM7X512), но его можно также запустить на макетных платах Atmel и Olimex. Для работы с файловой системой FAT32 используется библиотека EFSL. Алгоритм работы проекта прост - через консоль DBGU (коннектор DB9 на макетной плате, подключенный к COM-порту компьютера) можно подавать команды на считывание и воспроизведение файлов с карты памяти SD/MMC. Файлы формата WAV (несжатый PCM, 8-бит, моно, скорость выборок 16000 Гц) должны лежать в каталоге wav. В консоли DBGU доступна система подсказки (команда h или ?). -- PlayWav Project 1.0 -- Board : microsin AT91SAM7X, Chip ID : 0x275B0940 ? help or ? - help reboot - restart/reload crontab play filename.wav - play WAV-file info - show info play muson04.wav play muson05.wav play muzon06.wav Не найден файл wav/muzon06.wav info [elapsed] 0 days, 0 hours, 0 minutes, 54 seconds Звук формируется с помощью широтно-импульсной модуляции (PWM). Карта памяти подключена к разъему UEXT макетной платы Olimex SAM7-EX256 (U6 UEXT макетной платы AT91SAM7X), а PWM выведена на разъем EXT (U4 EXT макетной платы AT91SAM7X). Схемы макетных плат и подключения есть в каталоге doc архива проекта (см. ссылки в конце статьи). В статье приведены только файлы, касающиеся открытия и воспроизведения WAV-файлов (projtypes.h, wav.h, wav.c). Полностью проект можно скачать по ссылке из конца статьи. #ifndef _TYPES_ #define _TYPES_ #include "settings.h" #define u8 unsigned char #define u16 unsigned short #define u32 unsigned int #define s8 signed char #define s16 signed short #define s32 signed int #define true 1 #define false 0 #define bool u8 #define LEFT 0 #define RIGHT 1 typedef __packed struct _TWavBlock { u16 size; u8 data[512]; }TWavBlock; typedef __packed struct _TRIFFsection { char id[4]; u32 len; }TRIFFsection; typedef __packed struct _TWavFmtGeneric { u16 compression; u16 channels; u32 samplerate; u32 bytespersecond; u16 blockalign; u16 bitspersample; }TWavFmtGeneric; #endif //_TYPES_ #ifndef _WAV_ #define _WAV_ #include "projtypes.h" #define WAV_BUF_BLOCKS 16 #define WAV_BUF_BLOCKS_MASK (WAV_BUF_BLOCKS-1) #define WAVQUEUE_SIZE 16 #define WAVQUEUE_SIZE_MASK (WAVQUEUE_SIZE-1) #define WAVFILENAME_MAX_LEN 16 //коды ошибок, возвращаемые OpenWaveFile #define WAVFILE_NOT_FOUND (1 << 0) #define WAVFILE_HEADER_NO_DATA (1 << 1) #define WAVFILE_SECTION_NO_DATA (1 << 2) #define WAVFILE_FORMAT_ERR (1 << 3) #define WAVFILE_EFSL_ERR (1 << 6) extern TWavBlock wavbuf [WAV_BUF_BLOCKS]; extern u8 wavIn, wavOut; extern u32 bytes_to_read; extern char wavqueue [WAVQUEUE_SIZE][WAVFILENAME_MAX_LEN]; extern u8 inWav, outWav; void wavreadPoll (void); u8 OpenWaveFile (char* name, u32* datalen); void ConfigureTc1 (void); #endif // _WAV_ #include < string.h > #include < tc/tc.h > #include < aic/aic.h > #include "wav.h" #include "efs.h" #include "vars.h" #include "isr.h" #include "pins.h" TWavBlock wavbuf [WAV_BUF_BLOCKS]; u8 wavIn, wavOut; static EmbeddedFile wfile; u32 bytes_to_read; char wavqueue [WAVQUEUE_SIZE][WAVFILENAME_MAX_LEN]; u8 inWav, outWav; TWavFmtGeneric WAVEfmt; u8 StoredInBuffer (u8 idxIN, u8 idxOUT, u8 bufsize) { if (idxIN >= idxOUT) return (idxIN - idxOUT); else return ((bufsize - idxOUT) + idxIN); } //////////////////////////////////////////////////////////////////////////////// // Таймер TC1 используется для записи выборок воспроизводимого звука в регистр // PWM (настроен на 16000 Гц). void ConfigureTc1 (void) { u32 tcclks; //поле TCCLKS в регистре TC_CMR (режим формирования) u32 div; //коэффициент деления входной частоты при наших битах tcclks /// Конфигурируем Timer Counter 1 на частоту генерации выборок звука TC1_FREQ. AT91C_BASE_PMC->PMC_PCER = 1 <<AT91C_ID_TC1; #define TC1_FREQ 16000 div = 8; tcclks = 1; TC_Configure(AT91C_BASE_TC1, tcclks | AT91C_TC_CPCTRG); AT91C_BASE_TC1->TC_RC = (BOARD_MCK / div) / TC1_FREQ; AIC_ConfigureIT(AT91C_ID_TC1, AT91C_AIC_PRIOR_HIGHEST, ISR_Tc1); AT91C_BASE_TC1->TC_IER = AT91C_TC_CPCS; AIC_EnableIT(AT91C_ID_TC1); TC_Start(AT91C_BASE_TC1); } //////////////////////////////////////////////////////////////////////////////// // Функция открывает WAV-файл по имени name, считывает его параметры // воспроизведения. На выходе функция возвращает длину аудиоданных *datalen, // а также открытый файл, готовый к чтению в позиции секции аудиоданных. // Если все в порядке, то результат функции 0, иначе результат содержит // код ошибки. u8 OpenWaveFile (char* name, u32* datalen) { TRIFFsection riffsection; u32 rifflen; u32 readed, portion; char tmpbuf[512]; *datalen = 0; file_opened = false; //инициализируем файловую систему if (!efs_initialized) { if ( efs_init (&efsl, 0) !=0 ) { printf("Карта MMC не установлена\r\n"); return WAVFILE_EFSL_ERR; } else { efs_initialized = true; } } //открываем файл if (0 != file_fopen(&wfile, &efsl.myFs, name, 'r')) { printf ("Не найден файл %s\r\n", name); CloseFileSystem(&wfile); return WAVFILE_NOT_FOUND; } file_opened = true; //прочитаем заголовок RIFF memset (&riffsection, 0, sizeof(TRIFFsection)); readed = file_read(&wfile, sizeof(TRIFFsection), (u8*)&riffsection); if ((sizeof(TRIFFsection) != readed) || efsl.myIOman.error) { printf ("ERR read RIFF header.\r\n"); CloseFileSystem(&wfile); return WAVFILE_HEADER_NO_DATA; } //проверим заголовок RIFF rifflen = riffsection.len; if (0 != memcmp(riffsection.id, "RIFF", 4) || 0 == rifflen) { printf ("ERR in RIFF header.\r\n"); CloseFileSystem(&wfile); return WAVFILE_FORMAT_ERR; } //прочитаем идентификатор типа WAV-файла readed = file_read(&wfile, 4, (u8*)tmpbuf); if ((4 != readed) || efsl.myIOman.error) { printf ("ERR read WAVE ID.\n\r"); CloseFileSystem(&wfile); return WAVFILE_HEADER_NO_DATA; } rifflen -= readed; //проверим идентификатор типа WAV-файла if (0 != memcmp(tmpbuf, "WAVE", 4)) { printf ("ERR WAVE ID.\r\n"); CloseFileSystem(&wfile); return WAVFILE_FORMAT_ERR; } //читаем и пропускаем все секции, кроме "fmt " и "data" memset (&WAVEfmt, 0, sizeof(TWavFmtGeneric)); while (rifflen) { readed = file_read(&wfile, sizeof(TRIFFsection), (u8*)&riffsection); if ((sizeof(TRIFFsection) != readed) || efsl.myIOman.error) { printf ("ERR read file section.\r\n"); CloseFileSystem(&wfile); return WAVFILE_SECTION_NO_DATA; } rifflen -= readed; //секция формата? if (0==memcmp("fmt ", riffsection.id, 4)) { //проверим длину секции формата if (sizeof(TWavFmtGeneric) != riffsection.len) { printf ("ERR not support extra WAV format.\r\n"); CloseFileSystem(&wfile); return WAVFILE_FORMAT_ERR; } //прочитаем секцию формата readed = file_read(&wfile, sizeof(TWavFmtGeneric), (u8*)&WAVEfmt); if ((sizeof(TWavFmtGeneric) != readed) || efsl.myIOman.error) { printf ("ERR read WAV file format section.\r\n"); CloseFileSystem(&wfile); return WAVFILE_SECTION_NO_DATA; } rifflen -= readed; //тут надо проверить формат if (1 != WAVEfmt.compression) { printf ("ERR compression not supported\r\n"); CloseFileSystem(&wfile); return WAVFILE_FORMAT_ERR; } } else if (0==memcmp("data", riffsection.id, 4)) { if (0==WAVEfmt.channels) { printf ("ERR section 'data' before section 'fmt '\r\n"); CloseFileSystem(&wfile); return WAVFILE_FORMAT_ERR; } else { //все ОК, выходим на позиции чтения данных *datalen = riffsection.len; return 0; } } else { //не интересующая нас секция, просто пропускаем её данные while (riffsection.len) { if (512 >= riffsection.len) portion = riffsection.len; else portion = 512; readed = file_read(&wfile, portion, (u8*)&tmpbuf); if ((portion != readed) || efsl.myIOman.error) { printf ("ERR dummy read section %s.\r\n", riffsection.id); CloseFileSystem(&wfile); return WAVFILE_SECTION_NO_DATA; } rifflen -= readed; riffsection.len -= readed; } } }// while (rifflen): читаем секции WAV-файла printf ("ERR not exist 'data' section\r\n"); CloseFileSystem(&wfile); return WAVFILE_FORMAT_ERR; } //////////////////////////////////////////////////////////////////////////////// // Автоматом проигрывает файлы из очереди wavqueue. void wavreadPoll (void) { u32 readed, portion; char filepath [32]; if ((mode == MODE_IDLE)||(MODE_PLAY_DONE == mode)) { if (inWav == outWav) { RADIOSEND_OFF(); return; } mode = MODE_INIT_PLAY; } else if (MODE_INIT_PLAY == mode) { strcpy(filepath, WAVFOLDER); strcat(filepath, "/"); strcat(filepath, wavqueue[outWav++]); outWav &= WAVQUEUE_SIZE_MASK; errstate = OpenWaveFile(filepath, &bytes_to_read); if ((0 == errstate)&&(0 != bytes_to_read)) { //читаем новую порцию данных в буфер if (512 > bytes_to_read) portion = bytes_to_read; else portion = 512; readed = file_read(&wfile, portion, wavbuf[wavIn].data); if (readed != portion) { printf("ошибка чтения файла\r\n"); CloseFileSystem(&wfile); bytes_to_read = 0; mode = MODE_IDLE; } else { RADIOSEND_ON(); bytes_to_read -= portion; wavbuf[wavIn].size = portion; wavIn++; wavIn &= WAV_BUF_BLOCKS_MASK; if (0 == bytes_to_read) CloseFileSystem(&wfile); mode = MODE_PLAY_FILE; } } else { bytes_to_read = 0; mode = MODE_IDLE; } } else if (MODE_PLAY_FILE == mode) { if (0 == bytes_to_read) return; //есть еще данные для чтения if (WAV_BUF_BLOCKS-2 < StoredInBuffer(wavIn, wavOut, WAV_BUF_BLOCKS)) return; //буфер не переполнен, читаем новую порцию данных в буфер if (512 > bytes_to_read) portion = bytes_to_read; else portion = 512; readed = file_read(&wfile, portion, wavbuf[wavIn].data); if (readed != portion) { printf("ошибка чтения файла\r\n"); CloseFileSystem(&wfile); bytes_to_read = 0; mode = MODE_IDLE; } else { bytes_to_read -= portion; wavbuf[wavIn].size = portion; wavIn++; wavIn &= WAV_BUF_BLOCKS_MASK; if (0 == bytes_to_read) CloseFileSystem(&wfile); } } } [Ссылки] 1. Макетная плата AT91SAM7X. |