
/************************************************************
**	AudioStreamFromMic.c
**
**  Object name :
**     AudioStreamFromMic
**
**  History :
**     2013.05.13 M.Takigahira Build有無指定に対応
**     2013.04.03 K.Takagi     RASP-LC 4ch対応
**                               8ch中の後ろ4chが使えるようにCHオフセット追加
**     2013.03.22 K.Takagi     PS/EYE対応コードをマージ
**     2013.03.18 K.Takagi     pcm_openで指定するバッファサイズを拡張
**     2013.02.15 K.Takagi     データのbit数をマクロ化
**     2013.01.17 K.Takagi     メモリのチェックを追加
**     2012.12.05 K.Takagi     データ取得関数にフレームカウント引数追加
**     2012.11.26 K.Takagi     新規作成
**
**  Description
**  -----------
**  RASP-LCシリーズからのデータ読み出し
**
**  以下の順で操作
**    1.Set Parameter : HASetParamAudioStreamFromMic
**    2.Initialize    : RaspLc_Init
**    3.Start         : RaspLc_Start
**    4.Get Data      : RaspLc_DataRead
**    5.Stop          : RaspLc_Stop
**
**     Copyright (c) 2012-2013
**     System In Frontier,Inc.
**     All rights reserved. 
**
**     当社の許可なしに複製および転載を禁ず
**
*/

///////////////////////////////////////////////////////////////
//  Include Files
#include "HA_config.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#ifndef IS_APPLE_IOS
#include <tinyalsa/asoundlib.h>
#include <sound/asound.h>
#endif // IS_APPLE_IOS
#include "build_modules.h"
#include "HA_log.h"
#include "android_hark.h"

#include "MatrixMemoryAllocate.h"
#include "AudioStreamFromMic.h"
#include "ConvertVectorToMatrix.h"

#ifdef IS_APPLE_IOS
// iOSではこの関数群は使わない ==> iOSでは強制的にbuild対象外に設定
#undef BUILD_MODULE_AUDIO_STREAM_FROM_MIC
#endif // IS_APPLE_IOS
///////////////////////////////////////////////////////////////
//  Define Macros
//// cancel //// #define LOCAL_DEFAULT_DEVICE_TYPE    DEVICE_NUM_SELECT_MODE
#define LOCAL_DEFAULT_LENGTH         ARY_NUM_WAVE //512
#define LOCAL_DEFAULT_ADVANCE        ARY_NUM_WAVE //512
#define LOCAL_DEFAULT_CHANNELS       1 //_DEV_CH_VAL_
#define LOCAL_DEFAULT_CHANNEL_OFFSET 0 //_CH_OFFSET_VAL_
#define LOCAL_DEFAULT_SAMPLING_RATE  16000
#define LOCAL_DEFAULT_NBITS          24

#if !defined BUILD_MODULE_AUDIO_STREAM_FROM_MIC
///////////////////////////////////////////////////////////////
//  Dummy Functions for build empty methods.
int HASetParamAudioStreamFromMic(Param_AudioStreamFromMic_t *param){ return 0; }
int HAGetParamAudioStreamFromMic(Param_AudioStreamFromMic_t *param){ return 0; }
int RaspLc_Init(void){ return 0; }
int RaspLc_Start(void){ return 0; }
int RaspLc_Stop(void){ return 0; }
int RaspLc_ReadData(int frame_count, float **data, char* ts, int use_timestamp){ return 0; }
#ifdef STANDALON_TEST
int main(int argc, char **argv){ return 0; }
#endif
#else // #if defined BUILD_MODULE_AUDIO_STREAM_FROM_MIC
///////////////////////////////////////////////////////////////
//  Parameter
static Param_AudioStreamFromMic_t audio_info = {
    //// cancel //// LOCAL_DEFAULT_DEVICE_TYPE,
    LOCAL_DEFAULT_LENGTH,
    LOCAL_DEFAULT_ADVANCE,
    LOCAL_DEFAULT_CHANNELS,
    LOCAL_DEFAULT_CHANNELS,
    LOCAL_DEFAULT_CHANNEL_OFFSET,
    LOCAL_DEFAULT_SAMPLING_RATE,
    LOCAL_DEFAULT_NBITS
};

struct rasp_info_t
{
    int card;
    int device;
    int channels;
    int rate;
    int nbits;
    int nbytes;
    int period_size;
    int period_count;
    int buffer_size;
};
static struct rasp_info_t rasp_info;

static struct pcm_config config;
static struct pcm *pcm;

static char *audio_buffer = NULL;
static float **audio_pre_data;

///////////////////////////////////////////////////////////////
//  Local Buffer

#if NEVER
/************************************************************
**  History :
**    2012.09.20 K.Takagi 新規作成
*/
// [Code separation] refer to ConvertVectorToMatrix.c
// Private : Waveファイルから読み出した16bitまたは24bitデータをfloat型に変換
//           また配列を[lenght][ch] から [ch][length] の構成に並び替え
/*
static int ConvertVectorToMatrix(char *in_data, float **out_data, int length, int offset_pos)
{
    int  val;
    char *ptr;
    int  ch;
    int  index;
    ptr = (char *)&val;
    if(rasp_info.nbits == 16){
        for(index=0; index<length; index++){
            for(ch=0; ch<rasp_info.channels; ch++){
                val = 0;
                ptr[2] = *in_data++;
                ptr[3] = *in_data++;
                val = val >> 16;
                out_data[ch][offset_pos + index] = (float)val;
            }
        }
    }
    else if(rasp_info.nbits == 24){
        for(index=0; index<length; index++){
            for(ch=0; ch<rasp_info.channels; ch++){
                val = 0;
                ptr[1] = *in_data++;
                ptr[2] = *in_data++;
                ptr[3] = *in_data++;
                val = val >> 8;
                out_data[ch][offset_pos + index] = (float)val;
            }
        }
    }
    else{
        return -1;
    }
    return 0;
}
*/
#endif

///////////////////////////////////////////////////////////////
//  Clear

///////////////////////////////////////////////////////////////
//  Initialize
/******************************************************************************
**  History :
**    2013.05.31 M.Takigahira データのbit数をパラメータ化
**    2013.03.22 K.Takagi     PS/EYE対応
**    2013.02.15 K.Takagi     データのbit数をマクロ化
**    2012.11.26 K.Takagi     新規作成
*/
// Public : RASP-LCの検出と初期化
int RaspLc_Init(void)
{
    int count;
    char dev_name[64];
    int fd;
    char *ptr;
    
    struct snd_ctl_card_info card_info;
    
    if(audio_buffer != NULL){
        free(audio_buffer);
        audio_buffer = NULL;
    }
    
    /* RASP-LC初期設定
    **   UAC1.0は1msに1パケットの転送
    **   16kHzでのサンプリングならば1msあたりに16sampleのデータが含まれる
    **
    **   period_size は 32sample分
    **   period_count は advance / 32sample
    */
    rasp_info.card         = -1;
    rasp_info.device       = 0;
    rasp_info.channels     = audio_info.channel_count;
    rasp_info.rate         = audio_info.sampling_rate;
    //rasp_info.nbits        = 24;
    //rasp_info.nbits        = AUDIO_BITS;
    rasp_info.nbits        = audio_info.nbits;
    rasp_info.nbytes       = (rasp_info.nbits / 8);
    /* オリジナル設定 */
    //rasp_info.period_size  = (rasp_info.nbits / 8) * rasp_info.channels * (rasp_info.rate / 1000) * 2;
    //rasp_info.period_count = audio_info.advance / 32;
    //rasp_info.buffer_size  = (rasp_info.period_size * rasp_info.period_count);

    /* テスト */
    rasp_info.period_size  = (rasp_info.nbits / 8) * rasp_info.channels * audio_info.advance;
    rasp_info.period_count = 1;
    rasp_info.buffer_size  = (rasp_info.period_size * rasp_info.period_count);
    
/* 旧コード
    // Search of RASP-LC
    count = 0;
    while(1){
        sprintf(dev_name, "/dev/snd/controlC%d", count);
        
        fd = open(dev_name, O_RDONLY);
        if(fd <= 0){
            rasp_info.card = -1;
            break;
        }
        ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &card_info);
        close(fd);
        
#ifdef PS_EYE_DEVICE // support PS-Eye
        ptr = strstr((char const *)card_info.name, (char const *)"USB Camera-B4.09.24.1");
        if(ptr == NULL){ // retry search OLD model
            ptr = strstr((char const *)card_info.name, (char const *)"USB Camera-B3.04.06.1");
        }
#else // support RASP-LC, RASP-LC4
        ptr = strstr((char const *)card_info.name, (char const *)"RASP");
#endif
        if(ptr != NULL){
            rasp_info.card = count;
            break;
        }
        count++;
    }
*/
    // Device Search of RASP-LC, PS-Eye, microcone, etc...
    switch(audio_info.device_type){
    case RASP_ALL8CH_MODE: // support RASP-LC
    case RASP_FIRST4CH_MODE:
    case RASP_LAST4CH_MODE:
        count = 0;
        while(1){
            sprintf(dev_name, "/dev/snd/controlC%d", count);
            fd = open(dev_name, O_RDONLY);
            if(fd <= 0){
                rasp_info.card = -1;
                break;
            }
            ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &card_info);
            close(fd);
            ptr = strstr((char const *)card_info.name, (char const *)"RASP");
            if(ptr != NULL){
                rasp_info.card = count;
                break;
            }
            count++;
        }
        break;
    case RASP_ALL4CH_MODE: // support RASP-LC4
        count = 0;
        while(1){
            sprintf(dev_name, "/dev/snd/controlC%d", count);
            fd = open(dev_name, O_RDONLY);
            if(fd <= 0){
                rasp_info.card = -1;
                break;
            }
            ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &card_info);
            close(fd);
            ptr = strstr((char const *)card_info.name, (char const *)"RASP");
            if(ptr != NULL){
                rasp_info.card = count;
                break;
            }
            count++;
        }
        break;
    case PS_EYE_MODE: // support PS-Eye
        count = 0;
        while(1){
            sprintf(dev_name, "/dev/snd/controlC%d", count);
            fd = open(dev_name, O_RDONLY);
            if(fd <= 0){
                rasp_info.card = -1;
                break;
            }
            ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &card_info);
            close(fd);
            ptr = strstr((char const *)card_info.name, (char const *)"USB Camera-B4.09.24.1");
            if(ptr == NULL){ // retry search OLD model
                ptr = strstr((char const *)card_info.name, (char const *)"USB Camera-B3.04.06.1");
            }
            if(ptr != NULL){
                rasp_info.card = count;
                break;
            }
            count++;
        }
        break;
    case MICROCONE_MODE: // support Microcone
        count = 0;
        while(1){
            sprintf(dev_name, "/dev/snd/controlC%d", count);
            fd = open(dev_name, O_RDONLY);
            if(fd <= 0){
                rasp_info.card = -1;
                break;
            }
            ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &card_info);
            close(fd);
            ptr = strstr((char const *)card_info.name, (char const *)"Microcone");
            if(ptr != NULL){
                rasp_info.card = count;
                break;
            }
            count++;
        }
        break;
    case BLUETOOTH_MIC_MODE:
        count = 0;
        while(1){
            sprintf(dev_name, "/dev/snd/controlC%d", count);
            fd = open(dev_name, O_RDONLY);
            if(fd <= 0){
                rasp_info.card = -1;
                break;
            }
            ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &card_info);
            close(fd);
            ptr = strstr((char const *)card_info.name, (char const *)"KKKKK");
            if(ptr != NULL){
                rasp_info.card = count;
                break;
            }
            count++;
        }
        break;
    case DEVICE_NUM_SELECT_MODE:
        count = 0;
        sprintf(dev_name, "/dev/snd/controlC%d", count);
        fd = open(dev_name, O_RDONLY);
        if(fd <= 0){ //target isn't found
            rasp_info.card = -1;
        }
        else{
            //ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &card_info);
            //printf("%s", card_info.name);
            close(fd);
            rasp_info.card = count;
        }
        break;
    default:
        count = 0;
        while(1){
            sprintf(dev_name, "/dev/snd/controlC%d", count);
            fd = open(dev_name, O_RDONLY);
            if(fd <= 0){
                rasp_info.card = -1;
                break;
            }
            else{
                //ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &card_info);
                //printf("%s", card_info.name);
                close(fd);
                rasp_info.card = count;
                //break; //comment out: last capture device(default), comment in: first capture device
            }
            count++;
        }
        break;
    }
    return rasp_info.card;
}

///////////////////////////////////////////////////////////////
//  SetParam
/******************************************************************************
**  History :
**    2012.11.26 K.Takagi 新規作成
*/
// Public : パラメータ設定　RaspLc_Initの前に実行すること
int HASetParamAudioStreamFromMic(Param_AudioStreamFromMic_t *param)
{
    //audio_info.length        = param->length;
    //audio_info.advance       = param->advance;
    //audio_info.channel_count = param->channel_count;
    //audio_info.sampling_rate = param->sampling_rate;
	audio_info = *param;
    return 0;
}

int HAGetParamAudioStreamFromMic(Param_AudioStreamFromMic_t *param)
{
	*param = audio_info;
    return 0;
}

///////////////////////////////////////////////////////////////
//  MAIN
/******************************************************************************
**  History :
**    2013.03.18 K.Takagi データバッファを拡張
**    2013.01.17 K.Takagi メモリのチェック追加
**    2012.11.26 K.Takagi 新規作成
*/
// Public : tinyalsa経由で録音開始の準備　実際の開始は最初のpcm_readで実行される
int RaspLc_Start(void)
{
    int index;
    
    if(rasp_info.card < 0){
        return -1;
    }
    
    config.channels     = rasp_info.channels;
    config.rate         = rasp_info.rate;
    /* オリジナル */
    //config.period_size  = rasp_info.period_size;
    //config.period_count = rasp_info.period_count;

    /* テスト
     * period_count=1にするとNG、また12以上でもpcm_openがエラーになる
     * tinyalsa付属のtinycapを使っても同じ現象
     * 2～11の範囲で指定すること
     *
     * period_sizeを2倍してもpcm_openがエラーになるので
     * period_size * period_count の総容量で制限があるもよう
     */
    config.period_size  = rasp_info.period_size;
    config.period_count = rasp_info.period_count * 10;
    switch(rasp_info.nbits){
    case 16:
        config.format = PCM_FORMAT_S16_LE;
    	break;
    case 24:
        config.format = PCM_FORMAT_S24_LE;
    	break;
    case 32:
    default:
        config.format = PCM_FORMAT_S32_LE;
    	break;
    }
    config.start_threshold   = 0;
    config.stop_threshold    = 0;
    config.silence_threshold = 0;
    if(audio_buffer != NULL){
        free(audio_buffer);
        audio_buffer = NULL;
    }
    /* バッファ確保 */
    audio_buffer = (char *)malloc(sizeof(char) * rasp_info.buffer_size);
    if(audio_buffer == NULL){
        return -1;
    }
    if(audio_pre_data != NULL){
        free(audio_pre_data[0]);
        free(audio_pre_data);
        audio_pre_data = NULL;
    }
    audio_pre_data = (float **)MemoryAllocate2D(rasp_info.channels, audio_info.length, sizeof(float));
    if(audio_pre_data == NULL){
        free(audio_buffer);
		audio_buffer = NULL;
        return -1;
    }
    for(index=0; index<rasp_info.channels*audio_info.length; index++){
        audio_pre_data[0][index] = 0.0;
    }
    /* Audio Device(RASL-LC)を開く */
    pcm = pcm_open(rasp_info.card, rasp_info.device, PCM_IN, &config);
    if (!pcm || !pcm_is_ready(pcm)){
        free(audio_pre_data[0]);
        free(audio_pre_data);
		audio_pre_data = NULL;
        free(audio_buffer);
        audio_buffer = NULL;
        return -2;
    }
    // Test
    //pcm_start(pcm);
    return 0;
}

/******************************************************************************
**  History :
**    2012.11.26 K.Takagi 新規作成
*/
// Public : tinyalsa経由で録音停止
int RaspLc_Stop(void)
{
    if(rasp_info.card < 0){
        return -1;
    }
    // Stop & Close
    pcm_close(pcm);
    if(audio_buffer != NULL){
        free(audio_buffer);
        audio_buffer = NULL;
    }
    if(audio_pre_data != NULL){
        free(audio_pre_data[0]);
        free(audio_pre_data);
        audio_pre_data = NULL;
    }
    return 0;
}

/******************************************************************************
**  History :
**    2013.04.03 K.Takagi RASP-LC 4ch対応
**                        8ch中の後ろ4chが使えるようにCHオフセット追加
**    2012.12.05 K.Takagi フレームカウントの引数を追加
**                        現状frame_countは未使用、他のモジュールのI/Fと合わせるため追加
**    2012.11.26 K.Takagi 新規作成
*/
// Pubic : buffer_sizeを単位としてデータを取得
int RaspLc_ReadData(int frame_count, float **data, char* ts, int use_timestamp)
{
    int status;
    int index,shift_index;
    int ch;
    int device_channels,target_channels;
    int read_req_size;
    
    int length;
    int advance;
    int overrap_len;

    if(use_timestamp){
       	struct timeval tv;
       	gettimeofday(&tv, NULL);
       	unsigned long long millisecondsSinceEpoch =
       		(unsigned long long)(tv.tv_sec) * 1000 +
       		(unsigned long long)(tv.tv_usec) / 1000;
       	sprintf(ts, "%llu", millisecondsSinceEpoch);
    /*
        // tv.tv_usec=[0-999999]への正規化 while内は本来通らないはず
        while(tv.tv_usec<0){ tv.tv_sec--; tv.tv_usec += 1000000; } // tv_usecの負値対策
        while(tv.tv_usec>999999){ tv.tv_sec++; tv.tv_usec -= 1000000; } // tv_usecのsec値超過対策
        if(tv.tv_sec<0){ tv.tv_sec = 0; tv.tv_usec = 0; } // LANAさん実装に合わせtimevalの負値は0で処理する
        sprintf(ts, "%lu%06lu", tv.tv_sec, tv.tv_usec); // 符号+10桁+6桁+\0でtsは最低限18Byte必要
    */
    }

    length  = audio_info.length;
    advance = audio_info.advance;
    device_channels = rasp_info.channels;
    target_channels = device_channels - audio_info.channel_offset;
    overrap_len = length - advance;
    
    /* 前フレームのオーバーラップ部分をコピー */
    //for(ch=0; ch<rasp_info.channels; ch++){
    for(ch=0; ch<target_channels; ch++){
        //for(index=0; index<(length - advance); index++){
        for(index=0; index<overrap_len; index++){
            data[ch][index] = audio_pre_data[ch][index];
        }
    }
    
    //read_req_size = sizeof(char) * rasp_info.channels * advance * rasp_info.nbytes;
    read_req_size = sizeof(char) * device_channels * advance * rasp_info.nbytes;
    
    // データ取得(最初の1回目に実際のスタート処理がされる)
    status = pcm_read(pcm, (char *)audio_buffer, (int)read_req_size);
    if(status){
        return -1;
    }
    
    /* データ配列の並び替え */
    //ConvertVectorToMatrix(audio_buffer, data, advance, (length - advance));
    //ConvertVectorToMatrix(audio_buffer, data, rasp_info.channels, advance, (length - advance), rasp_info.nbits);
    ConvertVectorToMatrix(audio_buffer, data, device_channels, audio_info.channel_offset, advance, overrap_len, rasp_info.nbits);
    
    /* オーバーラップ部分を保持 */
    //for(ch=0; ch<rasp_info.channels; ch++){
    for(ch=0; ch<target_channels; ch++){
        //for(index=0; index<(length - advance); index++){
        for(index=0, shift_index=advance; index<overrap_len; index++, shift_index++){
             //audio_pre_data[ch][index] = data[ch][advance + index];
             audio_pre_data[ch][index] = data[ch][shift_index];
        }
    }
    return rasp_info.buffer_size;
}

#ifdef STANDALON_TEST
/************************************************************
**  History :
**    2012.12.05 K.Takagi フレームカウント用にhark_count追加
**    2012.11.26 K.Takagi 新規作成
*/
// Public : 単体テストのメイン
int main(int argc, char **argv)
{
    int stat;
    int channels;
    int count;
    int index;
    
//    int ch;
    int length;
    int advance;
    
    Param_AudioStreamFromMic_t audio_param;
    
    float **data;
    int out_data[512];
    
    FILE *fp;
    
    channels = 8;
    length   = 512;
    advance  = 512;
    
    
    audio_param.length        = length;
    audio_param.advance       = advance;
    audio_param.channel_count = channels;
    audio_param.sampling_rate = 16000;
    
    /* パラメータ設定 */
    HASetParamAudioStreamFromMic(&audio_param);
    
    /* 初期化 */
    stat = RaspLc_Init();
    if(stat < 0){
        printf("Device Not Found.\n");
        return -1;
    }
    printf("channels = %d\n", rasp_info.channels);
    printf("rate     = %d\n", rasp_info.rate);
    printf("nbits    = %d\n", rasp_info.nbits);
    printf("nbytes   = %d\n", rasp_info.nbytes);
    
    
    fp = fopen("data.raw", "wb");
    if(fp != NULL){
        data = (float **)MemoryAllocate2D(channels, length, sizeof(float));
        if(data == NULL){
            fclose(fp);
            return -1;
        }
        
        stat = RaspLc_Start();
        printf("Start %d\n", stat);
        
        /* オーバーラップなしでCH0出力 */
        index = 0;
        while(1){
#ifdef USE_TIMESTAMP
			//unused stamp
			char ts[21] = "00000000000000000";
			stat = RaspLc_ReadData(data,&ts[0]);
#else
            stat = RaspLc_ReadData(data);
#endif

            //printf("Read stat = %d\n", stat);
            if(stat < 0){
                printf("Read stat = %d\n", stat);
                break;
            }
            
            /* Audacityでraw(float)の読み出したらエフェクトから圧縮をすること */
            fwrite(&(data[0][0]), sizeof(float), length, fp);

            //for(count=0; count<length; count++){ printf("%f\n", data[0][count]); }

            /*
            for(count=0; count<length; count++){
                out_data[count] = data[0][count];
            }
            fwrite(&out_data, sizeof(int), length, fp);
            */
            
            /* OK 
            fwrite(audio_buffer, sizeof(char), rasp_info.buffer_size, fp);
            */
        }
        
        RaspLc_Stop();
        
        free(data[0]);
        free(data);
        
        fclose(fp);
    }
    return 0;
}
#endif // STANDALON_TEST
#endif // BUILD_MODULE_AUDIO_STREAM_FROM_MIC

/* EOF */
