[[戻る>TinyArcade]]
*サウンド再生 [#ac881d44]
サウンドを再生するだけのプログラムです。どうしても長くなってしまう・・・。~
本当はすべてinoファイルに書こうと思たんだけどどうしても~
void TC5_Handler(void) __attribute__ ((weak, alias("SoundStreamCallback")));~
の行でエラーになってしまったのでcppファイルに分割しました。TC5_Handlerはinoファイル内では書けないのかな?~
とにかくわかる範囲で説明。~
TCは多分タイムカウンタの事。このカウンタ使って周期的に割り込み処理を起こしてその中で10bitDACにデータを書き込み音を再生している。~
このプログラムでは1秒間に11025回の割り込み処理を起こしその度にstream_buffer内の1バイトを10bitDACに送っている。~
10bitDACに送るデータ形式は符号なし10bitデータなので符号なし8bitデータを用意して割り込み処理内でデータを<<2して送っている。~
stream_bufferはリングバッファになっていて800バイト確保して前半400バイトを再生している場合は後半400バイトをデータ更新、後半400バイトを再生している場合は前半400バイトをデータ更新している。~
UpdateSoundStream()はloopで毎回呼び出す必要があり、GetSoundStreamWritableSize()でサウンドバッファに書き込みするバイト数を取得して0以外ならWriteSoundStream()でwaveデータを書き込む。~
このプログラムはwaveの終端になったらwaveの先頭に戻ってループ再生している。~
またTinyArcadeは1チャンネル分しかサウンドを再生することができないためBGM,SEなどたくさん同時に再生したい場合は自前でwaveを合成する必要がある。~
~
要するにこのプログラムはBGM再生中にSEが再生できないためこのままゲームプログラムに使えない…。~
*サンプルプログラム [#qe1538e7]
-SoundSample1.ino
#include <TinyScreen.h>
#include "SoundStream.hpp"
#include "TwinkleBeep01.h"
TinyScreen tiny_screen = TinyScreen(TinyScreenPlus);
int wave_sample;
void setup()
{
tiny_screen.begin();
tiny_screen.setBitDepth(TSBitDepth8);
tiny_screen.setBrightness(8);
tiny_screen.setFont(liberationSansNarrow_12ptFontInfo);
tiny_screen.fontColor(TS_8b_White, TS_8b_Black);
InitializeSoundStream();
wave_sample = 0;
}
void loop()
{
tiny_screen.setCursor(0,0);
tiny_screen.print("Now playing!");
UpdateSoundStream();
int writable_size = GetSoundStreamWritableSize();
int wave_size = static_cast<int>(sizeof(TwinkleBeep01_wave));
if(writable_size > 0)
{
size_t write_size = writable_size;
size_t left_size = wave_size - wave_sample;
if(left_size < writable_size)
{
write_size = left_size;
}
if(write_size > 0)
{
unsigned char* write_wave_buffer = new unsigned char[writable_size];
memcpy(write_wave_buffer, &TwinkleBeep01_wave[wave_sample], write_size);
wave_sample += write_size;
if(writable_size > left_size)
{
int left_write_size = writable_size - left_size;
if(left_write_size > 0)
{
wave_sample = 0;
memcpy(&write_wave_buffer[write_size], &TwinkleBeep01_wave[wave_sample], left_write_size);
wave_sample += left_write_size;
}
}
WriteSoundStream(write_wave_buffer);
delete [] write_wave_buffer;
}
if(wave_sample >= wave_size)
{
wave_sample = 0;
}
}
}
-SoundStream.hpp
#ifndef SOUNDSTREAM_HPP
#define SOUNDSTREAM_HPP
static const int SOUND_BUFFER_SIZE = 800;
static const int SOUND_BUFFER_SIZE_HALF = SOUND_BUFFER_SIZE / 2;
static const int SOUND_FREQUENCY = 11025;
void InitializeSoundStream();
void FinalizeSoundStream(void);
void UpdateSoundStream(void);
int GetSoundStreamWritableSize(void);
bool WriteSoundStream(const unsigned char* buffer);
#endif
-SoundStream.cpp
#include <TinyScreen.h>
#include "SoundStream.hpp"
unsigned char stream_buffer[SOUND_BUFFER_SIZE];
int sample_index;
int write_buffer;
bool request;
bool tcIsSyncing(void)
{
return TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;
}
void tcReset(void)
{
// Reset TCx
TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
while(tcIsSyncing());
while(TC5->COUNT16.CTRLA.bit.SWRST);
}
void InitializeSoundStream(void)
{
memset(stream_buffer, 0x80, SOUND_BUFFER_SIZE);
sample_index = 0;
write_buffer = 0;
request = false;
analogWrite(A0, 0);
// Enable GCLK for TCC2 and TC5 (timer counter input clock)
GCLK->CLKCTRL.reg = static_cast<unsigned short>((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)));
while(GCLK->STATUS.bit.SYNCBUSY);
tcReset();
// Set Timer counter Mode to 16 bits
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
// Set TC5 mode as match frequency
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1;
TC5->COUNT16.CC[0].reg = static_cast<unsigned short>((SystemCoreClock / SOUND_FREQUENCY - 1));
while(tcIsSyncing());
// Configure interrupt request
NVIC_DisableIRQ(TC5_IRQn);
NVIC_ClearPendingIRQ(TC5_IRQn);
NVIC_SetPriority(TC5_IRQn, 0);
NVIC_EnableIRQ(TC5_IRQn);
// Enable the TC5 interrupt request
TC5->COUNT16.INTENSET.bit.MC0 = 1;
while(tcIsSyncing());
// Enable TC
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
while(tcIsSyncing());
}
void FinalizeSoundStream(void)
{
// Disable TC5
TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
while(tcIsSyncing());
// Reset TCx
TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
while(tcIsSyncing());
while(TC5->COUNT16.CTRLA.bit.SWRST);
}
void UpdateSoundStream(void)
{
int play_buffer = 0;
if(sample_index >= SOUND_BUFFER_SIZE_HALF)
{
play_buffer = 1;
}
if(write_buffer == play_buffer)
{
request = true;
}
}
int GetSoundStreamWritableSize(void)
{
if(request == false)
{
return 0;
}
return SOUND_BUFFER_SIZE_HALF;
}
bool WriteSoundStream(const unsigned char* buffer)
{
int play_buffer = 0;
if(sample_index >= SOUND_BUFFER_SIZE_HALF)
{
play_buffer = 1;
}
write_buffer = 1 - play_buffer;
memcpy(&stream_buffer[SOUND_BUFFER_SIZE_HALF * write_buffer], buffer, SOUND_BUFFER_SIZE_HALF);
request = false;
return true;
}
#ifdef __cplusplus
extern "C"
{
#endif
void SoundStreamCallback(void)
{
while(DAC->STATUS.bit.SYNCBUSY == 1);
DAC->DATA.reg = static_cast<unsigned short>(stream_buffer[sample_index] << 2); // 8bit->10bit
while(DAC->STATUS.bit.SYNCBUSY == 1);
++ sample_index;
if(sample_index >= SOUND_BUFFER_SIZE)
{
sample_index = 0;
}
TC5->COUNT16.INTFLAG.bit.MC0 = 1;
}
void TC5_Handler(void) __attribute__ ((weak, alias("SoundStreamCallback")));
#ifdef __cplusplus
}
#endif
-TwinkleBeep01.h
// 11025Hz 8bit mono wavedata
const unsigned char TwinkleBeep01_wave[40045] =
{
128, 128, 122, 116, 113, 119, 139, 150, 152, 141, 105, 83, 79, 91, 138, 176,
178, 174, 128, 79, 76, 84, 132, 186, 196, 190, 151, 92, 76, 81, 112, 172,
197, 194, 159, 95, 64, 63, 80, 135, 178, 179, 168, 117, 67, 60, 68, 111,
:
:
:
続く
*実行結果 [#n36c708f]
#ref(SoundSample1.jpg,,,100%)
TwinkleBeep01_waveがループ再生されます。~
*サンプルプログラムのダウンロード [#s4bd33eb]
#ref(SoundSample1.zip)~