スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

--.--.-- | スポンサー広告

[Skype API] Skype Audio API example

Skype 2.6BETAで、Skype APIにオーディオストリームに直接アクセスするAPIができたというので触ってみました。

APIの概要はこちらにあるのですが、正直わかりにくいです。

Skypeからのサウンドストリームのサンプルを得るには、まず、Skype API経由で
ALTER CALL 123 SET_OUTPUT PORT="23456"
のようなコマンドを送る必要があります。

上記のコマンドだと、音がまったく聞こえなくなるので、以下のようにすれば
とりあえず今までの方法で音がスピーカーから聞こえます。
ALTER CALL 123 SET_OUTPUT SOUNDCARD="default" PORT="23456"

123の部分はコールID、23456はポートIDです。
ポートIDは、PC上で使われていないものを選んでください。以下のサンプルの
nPortNoのところに入れておく必要があります。

マイクのキャプチャも可能で、この場合はSET_OUTPUTの部分をSET_CAPTURE_MICに変えてください。


以下のコードは、残念ながら、そのまま動くサンプルじゃないのですが、参考になればと思っておいておきます。


This is a example of receive/send sound data via Skype Audio API, introduced in Skype 2.6BETA. Maybe this is not-so-good exmaple as windows threading, but you can grab some idea about Skype Audio API.
Sorry to say, this code will not work without any modification.

NOTE: You need Skype 2.6 BETA for Windows to input/output sound data.

First, you need to send a Skype API command, after a call is established.
ALTER CALL 123 SET_OUTPUT PORT="23456"
123 : call ID
23456 : TCP/IP port number (which is currently not used in your PC)
The SET_OUTPUT part can be SEY_CAPTURE_MIC to get sound from MIC.

If you want to hear sound from normal speaker in the same time, use command like this.
ALTER CALL 123 SET_OUTPUT SOUNDCARD="default" PORT="23456"



ちなみに入力・出力サウンドデータはすべて PCM mono 16bit 16kHz なので、2バイトが1サンプルにあたります。

input/output sound sound data format is PCM mono 16bit 16kHz, so every 2byte is 1 sound sample (-32768 to 32767).

Receiving sound samples form Skype:Skypeから出力を受け取る場合

class ListenThreadInfo : inherited from ThreadInfo : ThreadInfoの派生クラス

listen() -> accept() -> recv()




//class ThreadInfo

//functionarity : start/end a thread and lock sound buffer.
//機能 : スレッドを開始・終了する機能と、
// サウンドバッファを保持・ロックする機能を持ちます。

class ThreadInfo
{
public:
int nPortNo; // port number

SOCKET sock1; // socket
fd_set fds, readfds; // for select()

std::deque< short > vecSoundBuffer; // sound buffer itself
CRITICAL_SECTION csSoundBuffer; // lock for sound buffer

// thread info
int nThreadIndex;
HANDLE hThreadShutdownEvent;
HANDLE hThread;
bool bEndRequested;

ThreadInfo( int nPortNo_ )
: nThreadIndex( 0 )
, hThreadShutdownEvent( NULL )
, hThread( NULL )
, sock1( 0 )
, bEndRequested( false )
, nPortNo( nPortNo_ )
{
InitializeCriticalSection( &csSoundBuffer );
}

virtual ~ThreadInfo()
{
DeleteCriticalSection( &csSoundBuffer );
}

virtual void EndThreadBegin(); // begin thread termination process
virtual void EndThreadEnd(); // end thread termination process


void LockSoundBuffer() {
EnterCriticalSection( &csSoundBuffer );
}
void UnlockSoundBuffer() {
LeaveCriticalSection( & csSoundBuffer );
}

bool CreateThread(); // create thread to call this->ThreadFunc()

virtual void ThreadFunc( ThreadInfo *pThreadInfo ) {}

static void __cdecl ThreadFuncStatic(void *pObj);
};




class ListenThreadInfo : public ThreadInfo
{
public:

ListenThreadInfo( int nPortNo )
: ThreadInfo( nPortNo ) {}
virtual ~ListenThreadInfo() {}
virtual void EndThreadBegin();
virtual void EndThreadEnd();
virtual void ThreadFunc( ThreadInfo *pThreadInfo );
};

void ListenThreadInfo::ThreadFunc( ThreadInfo *_pThreadInfo ) {

ListenThreadInfo *pThreadInfo =
(ListenThreadInfo *) _pThreadInfo;

// create socket
pThreadInfo->sock1 = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr1;
addr1.sin_family = AF_INET;
addr1.sin_addr.S_un.S_addr = INADDR_ANY;

// port number
addr1.sin_port = htons(pThreadInfo->nPortNo);
bind(pThreadInfo->sock1, (struct sockaddr *)&addr1,
sizeof(addr1));

static char buf[10000];

while( true ) {

struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50 * 1000; // 50ms timeout

// ポートをLISTENする : listen to the port
int res = listen( pThreadInfo->sock1, 5 );
if ( pThreadInfo->bEndRequested ) goto exit;
if ( res != 0 ) {
// error
TRACE("LISTEN ERROR?\n");
Sleep(100);
continue;
}

while (true) {
struct sockaddr_in client;
int len = sizeof(client);
SOCKET sock = 0;

// accept
sock = accept(pThreadInfo->sock1,
(struct sockaddr *)&client, &len);
if ( pThreadInfo->bEndRequested ) goto exit;
if ( sock == INVALID_SOCKET ) {
TRACE("ACCEPT ERROR: INVALID SOCKET!!!\n");
Sleep(100);
break;
}

// fd_setの初期化します : initialize fd_set for select()
FD_ZERO(&pThreadInfo->readfds);
FD_SET(sock, &pThreadInfo->readfds);

// write to disk (if you want)
//HMMIO hWavFile = InitSaveWaveFile("c:\\tcp_output.wav");

memset(buf, 0, sizeof(buf));
while(true) {
if ( WaitForSingleObject(
pThreadInfo->hThreadShutdownEvent, 0 )
== WAIT_OBJECT_0 ) {
goto exit;
}
if ( pThreadInfo->bEndRequested ) goto exit;

memcpy(&pThreadInfo->fds, &pThreadInfo->readfds,
sizeof(fd_set));
int res = select(0, &pThreadInfo->fds, NULL, NULL, &tv);
if ( pThreadInfo->bEndRequested ) goto exit;
if ( res == 0 ) {
// timeout
Sleep(30);
continue;
}
// sock1に読み込み可能データがある場合のみ通過
// is there data to be read ?
if (! FD_ISSET(sock, &pThreadInfo->fds)) {
Sleep(30);
continue;
}

// サウンドデータ受信 : receive sound data
int nReadBytes = recv(sock, buf, sizeof(buf), 0);
if ( nReadBytes == 0 ) break;
if ( nReadBytes == SOCKET_ERROR ) {
TRACE("RECV ERROR:%d\n",WSAGetLastError());
Sleep(100);
break;
}

// push WAV data to internal buffer
signed short *pBufWave = (signed short *)buf;
pThreadInfo->LockSoundBuffer();
for ( int i=0 ; i < nReadBytes/2 ; i++ ) {
pThreadInfo->vecSoundBuffer.push_back( pBufWave[i] );
}
pThreadInfo->UnlockSoundBuffer();

// 送信の場合はこんなかんじ
// example of send, to INPUT port.
// use this instead of recv()
//int nSendBytes = send(sock, buf, sizeof(buf), 0);

if ( pThreadInfo->bEndRequested ) goto exit;

// write to disk (if you want)
//mmioWrite( hWavFile, ( char * ) buf, nReadBytes );
}
closesocket(sock);

if ( pThreadInfo->bEndRequested ) goto exit;

// write to disk (if you want)
//EndSaveWaveFile( hWavFile );
}
}
exit:
_endthread();
}




Skypeにサウンドデータを送信する場合:Send sound samples to Skype

command : ALTER CALL 123 SET_INPUT PORT="23457"


自前のデータをSkypeに入力する場合です。
vecSoundBufferに既にデータが入っている必要があります。

コマンド : ALTER CALL 123 SET_INPUT PORT="23457"
のような感じです。


class SendThreadInfo : inherited from ThreadInfo : ThreadInfoの派生クラス
You need some sound samples in vecSoundBuffer.

listen() -> accept() -> send()




class SendThreadInfo : public ThreadInfo
{
public:

SendThreadInfo( int nPortNo )
: ThreadInfo( nPortNo ) {}
virtual ~SendThreadInfo() {}
virtual void EndThreadBegin();
virtual void EndThreadEnd();
virtual void ThreadFunc( ThreadInfo *pThreadInfo );
};

void SendThreadInfo::ThreadFunc( ThreadInfo *pThreadInfo )
{
// 指定ポートにsendする : send to the specified port.

static short buf[640];

while( true ) {

struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50 * 1000; // 50ms

// ポートをLISTENする : listen to the port
int res = listen( pThreadInfo->sock1, 5 );
if ( pThreadInfo->bEndRequested ) goto exit;
if ( res != 0 ) {
// error
TRACE("LISTEN ERROR?\n");
Sleep(100);
continue;
}

while (true) {
struct sockaddr_in client;
int len = sizeof(client);
SOCKET sock = 0;

// accept
sock = accept(pThreadInfo->sock1,
(struct sockaddr *)&client, &len);
if ( pThreadInfo->bEndRequested ) goto exit;
if ( sock == INVALID_SOCKET ) {
TRACE("ACCEPT ERROR: INVALID SOCKET!!!\n");
Sleep(100);
break;
}

// fd_setの初期化します : initialize fd_set for select()
FD_ZERO(&pThreadInfo->readfds);
FD_SET(sock, &pThreadInfo->readfds);

memset(buf, 0, sizeof(buf));
while(true) {
// if event object is signalled, exit thread.
if ( WaitForSingleObject(
pThreadInfo->hThreadShutdownEvent, 0 )
== WAIT_OBJECT_0 ) {
goto exit;
}
if ( pThreadInfo->bEndRequested ) goto exit;

this->LockSoundBuffer();

// 送信するデータを得る : get some data to send
int nDataSize = min( this->vecSoundBuffer.size(),
sizeof(buf)/sizeof(short) );
nDataSize -= ( nDataSize % 160 );
for ( int i=0 ; i < nDataSize ; i++ ) {
buf[i] = vecSoundBuffer[i];
}
this->UnlockSoundBuffer();
if ( nDataSize == 0 ) {
Sleep(30);
continue;
}

// 送信 : send
int nSendBytes =
send(sock, (char *) buf, nDataSize * sizeof(short), 0);
if ( nSendBytes == 0 ) break;
if ( nSendBytes == SOCKET_ERROR ) {
TRACE("SEND ERROR:%d\n",WSAGetLastError());
Sleep(100);
break;
}
int nSum = 0;
this->LockSoundBuffer();
for ( int i=0 ; i < nSendBytes/2 ; i++ ) {
this->vecSoundBuffer.pop_front();
}
this->UnlockSoundBuffer();

Sleep(10);
}
closesocket(sock);

if ( pThreadInfo->bEndRequested ) goto exit;

}
}
exit:
_endthread();
}



スポンサーサイト

2006.09.22 | Comments(0) | Trackback(0) | Skype API

新しい記事へ <<  | HOME |  >> 古い記事へ

広告:

FC2Ad

カテゴリ展開メニュー

  • 未分類(13)
  • Lua(38)
  • プログラミング(11)
  • 食べ物(3)
  • SPAM(2)
  • ゲーム開発(4)
  • GIS/GPS/GoogleMaps(2)
  • スポーツ(1)
  • Skype API(1)
  • AR(1)

はてブ ランキング

ブログ全体: このWikiのはてなブックマーク数

プロフィール

はむ!

Author:はむ!
よく使う言語・環境:
C++,C,Lua,java,VBA,DB
たまにPHPとかjavascript
血液型:O型

メール: lua%ham.nifty.jp
(%を@に変えてください)
ついったー: @hammmm

Lua関連アンテナ

ブロとも申請フォーム

この人とブロともになる

全記事表示リンク

全ての記事を表示する

ブログ内検索


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。