• 首頁
  • 文件
  • 下載
  • 狀態
  • 常見問題
  • 郵件列表
  • 應用程式
  • 外部鏈結
  • 版權
  • 志願者
  • 聯繫

範例程式:播放


「CIOS音訊核心」當中,不論是語音合成(Text To Speech)、播放音樂或是圖形使用者介面的音效,都是使用這個功能。

如果您需要自己處理全部的流程,要使用 「CIOS音訊核心」播放功能,需要處理五個主要的部分:
  1. 導流管的繼承
  2. 選取音訊設備
  3. 設定音訊參數
  4. 啟動音訊驅動介面
  5. 處理音訊串流程序
如果您不需要自己處理啟動音訊設備及串流偵測的問題,那麼只需要簡單地使用Core當中的Play函式,並且實作導流管的繼 承即可。


virtual bool Play (Conduit      * conduit              ,
 CaDeviceIndex deviceId , int channelCount ,
                   CaSampleFormat sampleFormat         ,
                   int            sampleRate           ,
                   int            frameCount           ,
                   CaStreamFlags  streamFlags   =  0   ,
                   int            sleepInterval = 50 ) ;

參數
說明
conduit
導 流管。
deviceId
音訊設備編號。
channelCount
聲道數量。
sampleFormat
取樣格式。格式請參考《CaSampleFormat》。
sampleRate
取樣頻率。「CIOS音訊核心」支援的範圍為8000-192000,某些 音訊驅動介面可能不支援特定取樣頻率。
frameCount
每次取樣的數量,一般建議為0.1秒或0.2秒。
如果您的取樣頻率是44100或48000,那麼這個frameCount值一般建議為4410、8820 或是4800、9600。
streamFlags
串流狀態,一般為0,一般而言你不需要設定這個值,這是為某些特定音訊驅動 介面功能而給定的參數。
sleepInterval
偵測播放狀態的時間間隔,一般建議值為50~250微秒,盡可能不要太長。

使用範例

Core core ;
MyOwnConduit * conduit = new MyOwnConduit () ;
...
core . Play ( conduit , 1 , 2 , cafInt16 , 44100 , 4410 , 0 , 100 ) ;
...

如果您需要無阻塞的播放方式,請參考《播放音訊檔》當中的線緒實作方法。

導流管的繼承

導流管是「CIOS音訊核心」當中最重要的觀念之一,這個實作方式被集智作業系統當中其他系統廣泛地應用。

使用者如果需要自行供應音訊串流給音訊設備,那麼您必須自行繼承Conduit類別,實作一個自有的導流管。完整的繼承處理 方式,請參考《導流管的繼承處理》、《LinearConduit》或是《BridgeConduit》。

如果您只需要自行供應音訊數據來源,那麼導流管當中僅需處理兩個函式,put函式只要留空白或是直接返回Complete即可:

virtual int  obtain (void) = 0 ;
virtual void finish (ConduitDirection direction = NoDirection ,FinishCondition  condition = Correct ) = 0 ;

obtain函式處理StreamIO Output,處理的方式大體如下:

int MyOwnConduit::obtain (void)
{
  if ( ... condition is ready for audio feeding ... ) {
     ... copy your audio feeding into Output.Buffer ...
     // example code in BridgeConduit.cpp : 
Buffer . get ( Output . Buffer , Output . Total ( ) ) ;
     return Continue ;
  }
  if ( ... condition is audio feeding is completely empty ... ) {
    return Complete ;
  }
  if ( ... condition has something unexpected ... ) {
    return Abort ;
  }
  return Postpone ;
}

整個串流被結束時,Stream會主動呼叫finish。

void MyOwnConduit::finish (ConduitDirection direction,FinishCondition condition = Correct)
{
  if ( OutputDirection != direction ) return ;
  ... handle your own finishing conditions here ...
}

選取音訊設備

您需要透過取得DeviceInfo來得知音訊設備的細節,詳 細的設定請參考《DeviceInfo》。

如果您需要知道音訊設備的名稱:

GetDeviceInfo((CaDeviceIndex)deviceId)->name

如果您想知道這個設備的名稱編碼:

HostApi * hostApi = NULL ;
DeviceInfo * deviceInfo = NULL ;

deviceInfo = core . GetDeviceInfo (deviceId) ;
if ( NULL != deviceInfo ) {
  core . GetHostApi ( &hostAPi , deviceInfo -> hostType ) ;
  if ( NULL != hostApi ) {
    if ( HostApi::NATIVE == hostApi -> encoding() ) {
      // deviceInfo->name is native OS encoding
    } else
    if ( HostApi::UTF8 == hostApi -> encoding() ) {
      // deviceInfo->name is UTF8 encoding
    } else
    if ( HostApi::UTF16 == hostApi -> encoding() ) {
      // deviceInfo->name is UTF16 encoding
    }
  }
} ;

CaPlay當中的範例程式

void ListOutputDevices(void)
{
  Core core                                       ;
  core . Initialize ( )                           ;
  for (int i=0;i<core.DeviceCount();i++)          {
    DeviceInfo * dev                              ;
    dev = core.GetDeviceInfo((CaDeviceIndex)i)    ;
    if ( NULL != dev && dev->maxOutputChannels>0) {
      printf("%4d : %s\n",i,dev->name)            ;
    }                                             ;
  }                                               ;
  core . Terminate  ( )                           ;
}

一般而言,音訊設備必須事先得知並取得編號,如果您沒有設定音訊設備的話,系統會自動選用:

virtual CaDeviceIndex DefaultOutputDevice (void) ;

這個函式所設定的輸出設備。

由於某些作業系統底層音訊驅動程式實作的問題,這個輸出設備不見得可以正常運作。由於「CIOS音訊核心」使用這些底層的音訊驅動程式,如果輸出設備不能正常運作,在
「CIOS音訊核心」當中尋找解決途徑一般是徒勞無功的。

設定音訊參數

設定音訊參數需要設定StreamParameters, 詳細使用方法請見《StreamParameters》。

一般而言,僅需要在宣告的時候,使用以下宣告即可設定音訊參數:

StreamParameters outputParameters ( deviceId , channelCount , sampleFormat ) ;

取樣頻率的設定,必須在設備打開的時候設定,請見《啟動音訊驅動介面》這一部分。

啟動音訊驅動介面


啟動及停止音訊驅動介面,需要以下步驟:

Core core                                                                  ;
...
core . Initialize
( ) ;
//////////////////////////////////////////////////////////////////////////// ... configure output paraments here ... //////////////////////////////////////////////////////////////////////////// rt = core . Open ( &stream , NULL , &outputParameters , sampleRate , frameCount , streamFlags , conduit ) ; if ( ( NoError != rt ) || ( NULL == stream ) ) correct = false ; //////////////////////////////////////////////////////////////////////////// if ( correct ) { rt = core . Start ( stream ) ; if ( NoError != rt ) correct = false ;
////////////////////////////////////////////////////////////////////////// ... handle your stream process here ... ////////////////////////////////////////////////////////////////////////// if ( correct && ( 0 != core.IsStopped ( stream ) ) ) core.Stop ( stream ) ; } core . Close ( stream ) ; core . Terminate ( ) ;
...


處理音訊串流程序


音訊串流啟動以後,您需要偵測它是否被停止或是被啟動,下面兩個Core函式是您所需要的:

virtual CaError IsStopped (Stream * stream) ;
virtual CaError IsActive (Stream * stream) ;

範例:

while ( 1 == core . IsActive  ( stream ) ) {
  Timer :: Sleep ( sleepInterval ) ;
}

範例程式


/* Play conduit into specific device */
bool Core::Play ( Conduit      * conduit       ,
                  CaDeviceIndex  deviceId      ,
                  int            channelCount  ,
                  CaSampleFormat sampleFormat  ,
                  int            sampleRate    ,
                  int            frameCount    ,
                  CaStreamFlags  streamFlags   ,
                  int            sleepInterval )
{
  if ( NULL == conduit ) return false                                        ;
  ////////////////////////////////////////////////////////////////////////////
  StreamParameters INSP ( deviceId , channelCount , sampleFormat )           ;
  Stream         * stream  = NULL                                            ;
  CaError          rt                                                        ;
  bool             correct = true                                            ;
  ////////////////////////////////////////////////////////////////////////////
  Initialize ( )                                                             ;
  ////////////////////////////////////////////////////////////////////////////
  INSP . suggestedLatency = GetDeviceInfo(deviceId)->defaultLowOutputLatency ;
  ////////////////////////////////////////////////////////////////////////////
  rt = Open ( &stream                                                        ,
              NULL                                                           ,
              &INSP                                                          ,
              sampleRate                                                     ,
              frameCount                                                     ,
              streamFlags                                                    ,
              conduit                                                      ) ;
  if ( ( NoError != rt ) || ( NULL == stream ) ) correct = false             ;
  ////////////////////////////////////////////////////////////////////////////
  if ( correct )                                                             {
    rt = Start ( stream )                                                    ;
    if ( NoError != rt ) correct = false                                     ;
    while ( correct && ( 1 == IsActive  ( stream ) ) )                       {
      Timer :: Sleep ( sleepInterval )                                       ;
    }                                                                        ;
    //////////////////////////////////////////////////////////////////////////
    if ( correct && ( 0 != IsStopped ( stream ) ) ) Stop ( stream )          ;
  }                                                                          ;
  Close     ( stream )                                                       ;
  Terminate (        )                                                       ;
  return correct                                                             ;
}