Пример простейшей обработки WAV-файла Печать
Добавил(а) microsin   

В статье описывается открытие WAV-файла с карты SD/MMC и воспроизведение его с помощью ШИМ (PWM, Pulse-Wide Modulation). Для упрощения допускаются только несжатые файлы звука (uncompressed PCM), 8 бит, моно, частота выборок 16000 Гц. Проект работает на макетной плате AT91SAM7X (микроконтроллер ARM Atmel AT91SAM7X256 или AT91SAM7X512), но его можно также запустить на макетных платах Atmel и Olimex. Для работы с файловой системой FAT32 используется библиотека EFSL.

playwav_IMG_0884.JPG

Алгоритм работы проекта прост - через консоль 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 архива проекта (см. ссылки в конце статьи).

playwav-sch03.png

В статье приведены только файлы, касающиеся открытия и воспроизведения 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.
2. IAR EW ARM: как сделать USB Mass Storage Device на основе MMC/SD.
3. IAR EW ARM: работа с файловой системой FAT на карточках SD/MMC (с использованием библиотеки EFSL).
4. Wave File Format - формат звукового файла WAV.
5. Проект playwav для IAR Embedded Workbench for ARM 5.50.1, микроконтроллер ARM AT91SAM7X256 или AT91SAM7X512.
6. Audio File Format Specifications site:ece.mcgill.ca.