サウンドを再生するだけのプログラムです。どうしても長くなってしまう・・・。
本当はすべて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を合成する必要がある。
#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; } }}
#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
#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
// 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, : : : 続く
TwinkleBeep01_waveがループ再生されます。