編集履歴一覧に戻る
chrmlinux03のアイコン画像

chrmlinux03 が 2024年01月27日21時27分03秒 に編集

色々掲載

タグの変更

+

Module

+

ライブラリ

+

8Track再生

本文の変更

# はじめに こんにちわっ リナちゃん X@chrmlinux03 です

-

``` このページは SPRESENSE2023コンテストで作った部品を紹介するものです 最初に 0.はじめに をお読みいただければ幸いです ```

+

++このページは SPRESENSE2023コンテストで作った部品を紹介するものです 最初に [開発まとめ](https://elchika.com/article/e96011d0-d280-49e2-a68c-dd3c183a204e/) をお読みいただければ幸いです++ # このモジュールで実現したかった事 ### - 誰も成しえてない8Track の音声をSPRESENSEで鳴らしたかった ### - Player0 と Player1 が Stero + Streo なんで 合計 4Track しか同時再生出来ないし💦 それの倍くらいの 8Track を再生したかった ### - 出来ればそれに 色と光によるエフェクトするための仕組みを付けたかった @[twitter](https://twitter.com/chrmlinux03/status/1536467975641104384?s=20)

# 用意するもの | 部品名 | 販売先 | 価格 |御提供品| | -------- | -------- | -------- |-------- | | スピーカ or 有線イヤホン|ダイソー|330円|ー|

+

|決して負けない強い力|誰でも|プライスレス|ー|

-

# なかなか綺麗に鳴るぢゃん SPRESENSE をご提供頂きまず最初に行うのはAudio機能ではだろ

+

# 知ってる声ファイル形式を列挙 ## MP3 (MPEG Audio Layer III) - 特徴: 高い圧縮率を持ち、ファイルサイズを小さく保ちがら音質を劣化させなに設計されている - 用途: インターネット上での音楽のストリーミングやダウンロード、携帯音楽プレーヤーでの再生など

+

## WAV (Waveform Audio File Format) - 特徴: 非圧縮形式であり、高品質な音声を保持 - 用途: 音楽制作、オーディオ編集、効果音の保存、CDオーディオトラックなど ## AAC (Advanced Audio Coding) - 特徴: 高い圧縮効率と良好な音質を兼ねる - 用途: iTunesでの音楽ファイル、iPhoneやiPadでの音楽再生、オンライン音楽配信など ## AIFF (Audio Interchange File Format) - 特徴: 非圧縮形式であり、高品質な音声を保持する - 用途: 音楽制作、オーディオ編集、Mac用の音楽アプリケーションでの使用など ## WMA (Windows Media Audio) - 特徴: マイクロソフトによって開発された形式で、MP3よりも高い圧縮率を持ちつつ、良好な音質を提供する - 用途: Windows Media Playerでの音楽再生、Windowsプラットフォーム上でのメディア再生など ## FLAC (Free Lossless Audio Codec) - 特徴: 無損失圧縮形式で、圧縮率が高くても音質の劣化がない - 用途: 音楽制作、オーディオアーカイブ、高品質な音楽再生機器での使用など ## PCM (Pulse Code Modulation) - 特徴: 音声をアナログからデジタルに変換するための基本的な方式で、音声信号を一定の間隔でサンプリングし、各サンプルの振幅を数値で表現する - 非圧縮形式であり、高品質な音声を保持する - 用途: CDオーディオ、音声録音、オーディオ制作など ## RAW (Raw Audio File) - 特徴: 圧縮やエンコードが一切行われていない生の音声データを含む 最高の音質が得られるが、ファイルサイズは大きくなる - 用途: 高品質な音声データの保存や処理、オーディオエンジニアリングなど ## PCM と RAW の概念 **RAW**と**PCM**は異なる概念であり、一般的に混同されることがある ### PCM (Pulse Code Modulation): - PCMは、アナログ信号をデジタル信号に変換するための一般的な手法 - 音声信号を一定の間隔でサンプリングし、各サンプルの振幅を数値で表現する - PCMはデジタル音声の基本形式であり、無損失のデジタルオーディオの表現に使用される - WAVやAIFFなど、多くの音声フォーマットがPCMを使用す ### RAW (Raw Audio File): - RAWオーディオファイルは、通常、圧縮やエンコードが一切行われていない生の音声データを含む形式である - RAWファイルは単なる生のバイナリデータであり、ファイル内のデータ構造やメタデータが少ないか一切ないことが大体ある - RAW形式は一般的に、デジタルオーディオデータをそのまま保存するために使用され、他の形式に変換する前の生データを含むことがたまにある ++要するに、PCMはデジタルオーディオの表現方法であり、RAWは通常、生の音声データを含む未加工の形式 ただし、PCMデータをRAWと呼ぶこともあるが、その場合は通常、PCMデータが特定のヘッダーやメタデータなしに生データとして保存されている状態を指す++ ## とりま MP3/WAV/RAW は再生できる事がわかった 深い事は気にしない 🚬 # 面倒な事は良いとして、音綺麗に鳴るぢゃん SPRESENSE を御提供頂きまず、最初に行うのはAudio機能ではないだろうか

私も前回まず最初に Audio機能に魅了された一人である

+

全てはここから始まった٩(ˊᗜˋ*)و @[twitter](https://twitter.com/chrmlinux03/status/1557610395917221888?s=20) ここまで可能になった٩(ˊᗜˋ*)و @[twitter](https://twitter.com/chrmlinux03/status/1735246091812217301?s=20) # 本家のライブラリが難解過ぎて涙 一番簡単な 1個のmp3ファイルを鳴らす ino ファイルで 度肝を抜かれた💦 え?こんなに設定しないと駄目なの? goto 使わないと駄目なの? 先は長いな 🚬 ```c++:Player.ino #define MP3FILENAME "Sound.mp3" #include <SDHCI.h> #include <Audio.h> SDClass theSD; AudioClass *theAudio; File myFile; bool ErrEnd = false; static void audio_attention_cb(const ErrorAttentionParam *atprm) { puts("Attention!"); if (atprm->error_code >= AS_ATTENTION_CODE_WARNING) { ErrEnd = true; } } void setup() { while (!theSD.begin()) { Serial.println("Insert SD card."); } theAudio = AudioClass::getInstance(); theAudio->begin(audio_attention_cb); puts("initialization Audio Library"); theAudio->setRenderingClockMode(AS_CLKMODE_NORMAL); theAudio->setPlayerMode(AS_SETPLAYER_OUTPUTDEVICE_SPHP, AS_SP_DRV_MODE_LINEOUT); err_t err = theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_MP3, "/mnt/spif/BIN", AS_SAMPLINGRATE_AUTO, AS_CHANNEL_STEREO); if (err != AUDIOLIB_ECODE_OK) { printf("Player0 initialize error\n"); exit(1); } myFile = theSD.open(MP3FILENAME); if (!myFile) { printf("File open error\n"); exit(1); } printf("Open! 0x%08lx\n", (uint32_t)myFile); err = theAudio->writeFrames(AudioClass::Player0, myFile); if ((err != AUDIOLIB_ECODE_OK) && (err != AUDIOLIB_ECODE_FILEEND)) { printf("File Read Error! =%d\n", err); myFile.close(); exit(1); } puts("Play!"); theAudio->setVolume(-160); theAudio->startPlayer(AudioClass::Player0); } void loop() { puts("loop!!"); int err = theAudio->writeFrames(AudioClass::Player0, myFile); if (err == AUDIOLIB_ECODE_FILEEND) { printf("Main player File End!\n"); } if (err) { printf("Main player error code: %d\n", err); goto stop_player; } if (ErrEnd) { printf("Error End\n"); goto stop_player; } usleep(40000); return; stop_player: theAudio->stopPlayer(AudioClass::Player0); myFile.close(); theAudio->setReadyMode(); theAudio->end(); exit(1); }

```

-

画像 初めてのAudio

+

> SPRESENSE->Arduino Ide->Audio->example->Player.ino ## 音声ファイル再生で(最低限)設定する内容 ### システム内にDSPをインストールし再生を行う SDカードもしくは SPIF上にインストールを行う

```

-

# 本家のライブラリが難し過ぎて涙

+

err_t err = theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_MP3, "/mnt/spif/BIN", AS_SAMPLINGRATE_AUTO, AS_BITLENGTH, AS_CHANNEL_STEREO);

-

# Audio0 とか Audio1 とかなんだろう

+

``` ### ノーマルかハイレゾか? サンプリング周波数 44.1までは AS_CLKMODE_NORMAL それ以上は AS_CLKMODE_HIRES ``` theAudio->setRenderingClockMode(mode); ``` ### 再生するファイル形式は? - MP3:AS_CODECTYPE_MP3 ``` 32kHz (AS_SAMPLINGRATE_32000) 44.1kHz (AS_SAMPLINGRATE_44100) 48kHz (AS_SAMPLINGRATE_48000) AS_SAMPLINGRATE_AUTO 指定するとサンプリングレート ``` - WAV/RAW:AS_CODECTYPE_WAV ``` 32kHz (AS_SAMPLINGRATE_32000) 44.1kHz (AS_SAMPLINGRATE_44100) 48kHz (AS_SAMPLINGRATE_48000) 96kHz (AS_SAMPLINGRATE_96000) 192kHz (AS_SAMPLINGRATE_192000) サンプリングオートは無い ``` ### 何ビットのデータか? ``` AS_BITLENGTH_8 AS_BITLENGTH_16 AS_BITLENGTH_24 ``` ### ステレオかモノラルか? ``` AS_CHANNEL_STEREO AS_CHANNEL_MONO ``` ## Volume - vol = -1020 ~ 120 ``` theAudio->setVolume(vol, vol, vol); ``` # Audio0 とか Audio1 とか Player0 とか Player1 とかなんだろう てか theAudio->writeFrames が2回書かれている なんだろうね💦 Audio0 / Audio1 / Player0 / Player1 とかもよくわからない さらに setVolume メソッドに至っては ``` Audio0->setVolume(vol, vol, vol); Audio1->setVolume(vol, vol, vol); ``` え💦 4ch 再生して 個別に音量調整できるの? スピーカーは 2個だからなんだろうか....(まだわかって居ないのが事実) # まず Player0 を解析 ``` #include <Audio.h> AudioClass *theAudio; theAudio = AudioClass::getInstance(); err = theAudio->writeFrames(AudioClass::Player0, myFile); theAudio->startPlayer(AudioClass::Player0); ``` この辺りから Player0 は theAudio0 を指定するだけのID番号として考える myFile はなんだろう addrs をPrintしても同じ番地が返ってくるから スレッドセーフされた特殊な File myFile と考える

# うーん Streo と 2track は違うのか

-

# 0x0000 | 0x0101 = 0x0101 だとなんか音が濁る # 0x0000 + 0x0101 = 0x0101 が正しいのかもやも # ライブラリ等

+

@[twitter](https://twitter.com/chrmlinux03/status/1736968449912627557?s=20) んぢゃ完全に 別々な音声を 8Track分作成する必要があるね 覚えていられないかも💦 # 基本を raw ファイルを再生するものとして考える RAWファイルは単なる生データでありバイナリデータである WAVファイルも同様にほぼ生データではあるがヘッダにその生データの - サンプリング周波数 - ビットレングス - チャンネル数 等の情報が含まれる ![キャプションを入力できます](https://camo.elchika.com/3a3a87096cafb120c916cebf143de7756359c03a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f64333233316338312d653833632d343238302d626532332d626563356135636664623637/) > https://learn.microsoft.com/ja-jp/windows-hardware/drivers/audio/extensible-wave-format-descriptors RAWファイルとして保存したものは上記ヘッダ情報は存在していないので それを知らないとほぼ再生する事は出来ないのである💦 # ぢゃファイル名にその情報入れちゃう? リナちゃんらしく単純な発想であった💦 例えば "192KS24.Rain.raw" というファイル名をシステムに食わせた場合 - サンプリングレート : 192000Hz - 再生チャンネル:ステレオ - ビットレート:24bit - 音声コーデック:RAW(WAV) という内容を FMT_T 構造体に返してくれる関数である٩(ˊᗜˋ*)و ```c++:抜粋 typedef struct { char fname[FILELEN_MAX]; int8_t codec; int32_t freq; int8_t bitlen; int8_t ch; int16_t mode; File file; } FMT_T; //============================================== // // getFmt // - Extracts audio file format information from the file name. // - Input: fmt - Pointer to the FMT_T structure. // - Return: None // //============================================== void getFmt(FMT_T *fmt) { String fname = String(fmt->fname); fmt->codec = fname.endsWith(".mp3") ? AS_CODECTYPE_MP3 : AS_CODECTYPE_WAV; if (fmt->codec == AS_CODECTYPE_MP3) { fmt->freq = AS_SAMPLINGRATE_AUTO; fmt->ch = 2; } else { fmt->freq = fname.substring(0, 3).toInt() * 1000; if (fmt->freq == 44000) fmt->freq = 44100; fmt->ch = (fname.charAt(4) == 'S') ? 2 : ((fname.charAt(4) == 'M') ? 1 : (isdigit(fname.charAt(4)) ? (fname.charAt(4) - '0') : 0)); fmt->bitlen = fname.substring(5, 7).toInt(); } } ``` また単純に 👉[SoundSerenity](https://elchika.com/article/d23c6bb2-2f8f-44c1-ade2-1e54982f03f5/) 等で使用する際には ``` #define PLAYING_FILENAME "044K416.soundSerenity.raw" const uint8_t numTracks = PLAYING_FILENAME[4] - '0'; ``` のように、これから作成する numTracks(仮想フェーダーの数) を一瞬で得る事が出来るという さらに "044K816/sound1.raw" のようなフォルダ構造でも解析できちゃうという 素敵なファイル名ヘッダが出来上がった訳である # DSP への転送手順 ここからが長かった💦 ## WAVファイル転送にヒントがあった ![player_wav](https://camo.elchika.com/e2e509e162acdb26f07c7a384746a210bb3979a6/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f62363434333664372d363932632d343864302d383637622d333934323363343761643738/) このファイルを見つけた時 どれだけ嬉しかったことか💕 ## 何故 setup() で writeFrame するのか loop は意味が分かる 音声を再生する訳なので loop の中で何回も何回もファイルを読み取ってそれを DSP に渡さないとそりゃ無理です 👉https://elchika.com/article/b50283d5-a148-4ed4-81ce-43b5a1f1d60c/ 44.1KHz 16bit Streo 3分の場合 31メガバイトのデータを回さないと駄目ですから💦 しかも1度に送れる量が決まってる ## 結論 = simpleFIFOの予備充填 ![Enqueue/Dequeue](https://camo.elchika.com/85d11d26b6e492b39b0a0b60ad90dace70a480bf/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f39336334623836302d313666322d346530312d383765612d633731366165623034373431/) > https://ja.wikipedia.org/wiki/FIFO DSPは DMA(Direct Memory Access) と言う機能で ある領域のデータを CPU の力を使わないで 音声デコーダに送ってくれる仕組みらしい 従って ある程度は loop の前に一杯にしておかないと CPU が動いている時に無くなってしまう(勝手に出ちゃうから) simpleFIFO は 先入れ先出しと言うルールにより構成されているので 先に入ったデータは DMA が勝手に再生しちゃうって事 ## 結論 = simpleFIFOの本充填 だから setupが終わった瞬間に theAudio->startPlayer(AudioClass::Player0); を実行し loop で theAudio->writeFrames(AudioClass::Player0, myFile); し続ける必要があるらしい ![DMAとは](https://camo.elchika.com/cf8a477f9206891b2301f5312e70956df7709635/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f34303865636665642d373232632d346531342d616533302d333838313831306235346335/) >https://edn.itmedia.co.jp/edn/articles/1608/18/news015.html # 6144バイトの謎 @[twitter](https://twitter.com/chrmlinux03/status/1740513417432371655?s=20) なぜ 6144バイトなのかやっとわかった んぢゃコード書いとく? デバッグも仕込もうねっ # HightLevel / ObjectLevel / LawLevel API ![DSP API](https://camo.elchika.com/818fd2192d9cada45a645c2787203c475931be5d/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f66383330313163342d386262652d343237622d623336332d656233626630623762326562/) 今回のコードは HightLevel ですね 基本的なコードしか使ってないし # コード ```c++:spre.AudioLib.hpp //============================================== // // Program Information // - Name: spre.AudioLib.hpp // - Date/Author: 2023/12/20 @chrmlinux03 // - Update/Author: 2023/12/20 @chrmlinux03 // //============================================== #include <SDHCI.h> #include <Audio.h> SDClass theSD; //============================================== // // Definitions // - Definitions for file length, count, channel count, etc. // //============================================== #define FILELEN_MAX (128) #define FILECNT_MAX (2) #define CHCNT_MAX (2) #define TRACK_MAX (16) #define BIT12_MAX (4096 - 1) #define BIT11_MAX (2048 - 1) #define BIT10_MAX (1024 - 1) #define PERCENT_MAX (100.0) #define SCALE_MAX (1.0) #define SCALE_MIN (0.0) #define SCALE_NEG (-1.0) #define VOLUME_MIN (-1000) #define VOLUME_MAX (0) #define VOLUME_DEF (0) #define AUDIO_BUFSIZE (6144) #define AUDIO_PRESTOREFRAME_SIZE (120) #define AUDIO_NOWSTOREFRAME_SIZE (60) #define DSP_PATH "/mnt/spif/BIN" //============================================== // // Redefinitions // - Redefinitions for audio-related constants. // //============================================== #define AUDIO ((AudioClass::PlayerId)0) #define AUDIO0 ((AudioClass::PlayerId)0) #define AUDIO1 ((AudioClass::PlayerId)1) #define CLOCK_NORMAL (AS_CLKMODE_NORMAL) #define CLOCK_HIRESO (AS_CLKMODE_HIRES) //============================================== // // Structs // - Definition of various data structures. // //============================================== typedef struct { int16_t master; int16_t audio0; int16_t audio1; } VOLUME_T; typedef struct { char fname[FILELEN_MAX]; int8_t codec; int32_t freq; int8_t bitlen; int8_t ch; int16_t mode; File file; } FMT_T; typedef struct { uint8_t fcnt; FMT_T fmt[FILECNT_MAX]; } SYS_T; //============================================== // // Global Variables // - Declaration of global variables. // //============================================== //float fbuf[CHCNT_MAX][AUDIO_BUFSIZE / sizeof(float)]; bool ErrEnd = false; bool useAudio = false; bool debugAudio = false; bool useEffect = false; SYS_T sys; VOLUME_T vol = {VOLUME_DEF, VOLUME_DEF, VOLUME_DEF}; float trackVolume[TRACK_MAX]; //============================================== // // getFmt // - Extracts audio file format information from the file name. // - Input: fmt - Pointer to the FMT_T structure. // - Return: None // //============================================== void getFmt(FMT_T *fmt) { String fname = String(fmt->fname); fmt->codec = fname.endsWith(".mp3") ? AS_CODECTYPE_MP3 : AS_CODECTYPE_WAV; if (fmt->codec == AS_CODECTYPE_MP3) { fmt->freq = AS_SAMPLINGRATE_AUTO; fmt->ch = 2; } else { fmt->freq = fname.substring(0, 3).toInt() * 1000; if (fmt->freq == 44000) fmt->freq = 44100; fmt->ch = (fname.charAt(4) == 'S') ? 2 : ((fname.charAt(4) == 'M') ? 1 : (isdigit(fname.charAt(4)) ? (fname.charAt(4) - '0') : 0)); fmt->bitlen = fname.substring(5, 7).toInt(); } } //============================================== // // addFile // - Adds a file to the system file list and prints its format information. // - Input: fname - File name to be added. // - Return: int16_t - Result code (always 0). // //============================================== int16_t addFile(char *fname) { FMT_T fmt; strcpy(fmt.fname, fname); getFmt(&fmt); sys.fmt[sys.fcnt] = fmt; Serial.printf("[%s] freq:%d bitlen:%d ch:%d \n", fname, fmt.freq, fmt.bitlen, fmt.ch); sys.fcnt++; return 0; } //============================================== // // audio_attention_cb // - Callback function for audio library attention events, particularly errors. // - Input: atprm - Pointer to the ErrorAttentionParam structure. // - Return: None // //============================================== static void audio_attention_cb(const ErrorAttentionParam *atprm) { Serial.printf("Attention!"); if (atprm->error_code >= AS_ATTENTION_CODE_WARNING) { ErrEnd = true; } } //============================================== // // setupAudio // - Initializes the audio library and sets rendering mode and output device. // - Input: playerid - ID of the audio player to be initialized. // mode - Rendering mode (AS_CLKMODE_NORMAL or AS_CLKMODE_HIRESO). // - Return: int16_t - Result code (always 0). // //============================================== int16_t setupAudio(AudioClass::PlayerId playerid, int16_t mode) { int16_t rtn = 0; AudioClass *theAudio = AudioClass::getInstance(); theAudio->begin(audio_attention_cb); theAudio->setRenderingClockMode(mode); theAudio->setPlayerMode(AS_SETPLAYER_OUTPUTDEVICE_SPHP, AS_SP_DRV_MODE_LINEOUT); Serial.printf("Initialization Audio Library\n"); memset(&sys, 0x0, sizeof(sys)); return rtn; } //============================================== // // frameWriteFromBuffer // // This function writes audio frames from a buffer to the specified audio player. // // Inputs: // - playerid: The ID of the target audio player. // - file: The File object representing the source audio file. // - storeFrames: The number of frames to be written. // // Outputs: // - err_t: An error code (0 if the operation is successful). // //============================================== err_t frameWriteFromBuffer(AudioClass::PlayerId playerid, File file, uint32_t storeFrames) { err_t err = 0; AudioClass *theAudio = AudioClass::getInstance(); uint8_t buf[AUDIO_BUFSIZE]; int16_t readedBytes = 0; uint32_t remainBytes = file.size() - file.position(); for (uint32_t i = 0; i < storeFrames; i++) { if (remainBytes == 0) { file.seek(0); remainBytes = file.size(); } memset(buf, 0, AUDIO_BUFSIZE); readedBytes = file.read(buf, min(remainBytes, AUDIO_BUFSIZE)); remainBytes -= readedBytes; int16_t *audioData = (int16_t *)buf; int32_t samplesPerFrame = readedBytes / (sizeof(int16_t) * numTracks); for (uint32_t j = 0; j < samplesPerFrame; j++) { for (int tr = 0; tr < numTracks; tr++) { int32_t result = (int32_t)audioData[j * numTracks + tr] * trackVolume[tr]; audioData[j * numTracks + tr] = (int16_t)(min(max(result, INT16_MIN), INT16_MAX)); } for (int tr = 0; tr < numTracks; tr += numTracks / 2) { for (int ch = 0; ch < numTracks / 2; ch++) { int32_t result = 0; for (int subTr = 0; subTr < numTracks / 2; subTr++) { result += audioData[j * numTracks + tr + subTr]; } audioData[j * numTracks / (numTracks / 2) + tr / (numTracks / 2) + ch] = (int16_t)(min(max(result, INT16_MIN), INT16_MAX)); } } } uint32_t writeBytes = readedBytes / (numTracks / 2); err = theAudio->writeFrames(playerid, (uint8_t *)buf, writeBytes); if (debugAudio) { Serial.printf(" [frameWriteFromBuffer(%d)] err:%d store:%d remains:%s readeds:%s writes:%s\n", playerid, err, i, fmtC(remainBytes).c_str(), fmtC(readedBytes).c_str(), fmtC(writeBytes).c_str()); } if (err) { remainBytes += readedBytes; file.seek(file.position() - readedBytes); err = 0; break; } } return err; } //============================================== // // setupPlayer // - Initializes the audio player for a specific player ID. // - Input: playerid - ID of the audio player. // - Return: err_t - Error code (0 if successful). // //============================================== err_t setupPlayer(AudioClass::PlayerId playerid) { err_t err = 0; File file; AudioClass *theAudio = AudioClass::getInstance(); file = theSD.open(sys.fmt[playerid].fname); if (!file) { Serial.printf("can't open [%s]\n", sys.fmt[playerid].fname); return; } sys.fmt[playerid].file = file; err = theAudio->initPlayer(playerid, sys.fmt[playerid].codec, DSP_PATH, sys.fmt[playerid].freq, sys.fmt[playerid].bitlen, (sys.fmt[playerid].ch >= 2) ? 2 : sys.fmt[playerid].ch); Serial.printf("theAudio(%d)->initPlayer err:%d\n", playerid, err); if (err != AUDIOLIB_ECODE_OK) { Serial.printf("player%d initialize error\n", playerid); return err; } err = frameWriteFromBuffer(playerid, file, AUDIO_PRESTOREFRAME_SIZE); theAudio->setVolume(vol.master, vol.audio0, vol.audio1); theAudio->startPlayer(playerid); Serial.printf("theAudio(%d)->startPlayer err:%d\n", playerid, err); return err; } //============================================== // // loopPlayer // - Continuously writes audio frames to the player to keep it playing. // - Input: playerid - ID of the audio player. // - Return: err_t - Error code (0 if successful). // //============================================== err_t loopPlayer(AudioClass::PlayerId playerid) { err_t err; File file = sys.fmt[playerid].file; err = frameWriteFromBuffer(playerid, file, AUDIO_NOWSTOREFRAME_SIZE); return err; } //============================================== // // endAudio // - Closes the file associated with the audio player. // - Input: playerid - ID of the audio player. // - Return: err_t - Error code (0 if successful). // //============================================== err_t endAudio(AudioClass::PlayerId playerid) { err_t err = 0; File file = sys.fmt[playerid].file; file.close(); return err; } ``` - 0x0000 | 0x0101 = 0x0101 だとなんか音が濁る - 0x0000 + 0x0101 = 0x0101 が正しいのかもやも そのあたりが永遠の謎 ```c++: 抜粋 #define AUDIO_PRESTOREFRAME_SIZE (120) #define AUDIO_NOWSTOREFRAME_SIZE (60) ``` これで setup時に DSP に転送するFRAMEの数と loop時に DSP に転送するFRAMEの数を操作できます # ある意味一番大事なこと = ファイルの作り方 RAWファイルはRAWファイルでも実は 41.1KHz 16bit (CDと同じクオリティ) ですけど Track数 を変えています ここで重要なのは"RAWファイルの名前"❣ - 041K216.soundSerenity.raw:2Track - 041K416.soundSerenity.raw:4Track - 041K816.soundSerenity.raw:8Track # Audacity起動 ![Audacity](https://camo.elchika.com/317b728bf6fb7f0e3086370e1542be00182f4662/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f63346630643735632d393061612d343238652d613931622d396266636231336466363962/) 詳しい使い方は私も良く分からないので 適当に処理しましたが💦 ご自分で モノラル(1Track) なファイルを8個用意するか インターネットに上に存在する "無料環境音" などを持ってきて モノラルにしてから Audacity に読み込ませる この時 重要なのは**同じ長さ(秒数)**にする事 ## 8ファイル読み込んだ所 ![8FIleRead](https://camo.elchika.com/467874c947a59522704189c99b025c8923d3311a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f30353235633838312d396130322d346338332d396235362d353332396262353930666466/) ## オーディオをエクスポート ![オーディオをエクスポート](https://camo.elchika.com/04d46856ddc17fc9a86425ef8ee0b1d3aac0d75c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f63306536316236312d306135352d346662332d396166642d646563333238643564396239/) ## 高度なミキシングオプション ![高度なミキシングオプション](https://camo.elchika.com/50e01299a62959f0cc4afe0831dbded3817f83af/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f64366466343430312d343239322d346333332d623638342d313136626139336130626234/) で、ファイルは作れます٩(ˊᗜˋ*)و ## ファイルイメージ ステレオのファイルが こういう並びのファイルだった場合 ``` [L][R] [L][R] [L][R] [L][R] : [L][R] ``` この特殊な RAWファイルは こういう並びになります💦 ``` [T1][T2][T3][T4][T5][T6][T7][T8] [T1][T2][T3][T4][T5][T6][T7][T8] [T1][T2][T3][T4][T5][T6][T7][T8] : [T1][T2][T3][T4][T5][T6][T7][T8] ``` 考えただけでも嫌になりますね(棒)

# 応用例

-

## SoundSerenity

+

👉[SoundSerenity](https://elchika.com/article/d23c6bb2-2f8f-44c1-ade2-1e54982f03f5/) ![SoundSerenity](https://camo.elchika.com/b0d85787f23923aed1fce6c408625a14df3b0f64/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f30396263623639342d646539352d343934372d386264302d383963653330363835666536/) 👉[tinyMixer](https://elchika.com/article/941cc548-c610-4856-80ca-da1df4d66655/) ![tinyMixer](https://camo.elchika.com/7045a81be8b32bff54c0fad66e481f90f51bd616/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f32613434653335632d663635352d346137642d623461622d633766666436376563366335/)

# 残務と今後の考察

+

次回 Audio0 + Audio1 で 16Track 再生でお会いしましょう~(謎

# さいごに ご清聴ありがとうございました

-

[[0.はじ]](https://elchika.com/) に戻る

+

++このページは SPRESENSE2023コンテストで作った部品を紹介するものです 最初に [開発まと](https://elchika.com/article/e96011d0-d280-49e2-a68c-dd3c183a204e/) をお読みいただければ幸いです++