OSDN Git Service

First commitment for the BlackTank LPC1769.
[blacktank/blacktank.git] / task_audio.c
1 /**
2  * @file task_audio.c
3  * @brief オーディオDMAに同期して動くタスク
4  * @details
5  * @ref dma_intr_handler からのSEM_I2SDMA セマフォ・シグナルを受けて
6  * しかるべきデータ・バッファを特定し、信号処理を行う。
7  *
8  * なお、このタスクはシステム起動時にはインアクティブである。
9  * 明示的にアクティベートして始めて動作を開始する。
10  *
11  * 内蔵I2SペリフェラルとDMAを使って外部のオーディオ・コーデック
12  * からデータを取り込み、内部でコピーして再度オーディオ・コーデックに
13  * 出力する。内部コピーは \ref process_audio 関数で行っており
14  * 内部を書き換えることでどのようなアルゴリズムでも実装できる。
15  *
16  * audio_taskはDMA割り込みハンドラ \ref dma_intr_handler からの
17  * シグナルに同期して動作し、ピンポンバッファを使ってDMAと
18  * ソフトウェア処理の並列化を図っている。
19  *
20  * このプログラムが使用しているコーデックはTI社のTLV320AIC23Bである。
21  * 初期化データ列は \ref codec_init ルーチンから送出している。
22  *
23  * なお、LPC1768のDMAは、何らかのDMA管理機構を使ってチャンネル割り当てを
24  * すべきだが、このプログラムではその操作を省いている。
25  */
26
27 #include <kernel.h>
28 #include <t_syslog.h>
29 #include <t_stdlib.h>
30 #include <LPC17xx.h>
31 #include "kernel_cfg.h"
32 #include "audio_common.h"
33 #include "audio_effect.h"
34 #include "i2s_subsystem.h"
35 #include "i2c_subsystem.h"
36 #include "codec_subsystem.h"
37 #include "testpin.h"
38 #include "task_audio.h"
39 #include "task_display.h"
40
41 #define MSG_TARGET(n) (((n) & 0xF000) >> 12)
42 #define MSG_VALUE(n)  (((n) & 0x0FFF) >>  0)
43
44 /**
45  * \param file ソースコードのファイル名
46  * \param line ソースコードの行番号
47  * \param expr サービスコールのテキスト表現
48  * \param ercd サービスコールの結果(エラー値)
49  * \brief サービスコールのエラーのログ出力を行う補助関数
50  * \details
51  * TOPPERS/ASPのサービスコールのエラー出力を行うためのサポート関数。
52  * \ref SVC_PERROR マクロの中から使う。
53  */
54 Inline void
55 svc_perror(const char *file, int_t line, const char *expr, ER ercd)
56 {
57     if (ercd < 0) {
58         t_perror(LOG_ERROR, file, line, expr, ercd);
59     }
60 }
61
62 /**
63  * \brief サービスコールのエラー出力マクロ
64  * \param expr サービスコールの式
65  * \details
66  * exprとして与えたサービスコールのソースコード上の表現と
67  * その実行結果を印字する。
68  * サービスコールに限らず値を持つ式ならなんでもよい。
69  */
70 #define SVC_PERROR(expr) svc_perror(__FILE__, __LINE__, #expr, (expr))
71
72 /**
73  * \brief DMAハンドラ
74  * \details
75  * このハンドラは全DMAに対して共通に呼ばれる.
76  *
77  * 呼ばれると、DMAのバッファ終了割り込みステータスを確認する。
78  * それがI2S DMAバッファの終了割り込みなら、割り込みをクリアして
79  * タスクに通知する。
80  * \todo
81  * DMAチャンネルの番号は決め打ちである。
82  * 適切なDMA管理機構を使った方式への変更が必要である。
83  */
84 void dma_intr_handler(intptr_t exinf)
85 {
86     i2s_dma_intr_handler();
87 }
88
89 /**
90  * \brief I2S用データ
91  * \details
92  * DMAのLLIやバッファなどはすべてこの変数にパッケージしている。
93  * こうすることで、グローバル空間に名前が散らかることを阻止できる。
94  */
95 struct I2S_AUDIO_DATA audio_data;
96
97 struct I2S_AUDIO_DATA* get_audio_data(void)
98 {
99     return &audio_data;
100 }
101
102 void task_audio(intptr_t exinf)
103 {
104     AUDIOSAMPLE *txbuf;
105     const AUDIOSAMPLE *rxbuf;
106     int index;
107     AUDIOSAMPLE lm_left, lm_right;
108     uint16_t msg;
109     effect_param_t effect_param;
110     void (*effect_func)(
111             const effect_param_t *param,
112             const AUDIOSAMPLE *in_left,
113             const AUDIOSAMPLE *in_right,
114             AUDIOSAMPLE *out_left,
115             AUDIOSAMPLE *out_right);
116
117     /*
118      * オーディオコーデックを初期化する。
119      */
120     i2c_init();
121     codec_init();
122
123     /*
124      * ペリフェラルを初期化する。
125      */
126     i2s_init();
127     i2s_dma_init(get_audio_data());
128
129     /*
130      * @todo
131      * i2s_start()を呼ぶとDMA割り込みの無限ループに陥る。
132      * 現象が発生するのはコールドスタートの時だけ。要調査。
133      */
134     i2s_start();
135
136     /*
137      * リアルタイム・ステータス用のテストピンを出力にする。
138      */
139     testpin_init();
140
141     effect_func = audio_effect_through;
142
143     while(1)
144     {
145         // 外部タスクからパラメータを取得する。
146         if (prcv_dtq(DTQ_AUDIOPARAM, (intptr_t *)&msg) == E_OK) {
147             switch (MSG_TARGET(msg)) {
148                 case AUDIO_PARAM_MODE:
149                     switch (MSG_VALUE(msg)) {
150                         case AUDIO_VALUE_MODE_THROUGH:
151                             effect_func = audio_effect_through;
152                             break;
153                         case AUDIO_VALUE_MODE_VOCAL_CANCEL:
154                             effect_func = audio_effect_vocal_cancel;
155                             break;
156                         case AUDIO_VALUE_MODE_FIR:
157                             effect_func = audio_effect_fir;
158                             break;
159                         case AUDIO_VALUE_MODE_IIR:
160                             effect_func = audio_effect_iir;
161                             break;
162                         default:
163                             syslog(LOG_NOTICE,
164                                     "Unknown effect mode %d.",
165                                     MSG_VALUE(msg));
166                             break;
167                     }
168                     break;
169                 case AUDIO_PARAM_VAR0:
170                     effect_param.var0 = MSG_VALUE(msg);
171                     break;
172                 case AUDIO_PARAM_VAR1:
173                     effect_param.var1 = MSG_VALUE(msg);
174                     break;
175                 case AUDIO_PARAM_VAR2:
176                     effect_param.var2 = MSG_VALUE(msg);
177                     break;
178                 case AUDIO_PARAM_VAR3:
179                     effect_param.var3 = MSG_VALUE(msg);
180                     break;
181                 default:
182                     break;
183             }
184         }
185
186         // DMAバッファ転送の終了を待つ。
187         wai_sem(SEM_I2SDMA);
188
189         // プログラムが使用してもよいバッファのアドレスを取得する。
190         txbuf = i2s_getTxBuf();
191         rxbuf = i2s_getRxBuf();
192
193         // 同期状態を示すためのテストピン信号を作成する。
194         testpin_tp1_write(1);
195
196         lm_left = 0;
197         lm_right = 0;
198         for (index = 0; index < AUDIOBUFSIZE; index+=2) {
199             effect_func(
200                     &effect_param,
201                     rxbuf + (index + 0), rxbuf + (index + 1),
202                     txbuf + (index + 0), txbuf + (index + 1));
203             lm_left = (lm_left >> 1) + (*(txbuf + (index + 0)) >> 1);
204             lm_right = (lm_right >> 1) + (*(txbuf + (index + 1)) >> 1);
205         }
206
207         // 同期状態を示すためのテストピン信号を作成する
208         testpin_tp1_write(0);
209     }
210 }
211