/************************************************************
**
**     Object name : 
**
**     Public      : 
**
**     Private     : 
**
**     Description
**     -- -- -- -- -- -- -- --
**     RASP-ZXを用いた音声収録システム
**     
**     fwrite で 2GB を越えるファイルを作成する場合
**     -D_FILE_OFFSET_BITS=64 コンパイルオプションが必要
**
**     History
**     -- -- -- -- -- -- -- --
**     2015.08.13 K.Takagi MX用に移植
**     2015.08.07 K.Takagi RASP24版ベースで作成
**
**     [RASP-24]
**     2012.12.19 K.Takagi 不要なコードを削除
**     2011.10.05 K.Takagi LEDの制御はディスクモニタ側に移動
**     2011.08.12 K.Takagi マウント、アンマウント時の引数の与えかたを修正
**                         LEDのマクロを修正
**     2011.07.17 K.Takagi LED制御を追加
**                         基本部分の実装完了
**     2011.07.16 K.Takagi 各種処理を実装
**     2011.07.11 K.Takagi 新規作成
**
**
**     Copyright (c) 2011 - 2015
**     System In Frontier Inc.
**
*/


/*=========================================================*/
/*               Special Macro Definition                  */
/*=========================================================*/

#undef  DEBUG
#undef  TEST_MODE

/*=========================================================*/
/*                   Include Files                         */
/*=========================================================*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include <sys/io.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>

#include "RecSystem.h"
#include "raspzx-driver.h"
#include "rasp-netapi-arg.h"

/*=========================================================*/
/*                   Macro Definition                      */
/*=========================================================*/

#undef DEBUG_PRINT
#ifdef DEBUG

#define DEBUG_PRINT printf

#else

#define DEBUG_PRINT(...)  

#endif

#define  AD_BUFFER_SIZE         (64 * 1024)



/*=========================================================*/
/*                 Structure Declaration                   */
/*=========================================================*/


/*=========================================================*/
/*                    Private Valiable                     */
/*=========================================================*/
static int stack_board_num;

/*=========================================================*/
/*                    Public Valiable                      */
/*=========================================================*/


/************************************************************
**
**  Public  : 
**
**  History : 2011.07.11 K.Takagi 新規作成
**
**  Description
**  -----------
**  音声用デバイス（RASP-24）の初期化
**
*/

int InitSoundDevice(char *device_file)
{
    int  stat;
    int  ch;
    int  fd_rasp;
    char device_file_name[DEVICE_FILE_NAME_LENGTH];
    
    CAPTURE_CHANNELS_t  capture_info;
    int  set_value;
    
    
    if(device_file == NULL)
    {
        sprintf(device_file_name, "/dev/rasp");
    }
    else
    {
        sprintf(device_file_name, "%s", device_file);
    }
    
    /* デバイスファイルを開く */
    fd_rasp = open(device_file_name, O_RDWR, 0666);
    if(fd_rasp < 0)
    {
        fprintf(stderr, "%s Open Error.\n", device_file_name);
        return -1;
    }
    
#ifdef NEVER
    /* スタックしているボード枚数を取得 */
    stat = ioctl(fd_rasp, FIO_RASPZX_GET_BOARD_NUM, &stack_board_num);
    if(stat < 0)
    {
        fprintf(stderr, "Get Board Num Error.\n");
        goto InitErrEnd;
    }
    printf("RASP Stack Board Num = %d\n", stack_board_num);
    
    
    /* 同時サンプリングのCHの個別設定 */
    capture_info.board_no = 0;
    capture_info.channels = 0xFF;
    
    stat = ioctl(fd_rasp, FIO_RASPZX_SET_CAPTURE_CHANNELS, &capture_info);
    if(stat < 0)
    {
        fprintf(stderr, "FIO_RASP_SET_CAPTURE_CHANNELS ioctl Error.\n");
        goto InitErrEnd;
    }
#endif
    
    /* 正常終了 */
    return fd_rasp;
    
    /* エラー終了 */
InitErrEnd:
    close(fd_rasp);
    
    return -1;
    
}


/************************************************************
**
**  Public  : 
**
**  History : 2011.10.05 K.Takagi LED制御はディスクモニタ側へ移動
**            2011.07.17 K.Takagi LED制御を追加
**            2011.07.16 K.Takagi 実際の処理を組込み
**            2011.07.11 K.Takagi 新規作成
**
**  Description
**  -----------
**  収録のメイン
**
*/

void *RecSoundMainServer(void *arg)
{
    int stat;
    int fd_rasp;
    REC_SOUND_INFO_t *info;
    
    char *ad_data_buffer;
    unsigned long file_size;
    int file_no;
    int write_size;
    int read_size;
    int req_size;
    int offset;
    
    FILE *fp;
    char save_file_name[DIR_NAME_LENGTH + FILE_NAME_LENGTH + 10];
    
    
    /* 初期化処理 */
    info = (REC_SOUND_INFO_t *)arg;
    fd_rasp = info->fd;
    
    /* バッファを確保 */
    ad_data_buffer = (char *)malloc(sizeof(char) * AD_BUFFER_SIZE);
    if(ad_data_buffer == NULL)
    {
        info->run_flag = STATE_ERROR;
        sem_post(&(info->sem_id));
        return 0;
    }
    
    /* 実際の開始のための同期 */
    sem_post(&(info->sem_id));
    
    /* A/D開始 */
    stat = ioctl(fd_rasp, FIO_RASPZX_AD_START);
    
    /* 実際の収録処理 */
    file_no = 0;
    while(info->run_flag == STATE_RUN)
    {
        /* バッファサイズ単位でI/O */
        file_size = info->sound_file_size * ((1024 * 1024) / AD_BUFFER_SIZE);
        
        /* ファイルを開く */
        sprintf(save_file_name, "%s%d.raw", info->sound_file_name, file_no);
        fp = fopen(save_file_name, "wb");
        if(fp == NULL)
        {
            printf("File Open Error [%s]\n", save_file_name);
            info->run_flag = STATE_ERROR;
            break;
        }
        
        /* 1ファイル分の収録処理 */
        while((info->run_flag == STATE_RUN) && (file_size > 0))
        {
            /* データを取得 */
            req_size = AD_BUFFER_SIZE;
            offset   = 0;
            do
            {
                read_size = read(fd_rasp, &ad_data_buffer[offset], req_size);
                if(read_size < 0)
                {
                    printf("A/D Data Read Error\n");
                    info->run_flag = STATE_ERROR;
                    break;
                }
                req_size -= read_size;
                offset   += read_size;
                
            }while(req_size > 0);
            
            if(read_size < 0)
            {
                fclose(fp);
                break;
            }
            
            /* データをファイルに保存 */
            req_size = AD_BUFFER_SIZE;
            offset   = 0;
            do
            {
                write_size = fwrite(&ad_data_buffer[offset], sizeof(char), req_size, fp);
                if(write_size < 0)
                {
                    printf("File Write Error\n");
                    info->run_flag = STATE_ERROR;
                    break;
                }
                req_size -= write_size;
                offset   += write_size;
                
            }while(req_size > 0);
        
            /* 残りのファイルサイズを更新 */
            file_size--;
        }
        
        /* ファイルを閉じる */
        fclose(fp);
        file_no++;
    }
    
    /* A/D停止 */
    stat = ioctl(fd_rasp, FIO_RASPZX_AD_STOP);

    /* バッファを開放 */
    free(ad_data_buffer);
    
    
    return 0;
}


/************************************************************
**
**  Public  : 
**
**  History : 2011.07.16 K.Takagi 実際に起動したかの確認を追加
**            2011.07.11 K.Takagi 新規作成
**
**  Description
**  -----------
**  収録用プログラムをスレッドとして起動
**
**
*/

int RecSouncMainStart(REC_SOUND_INFO_t *info)
{
    int status;
    pthread_t th_id;
    
    info->run_flag = STATE_RUN;
    
    /* スレッドを起動 */
    status = pthread_create(&th_id, NULL, RecSoundMainServer, (void *)info);
    
    /* 実際に起動したか確認 */
    sem_wait(&(info->sem_id));
    
    if((status != 0) || (info->run_flag != STATE_RUN))
    {
        printf("RecSoundMainServer Start Error.\n");
        return -1;
    }
    else
    {
        printf("RecSoundMainServer Start OK.\n");
    }
    info->th_id = th_id;
    

    return 0;
}

/************************************************************
**
**  Public  : 
**
**  History : 2011.07.16 K.Takagi スレッドの終了待ちを追加
**            2011.07.12 K.Takagi 新規作成
**
**  Description
**  -----------
**  収録用プログラムを停止
**
*/

int RecSouncMainStop(REC_SOUND_INFO_t *info)
{
    int status;

    /* 停止要求 */
    info->run_flag = STATE_STOP;
    
    if(info->th_id != 0)
    {
        /* スレッドの終了待ち */
        status = pthread_join(info->th_id, NULL);
    }
    info->th_id = 0;
    
    return 0;
}


#ifdef TEST_MODE

#include "LoadSystemInfo.c"
#include "DeviceMount.c"
#include "MakeDirectory.c"

/************************************************************
**
**  Public  : 
**
**  History : 2011.08.15 K.Takagi 開始、終了の時刻記録を追加
**            2011.08.12 K.Takagi 単独動作確認
**            2011.06.16 K.Takagi マウント、アンマウント、ディレクトリ作成追加
**            2011.07.11 K.Takagi 新規作成
**
**  Description
**  -----------
**  単独テスト時のメイン
**
*/

int main()
{
    int ret_stat;
    int fd_rasp24;
    RASP24_SYSTEM_INFO_t system_info;
    REC_SOUND_INFO_t sound_info;
    float disk_space;
    
    char info_file_name[DIR_NAME_LENGTH + FILE_NAME_LENGTH + 10];
    FILE *fp;
    struct timeval rec_time;
    struct tm *rec_tm;
    
    
    /* 各種情報を初期化 */
    disk_space = 80.0;
    memset(&system_info, 0, sizeof(RASP24_SYSTEM_INFO_t));
    memset(&sound_info,  0, sizeof(REC_SOUND_INFO_t));
    
    sem_init(&sound_info.sem_id, 0, 0);
    
    /* システム情報取得 */
    LoadSystemInfo("rasp24-st.conf", &system_info);

    /* システム情報表示 */
    ShowSystemInfo(&system_info);
    
    /* USBストレージのマウント */
    ret_stat = DeviceMount(system_info.mount_device, system_info.mount_point, system_info.mount_fs);
    if(ret_stat == 0)
    {
        printf("Mount OK\n");
    }
    else
    {
        printf("Muont Error\n");
        perror(NULL);
        return -1;
    }
    
    /* 音声データ保存用のディレクトリを作成 */
    ret_stat = MakeDirectory(system_info.mount_point, system_info.sound_save_dir);
    if(ret_stat < 0)
    {
        printf("MakeDirectory Error.\n");
        goto ErrEnd1;
    }
    else
    {
        printf("MakeDirectory OK.\n");
    }
    
    /* 音声デバイス（RASP-24）の初期化 */
    fd_rasp24 = InitSoundDevice(NULL);
    if(fd_rasp24 < 0)
    {
        printf("RASP-24 Device Init Error.\n");
        goto ErrEnd1;
    }
    else
    {
        printf("RASP-24 Device Init OK.\n");
    }
    
    /* 各種情報をコピー */
    sound_info.fd              = fd_rasp24;
    sound_info.sound_file_size = system_info.sound_file_size;
    sprintf(sound_info.sound_save_dir, "%s/%s",  system_info.mount_point, system_info.sound_save_dir);
    strcpy(sound_info.sound_file_name, system_info.sound_file_name);
    
    sprintf(info_file_name, "%s/%s/%s",  system_info.mount_point, system_info.sound_save_dir, system_info.save_info_file);
    fp = fopen(info_file_name, "wt");
    if(fp == NULL)
    {
        printf("File Open Error.\n");
        goto ErrEnd2;
    }
    
    /* 開始時刻 */
    ret_stat = gettimeofday(&rec_time, NULL);
    if(ret_stat == 0)
    {
        fprintf(fp, "Start\n");
        fprintf(fp, "%ld[sec] %ld[usec]\n", rec_time.tv_sec, rec_time.tv_usec);
        rec_tm = localtime(&(rec_time.tv_sec));
        fprintf(fp, "%d/%d/%d %d:%d:%d\n", 
                rec_tm->tm_year+1900, rec_tm->tm_mon+1, rec_tm->tm_mday,
                rec_tm->tm_hour, rec_tm->tm_min, rec_tm->tm_sec);
    }
    
    /* 収録開始 */
    ret_stat = RecSouncMainStart(&sound_info);
    if(ret_stat < 0)
    {
        printf("RecSoundMainStart Error.\n");
        goto ErrEnd2;
    }
    else
    {
        printf("RecSoundMainStart OK.\n");
    }
    
    
    /* なにか入力されるまで待機 */
    ret_stat = getchar();
    
    
    /* 収録停止 */
    ret_stat = RecSouncMainStop(&sound_info);
    if(ret_stat < 0)
    {
        printf("RecSoundMainStop Error.\n");
    }
    else
    {
        printf("RecSoundMainStop OK.\n");
    }
    
    /* 終了時刻 */
    ret_stat = gettimeofday(&rec_time, NULL);
    if(ret_stat == 0)
    {
        fprintf(fp, "End\n");
        fprintf(fp, "%ld[sec] %ld[usec]\n", rec_time.tv_sec, rec_time.tv_usec);
        rec_tm = localtime(&(rec_time.tv_sec));
        fprintf(fp, "%d/%d/%d %d:%d:%d\n", 
                rec_tm->tm_year+1900, rec_tm->tm_mon+1, rec_tm->tm_mday,
                rec_tm->tm_hour, rec_tm->tm_min, rec_tm->tm_sec);
    }
    fclose(fp);
    

ErrEnd2:
    /* RASP-24デバイスを閉じる */
    close(fd_rasp24);
    

ErrEnd1:
    /* USBストレージをアンマウント */
    ret_stat = DeviceUnmount(system_info.mount_point);
    if(ret_stat == 0)
    {
        printf("Unmount OK\n");
    }
    else
    {
        printf("Unmuont Error\n");
        perror(NULL);
    }


    return 0;
    
}

#endif /* End of TEST_MODE */

/* デバックモードを解除 */
#undef DEBUG
#undef TEST_MODE



/* EOF */
