# ワークメモリ上の波形データをPCMストリームを利用して再生するサンプル

1998.5.12更新

## サンプルについて

このサンプルはワークメモリ既にある波形データをPCMストリームを利用して再生を行うプログラムです．

## サンプルの流れ

サウンドライブラリ・ドライバの初期化を行い，440Hzの音を再生する．
処理の流れとしては

1. PCMストリームポートのハンドルの取得（`sdPstmOpenPortPort`）
2. 使用するチャンネル分のメモリハンドルの取得（`sdMemBlkCreate`）
3. あらかじめある程度の波形データをサウンドメモリに転送（`sdPstmTransfer`）
4. PCMストリームの再生開始（`sdPstmPlay`）
5. 残りの波形データをサウンドメモリに転送（`sdPstmTransfer`）
6. 後処理．

となります．

## 注意点

本サンプルには下記の注意事項があります．

1. あらかじめ転送しておく波形データのサイズの修正
   あらかじめ転送しておく波形データのサイズを`PRE_TRANS_SZ`に設定しておく．
2. 残りの波形データの転送するサイズの修正
   残りの波形データを転送するサイズを`TRANS_SZ`に設定しておく．

## 実際のコーディング

実際のコーディングを下記に提示する．なお，このサンプルはエラーチェックをかなり省いています．

簡単なワークメモリ上の波形データのPCMストリームのサンプル
``/********************************************************* * * Sound Library Sample * * ワークメモリ上の波形データをPCMストリームで再生する．
*
*********************************************************/`
#include <sd_sg.h>
`/* 前もって転送しておく波形サイズ（単位はバイト） */`
#define PRE_TRANS_SZ (0x4000)
`/* 再生中に転送する波形サイズ（単位はバイト） */`
#define TRANS_SZ (0x1000)
SDE_ERR OnMemoryPCMStream(`
Sint8 ch_num, `チャンネル数（モノラルは０，ステレオは１）`
SDE_PCM_TYPE src_type, `波形データのタイプ．`
Uint16 src_freq, `サンプリング周波数（本当にサンプリング周波数が入っているわけではない）`
Void *ch0_src_adr, `チャンネル０（ステレオの場合は左チャンネル）の再生波形の先頭アドレス．`
Void *ch1_src_adr, `左チャンネルの再生波形の先頭アドレス（ステレオの場合のみ有功．モノラルの時は無視される）`
Sint32 src_sz) `波形サイズ（単位はバイト）`
` {
``SDPSTM` pstm_handle = NULL;
Sint32 seek_sz, ch;
Sint8 *seek_adr[ `SDD_PSTM_CH_MAX`];
`SDMEMBLK` memblk[ `SDD_PSTM_CH_MAX`];
Sint32 i;
`/*
* ローカル変数の初期化
*/`
for ( i = 0; i < [ `SDD_PSTM_CH_MAX`]; i++)
{`
memblk[ i] = NULL;
` }
seek_adr[ 0] = ch0_src_adr;
seek_adr[ 1] = ch1_src_adr;
`/*
* PCMストリームポートのハンドルを取得する
*/`
` #if PCM_CH==1`
`/*
* モノラルの場合
*/`
`/* リングバッファは０番使用する */`
if ( `sdPstmOpenPort`( &pstm_handle, PSTM_CH, 0) != `SDE_ERR_NOTHING`)
` #elif PCM_CH==2`
`/*
* ステレオの場合
*/`
`/* リングバッファは０番と１番を使用する */`
if ( `sdPstmOpenPort`( &pstm_handle, PSTM_CH, 0, 1) != `SDE_ERR_NOTHING`)
` #else
#error PCMチャンネルは１もしくは２のみ指定可能．
#endif `/* PCM_CH==1 */``
{`
`/* エラーが起こった */`
goto exit;
` }
`/*
* メモリブロックハンドルの取得
*/`
for ( ch = 0; ch < ch_num; ch++)
{`
if ( `sdMemBlkCreate`( &memblk[ ch]) != `SDE_ERR_NOTHING`)
{`
`/* エラーが起こった */`
goto exit;
` }
` }
`/*
* まずあらかじめリングバッファを少し埋めておく
*/`
for ( ch = 0; ch < ch_num; ch++)
{`
Bool flg;
`/* 念の為転送していいかどうか調べてみる */`
do {`
`sdPstmIsTransfer`( pstm_handle, ch, PRE_TRANS_SZ, &flg);
} while ( flg != true);`
`/* 実際に転送 */`
`sdMemBlkSetPrm`( memblk[ ch], seek_adr[ ch], PRE_TRANS_SZ, `SDD_MEMBLK_SYNC_FUNC`, NULL);
`sdPstmTransfer`( pstm_handle, memblk[ ch]);
`/* シーク位置の移動 */`
seek_adr[ ch] = &seek_adr[ ch][ PRE_TRANS_SZ];
` }
`/*
* PCMストリーム再生開始
*/`
if ( `sdPstmPlay`( pcm_handle, src_type, src_freq, 0) != `SDE_ERR_NOTHING`) {`
`/* エラーが起こった */`
goto exit;
` }
`/*
* 必要分だけ波形データを逐次転送する
*/`
for ( seek_sz = PRE_TRANS_SZ; seek_sz < src_sz; seek_sz += TRANS_SZ)
{`
for ( ch = 0; ch < ch_num; ch++)
{`
Bool flg;
`/* 念の為転送していいかどうか調べてみる */`
do {`
`sdPstmIsTransfer`( pstm_handle, ch, TRANS_SZ, &flg);
} while ( flg != true);`
`/* 実際に転送 */`
`sdMemBlkSetPrm`( memblk[ ch], seek_adr[ ch], TRANS_SZ, `SDD_MEMBLK_SYNC_FUNC`, NULL);
`sdPstmTransfer`( pstm_handle, memblk[ ch]);
`/* シーク位置の移動 */`
seek_adr[ ch] = &seek_adr[ ch][ TRANS_SZ];
` }
` }
` exit:
`if ( pstm_handle != NULL)
{`
`/* PSTMポートの再生停止 */`
`sdPstmStop`( pcm_handle);
`/* PSTMポートのハンドルの破棄 */`
`sdPstmClosePort`( pstm_handle);
` }
for ( i = 0; i < ch_num; i++)
{`
if ( memblk[ i] != NULL)
{`
`sdMemBlkDestory`( memblk[ i]);
` }
` }
` }
`/*EOF*/`
`

## サンプルの応用

現在メモリブロックハンドルにのコールバック関数`SDD_MEMBLK_SYNC_FUNC`を使用していますが，独自でコールバック関数を用意することで波形データの転送終了を待たずに別の処理をすることが可能になります（その場合上記のサンプルよりかなり複雑な処理になります）