nlgplay for mbed

Dependencies:   SDFileSystemEx mbed

main.cpp

Committer:
bkc_mbed
Date:
2014-09-17
Revision:
11:68032f2a11b9
Parent:
10:b8d41133e499

File content as of revision 11:68032f2a11b9:

//
// NLGPLAY : NLG file player for mbed
//
// example to write : 
// ./lpc21isp -bin file.bin /dev/cu.usbserial-??? 115200 12000
//

#include "mbed.h"
#include "SDFileSystem.h"
#include "lcd.h"

#define USE_NBV3
#define USE_FASTIO

#define NBCTRL_VER "Ver 1.13"

#pragma O3
#pragma Otime


// (pinname, mode)
DigitalIn sw_play(dp24, PullUp);
DigitalIn sw_next(dp10, PullUp);
DigitalIn sw_prev(dp9, PullUp);

// LED 
DigitalOut led1(LED1, 0);
DigitalOut led2(LED2, 0);

// SPI: MOSI, MISO, SCLK, CS
SDFileSystem sd(dp2, dp1, dp6, dp25, "sd"); 

//
// nlg_emb.c
//

#define CMD_PSG  0x00
#define CMD_OPM  0x01
#define CMD_OPM2 0x02
#define CMD_IRQ  0x80

#define CMD_CTC0 0x81
#define CMD_CTC3 0x82

#define NLG_VER (110)
#define NLG_BASECLK (4000000)

FILE *nlg_file;

char nlg_title[65];
int  nlg_baseclk;
int  nlg_tick;
int  nlg_length;
int  nlg_loop_ptr;
int  nlg_version;

int  nlg_ctc0;
int  nlg_ctc3;

#define _WAIT for(int wcnt=0; wcnt < 2; wcnt++)

#ifndef USE_FASTIO
// 通常IO

#define RCK io04 // pin4 , P0_11
#define SCK io03 // pin17, P1_8
#define DBS io01 // pin13, P1_4
#define CTS io02 // pin26, P0_3

#define IO_A0 io05
#define IO_WR io06

// (pinname, initial value)
DigitalOut io01(dp13, 0); // P1_4
DigitalOut io02(dp26, 0); // P0_3
DigitalOut io03(dp17, 0); // P1_8
DigitalOut io04(dp4, 0); // P0_11

DigitalOut io05(dp18, 0); // P1_9
DigitalOut io06(dp11, 0); // P1_2

void ioInit()
{
}

// 16bit output
void ioShiftOut(unsigned int data)
{
    int i;
    
    for(i = 0; i < 8; i++)
    {
        /* 2ビット分のデータをそれぞれ出力 */
        if (data & 0x80)
            CTS = 1;
        else
            CTS = 0;
        
        if (data & 0x8000)
            DBS = 1;
        else
            DBS = 0;
        
        data <<= 1;

        SCK = 1;
        // _WAIT;
        SCK = 0;
        // _WAIT;
    }

    RCK = 1;
    // _WAIT;
    RCK = 0;
    // _WAIT;
}
#else

// 高速IO

#define RCK IO_04
#define SCK IO_03
#define DBS IO_01
#define CTS IO_02


#define IO_A0 io05
#define IO_WR io06

DigitalOut io01(dp13, 0); // P1_4
DigitalOut io02(dp26, 0); // P0_3
DigitalOut io03(dp17, 0); // P1_8
DigitalOut io04(dp4, 0); // P0_11

DigitalOut io05(dp18, 0); // P1_9
DigitalOut io06(dp11, 0); // P1_2


#define IO_01 (1<<4)  // DBS P1_4
#define IO_02 (1<<3)  // CTS P0_3
#define IO_03 (1<<8)  // SCK P1_8
#define IO_04 (1<<11) // RCK P0_11


void ioInit()
{    
    // 出力
    LPC_GPIO0->DIR |= (IO_02 | IO_04);
    LPC_GPIO1->DIR |= (IO_01 | IO_03);
}

// 16bit output
void ioShiftOut(unsigned int data)
{
    int i;
     
    for(i = 0; i < 8; i++)
    {
        /* 2ビット分のデータをそれぞれ出力 */
        if (data & 0x80)
            LPC_GPIO0->DATA |= CTS;
        else
            LPC_GPIO0->DATA &= ~CTS;
        
        if (data & 0x8000)
            LPC_GPIO1->DATA |= DBS;
        else
            LPC_GPIO1->DATA &= ~DBS;
        
        data <<= 1;

        LPC_GPIO1->DATA |= SCK;
        LPC_GPIO1->DATA &= ~SCK;
    }

    LPC_GPIO0->DATA |= RCK;
    LPC_GPIO0->DATA &= ~RCK;

}

#endif


/* 制御信号定義 */
#define CS_PSG (1 << 1)
#define CS_FM1 (1 << 2)
#define CS_FM2 (1 << 3)
#define A0     (1 << 4)
#define WR     (1 << 5)
#define ICL    (1 << 6)

/* アクティブローの制御信号 */
#define ACTLOW (CS_PSG | CS_FM1 | CS_FM2 | WR | ICL)

#ifdef USE_NBV3
/* NBV3 16bit出力 */
void regOutBase(int addr, int data, int select)
{
    /* アドレスを出力 */
    /* A0をローにして待つ */
    IO_A0 = 0;
    ioShiftOut((addr << 8) | (ACTLOW & ~(select | WR)));
    ioShiftOut((addr << 8) | (ACTLOW));
    
    
    /* チップ側の処理を待つ */
    
    /* データを出力 */
    /* A0をハイにして待つ */
    IO_A0 = 1;
    ioShiftOut((data << 8) | (ACTLOW & ~(select | WR)));
    ioShiftOut((data << 8) | (ACTLOW));

    /* 処理を待つ */
}
#else
/* NBV2互換出力 */
void regOutBase(int addr, int data,int select)
{
    /* アドレスを出力 */
    /* A0をローにして待つ */
    ioShiftOut((addr << 8) | (ACTLOW));
    ioShiftOut((addr << 8) | (ACTLOW & ~(select | WR)));
    ioShiftOut((addr << 8) | (ACTLOW));
    
    /* チップ処理待ち */        
    
    /* データを出力 */
    /* A0をハイにして待つ */
    ioShiftOut((data << 8) | A0 | (ACTLOW));
    ioShiftOut((data << 8) | A0 | (ACTLOW & ~(select | WR)));
    ioShiftOut((data << 8) | A0 | (ACTLOW));

    /* チップ処理待ち */
}
#endif

/* PSG出力 */
void regPSGOut(int addr, int data)
{
    regOutBase(addr, data, CS_PSG);
}

/* FM2出力 */
void regFM2Out(int addr, int data)
{
    regOutBase(addr, data, CS_FM2);
}

/* FM音源にデータを出力 */
void regFMOut(int addr,int data)
{
    regOutBase(addr, data, CS_FM1);
}

/* ボード消音 */
void boardMute(void)
{
    int i;
    
    /* PSG初期化 */
    regPSGOut(0x00,0);
    regPSGOut(0x01,0);
    
    regPSGOut(0x06, 0x00);
    regPSGOut(0x07, 0x3f); // ALL OFF
    regPSGOut(0x08, 0x00); // CH.A 0
    regPSGOut(0x09, 0x00); // CH.B 0
    regPSGOut(0x0a, 0x00); // CH.C 0
    
    /* MUTE(disable) */
    for(i = 0x20; i < 0x28; i++)
    {
        regFMOut(i, 0x00);
        regFM2Out(i, 0x00);
    }
    
    // KEYOFF
    for(i = 0x00; i < 0x08; i++)
    {
        regFMOut(0x08, i & 0x07);
        regFM2Out(0x08, i & 0x07);
    }
    
    // FORCE RELEASE
    for(i= 0x00; i < 0x20; i++)
    {
        regFMOut(0xE0 + i, 0x0f);        
        regFM2Out(0xE0 + i, 0x0f);
    }
}


/* ボード初期化 */
void boardInit(void)
{
    wait_ms(20);
    /* ICLのみをLOW(アクティブ)にする */
    ioShiftOut(ACTLOW & ~(ICL));    
    wait_ms(150);
    
    /* 元に戻す */
    ioShiftOut(ACTLOW);
    wait_ms(10);
}

typedef unsigned char  byte;
typedef unsigned short word;
typedef unsigned long  dword;


// 変数読み出し(WORD)
word ReadWORD(byte *p)
{
    return
        ((word)p[0]) |
        ((word)p[1])<<8;
}

// 変数読み出し(DWORD)
dword ReadDWORD(byte *p)
{
    return
        ((dword)p[0]) |
        ((dword)p[1])<<8 |
        ((dword)p[2])<<16 |
        ((dword)p[3])<<24;
}

// NLGファイルを開く
int OpenNLG(const char *file)
{    
    byte nlg_hdr[0x60];

    nlg_file = fopen(file, "rb");
        
    if (!nlg_file)
    {
        printf("File open error:%s\n", file);
        return -1;
    }
        
    fread(nlg_hdr, 0x60, 1, nlg_file);
    
    if (memcmp(nlg_hdr, "NLG1", 4) != 0)
    {
        printf("Unknown format!\n");
        fclose(nlg_file);
        return -1;
    }
    
    nlg_version = ReadWORD(nlg_hdr + 4);

    memcpy(nlg_title, nlg_hdr + 8, 64);
    nlg_title[64]=0;

    nlg_baseclk = ReadDWORD(nlg_hdr + 72);
    nlg_tick = ReadDWORD(nlg_hdr + 76);

    nlg_length = ReadDWORD(nlg_hdr + 88);
    nlg_loop_ptr = (long)ReadDWORD(nlg_hdr + 92);
    
    fseek(nlg_file, 0x60, SEEK_SET);
    
    nlg_ctc0 = nlg_ctc3 = 0;
    
    return 0;
}

#if 0


// 変数書き出し(WORD)
inline void WriteWORD(byte *p,word val)
{
    p[0] = (val & 0xff);
    p[1] = ((val>>8) & 0xff);
}

// 変数書き出し(DWORD)
inline void WriteDWORD(byte *p,dword val)
{
    p[0] = (val & 0xff);
    p[1] = ((val>>8) & 0xff);
    p[2] = ((val>>16) & 0xff);
    p[3] = ((val>>24) & 0xff);
}

// 書き込み用NLGファイルを開く
int CreateNLG(const char *file)
{
    byte hdr[0x80];
    
    nlg_file = fopen(file, "wb");
    
    if (!nlg_file)
    {
        printf("File open error:%s\n", file);
        return -1;
    }
    
    memset(hdr, 0, 0x80);
    memcpy(hdr,"NLG1",4);
    
    WriteWORD(hdr + 4, NLG_VER); // Version
    WriteDWORD(hdr + 72, NLG_BASECLK); // BaseCLK
    WriteDWORD(hdr + 76, 0); // Tick
    WriteDWORD(hdr + 88, 0); // Length
    WriteDWORD(hdr + 92, 0); // Loop Pointer
    
    fwrite(hdr, 0x60, 1, nlg_file);

    nlg_ctc0 = nlg_ctc3 = 0;

    return 0;
}


// コマンドの書き込み
void WriteNLG_CMD(int cmd)
{
    if (!nlg_file)
        return;
    
    fputc(cmd, nlg_file);
}

// CTC定数の書き込み
void WriteNLG_CTC(int cmd,int ctc)
{
    if (!nlg_file)
        return;

    fputc(cmd, nlg_file);
    fputc(ctc, nlg_file);
}

// データの書き込み
void WriteNLG_Data(int cmd,int addr,int data)
{
    if (!nlg_file)
        return;

    fputc(cmd, nlg_file);
    fputc(addr, nlg_file);
    fputc(data, nlg_file);
}

#endif

// ファイルを閉じる
void CloseNLG(void)
{
    if (!nlg_file)
        return;

    fclose(nlg_file);

#if defined(__MICROLIB) && defined(__ARMCC_VERSION) // with microlib and ARM compiler
    free(nlg_file);
#endif

    nlg_file = NULL;
}

// データの読み出し
inline int ReadNLG(void)
{
    return fgetc(nlg_file);
}

// ファイルポインタの位置を取得
inline long TellNLG(void)
{
    return ftell(nlg_file);
}

// ファイルポインタの位置を設定
inline void SeekNLG(long pos)
{
    fseek(nlg_file, pos, SEEK_SET);
}

// タイトルの取得
inline char *GetTitleNLG(void)
{
    return nlg_title;
}

// ティックの取得
inline int GetTickNLG(void)
{
    return nlg_tick;
}

// Set Tick
inline void SetTick(void)
{
    nlg_tick = ((nlg_ctc0 * 256) * nlg_ctc3);
    // printf("nlg_tick=%d\n", nlg_tick);
}

// CTC0値の設定
void SetCTC0_NLG(int value)
{
    nlg_ctc0 = value;
    SetTick();
}

// CTC3値の設定
void SetCTC3_NLG(int value)
{
    nlg_ctc3 = value;
    SetTick();
}

// 曲の長さを得る
int GetLengthNLG(void)
{
    return nlg_length;
}

// ループポインタを得る
int GetLoopPtrNLG(void)
{
    return nlg_loop_ptr;
}

// ベースクロックを得る
int GetBaseClkNLG(void)
{
    return nlg_baseclk;
}

#define CMD_PSG  0x00
#define CMD_OPM  0x01
#define CMD_OPM2 0x02
#define CMD_IRQ  0x80

#define CMD_CTC0 0x81
#define CMD_CTC3 0x82


/* 単位を扱いやすいように */
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;

/* NLGを処理するための構造体 */
typedef struct
{
    int base_clk;
    int clk_per_sample;
    
    int freeze;
    int total_samples;
        
    int total_sec;

    int current_samples;
    int tick;
    int tick_sec;
    
    int irq_count;
    int irq_loop;
    long loop_address;
    
    int irq_counter;
    int irq_counter2;
    
} NLG_R;


/* 初期化 */
int initNLG(NLG_R *np, const char *nlg_name)
{
    if (OpenNLG(nlg_name) < 0)
        return -1;
    
    np->base_clk = GetBaseClkNLG();
    
    /* printf("Title:%s %d %d %dsec %d\n",
           GetTitleNLG(),
           GetBaseClkNLG(),
           GetTickNLG(),
           GetLengthNLG(),
           GetLoopPtrNLG());
    */
    
    np->total_samples = 0;
    np->current_samples = 0;
        
    np->total_sec = 0;
    
    np->tick = 0;
    np->tick_sec = 0;
    
    np->freeze = 0;
    
    np->irq_count = 0;
    np->irq_loop = GetLoopPtrNLG();
    np->loop_address = -1;
    
    np->irq_counter = 0;
    np->irq_counter2 = 0;

    return 0;
}

/* PSGへの出力 */
void WritePSG(int addr,int value)
{
    regPSGOut(addr, value);
}

/* FM1への出力 */
void WriteOPM(int addr,int value)
{
    regFMOut(addr, value);
}

/* FM2への出力 */
void WriteOPM2(int addr,int value)
{
    regFM2Out(addr, value);
}

// stop flag
bool g_stop = false;
bool g_prev = false;

void DispNLG(NLG_R *np)
{
    // printf("Time %02d:%02d\r",np->total_sec / 60, np->total_sec % 60);
    // fflush(stdout);
    
    char buf[16];
    sprintf(buf, "%02d:%02d",
        np->total_sec / 60, 
        np->total_sec % 60);
    
    lcd_setCursor(3,1);
    lcd_printStr(buf);
}

// wait until release buttons
void waitButtonRelease(void)
{
    while(!sw_next || !sw_play || !sw_prev);
}

// wait until press buttons
void waitButtonPress(void)
{
    while(sw_next && sw_play && sw_prev);
}

/* NLGの再生 */
int PlayNLG(NLG_R *np, int sec)
{
    int cmd;
    int addr, data;
    int result = 0;
    
    int tick;
    int total_us = 0;
    int us_tick = np->base_clk / 1000000;
    
    printf("start play\n");
    Timer t;
    
    t.start();
    total_us = 0;
    
    // wait until release buttons
    waitButtonRelease();
    
    DispNLG(np);

    while(np->total_sec < sec && !g_stop)
    {
        // push next
        if (!sw_next)
            break;
        
        // push prev
        if (!sw_prev)
        {
            g_prev = true;
            break;
        }

        // push play
        if (!sw_play)
        {
            g_stop = true;
            break;
        }

        /* ウエイトの処理 */
        while (np->tick >= us_tick)
        {
            int us = np->tick / us_tick;
            np->tick -= (us * us_tick);
            
            // 次のタイミングまで待つ
            while(t.read_us() < total_us + us);
            
            total_us += us;
            
            // 1秒ごとにリセットする
            if (total_us >= 1000000)
            {
                total_us -= t.read_us();
                t.reset();
            }
            // DELAY_NEXT(us);
            
            // printf("delay : %dus\n",us);
        }
        
        /* コマンドの読み出し */
        cmd = ReadNLG();
        if (cmd == EOF)
        {
            if (np->loop_address >= 0)
            {
                SeekNLG(np->loop_address);
                cmd = ReadNLG();
            }
            else
            {           
                result = EOF;
                break;
            }
        }
        
        /* コマンドの処理 */
        switch (cmd)
        {
            case CMD_PSG:
                addr = ReadNLG();
                data = ReadNLG();
                WritePSG(addr, data);
                break;
            case CMD_OPM:
                addr = ReadNLG();
                data = ReadNLG();
                WriteOPM(addr, data);
                break;
            case CMD_OPM2:
                addr = ReadNLG();
                data = ReadNLG();
                WriteOPM2(addr, data);
                break;
            case CMD_IRQ:
                tick = GetTickNLG();
                np->tick_sec += tick;
                np->tick += tick;
                np->irq_count++;
                if (np->irq_count == np->irq_loop)
                {
                    /* ループ位置の設定 */
                    np->loop_address = TellNLG();
                    np->loop_address -= 1;
                }
                np->irq_counter++;
                if (np->irq_counter >= 48)
                {
                    np->irq_counter = 0;
                    led1 = !led1;

                    np->irq_counter2++;
                    if (np->irq_counter2 >= 4)
                    {
                        np->irq_counter2 = 0;
                        led2 = !led2;
                    }
                }
            break;
            case CMD_CTC0:
                SetCTC0_NLG(ReadNLG());
            break;
            case CMD_CTC3:
                SetCTC3_NLG(ReadNLG());
            break;
                
        }
        /* 秒情報の表示 */
        while (np->tick_sec >= np->base_clk)
        {
            np->tick_sec -= np->base_clk;
            np->total_sec++;

            DispNLG(np);            
        }
    }
    
    return result;
}

NLG_R n;

int nlg_play(const char *nlg_file)
{
    printf("nlg_play:%s\n",nlg_file);

       /* NLGの初期化 */
    if (initNLG(&n, nlg_file) < 0)
    {
        lcd_printStrY(1, " ERROR!!");
        printf("Failed to init.\n");
        return -1;
    }

    printf("Play..\n");
    
    /* 再生する */
    PlayNLG(&n, 360);
    
    /* mute */
    boardMute();
    
    /* NLGファイルを閉じる */
    CloseNLG();
    
    return 0;
}

void error_sdcard(void)
{
    lcd_printStr2("SD CARD", "ERROR!");
    
    while(1);
}

int get_nlg_file(char *dest, int index)
{
    char *cwd = "/sd/";
    int count = -1;
    DIR *dir = opendir(cwd); 

    dest[0] = 0;
    
    if (dir == NULL)
    {
        return -1;
    } 
    struct dirent *dent;
    
    while(1)
    {
        dent = readdir(dir);
        if (!dent)
            break;

        // resource or hidden file
        if (dent->d_name[0] == '.')
            continue;
            
        char *ext = strrchr(dent->d_name, '.');
        if (!ext)
            continue;

        if (strcasecmp(ext, ".nlg") != 0)
            continue;
        
        if (count < 0)
            count = 0;

        count++;
        
        if (index < 0)
            continue;
        
        if (count <= index)
            continue;         
        
        strcpy(dest, dent->d_name);
        break;
    }
    closedir(dir);
    return count;
}

//
// COM mode
//
void loop_for_com()
{
    Serial pc(USBTX, USBRX);
    
    lcd_printStrY(1,"COM MODE");

    pc.printf("com_mode\n");
    
    while(1)
    {
        unsigned char sw, adr, val;
        
        sw = pc.getc();
        if (sw == 0x00)
            continue;
            
        adr = pc.getc();
        val = pc.getc();
        switch(sw)
        {
            case 1:
                regFMOut(adr, val);
            break;
            case 2:
                regFM2Out(adr, val);
            break;
            case 3:
                regPSGOut(adr, val);
            break;
            case 0x0f:
                pc.baud(9600 * val);
            break;
        }

        // pc.printf("sw = %02x, adr = %02x, val = %02x\n", sw, adr, val);
    }
}

//
// title
//
void putTitle()
{
        lcd_printStr2("NBCTRL", NBCTRL_VER);
        wait(1.5);
}

// 情報の表示
void show_info(int files)
{
    char buf[16];

    Timer t;
    int result_us = 0;
    t.reset();
    t.start();
            
    // actual timing
    regFMOut(0x20, 0x00);
    
    result_us = t.read_us();
    t.stop();
    
    printf("result_us=%dus\n", result_us);            
    sprintf(buf, "R:%dus", result_us); 
    lcd_printStrY(1, buf);
    wait(3);
            
    sprintf(buf, "%8s", __DATE__); 
    lcd_printStrYscr(1, buf);
    wait(3);

    if (files < 0)
        lcd_printStrY(1, "NO FILES");
    else
    {
        char buf[16];
        sprintf(buf, "%3dfiles", files);    
        lcd_printStrY(1, buf);
    }
    wait(3);
    lcd_cls();
}

//
// main 
//
int main() 
{
    bool info_mode = false;
    bool com_mode = false;

    int files = 0;
    
    char file[32];
    char path[48];
    
    ioInit();
    
    wait_ms(20);

    // reset SHIFT REGISTER
    ioShiftOut((0xFF << 8) | (ACTLOW));
    
    // モード
    if (!sw_next)
        info_mode = true;
    if (!sw_prev)
        com_mode = true;
    
    // 初期化とタイトル表示
    lcd_init();
    putTitle();
    
    // ボードの初期化
    boardInit();

    // 通信モードに移行    
    if (com_mode)
            loop_for_com();
        
    files = get_nlg_file(file, -1);
    
    // エラー表示
    if (files < 0)
        error_sdcard();
    
    // 情報モード
    if (info_mode)
        show_info(files);

    
    bool repeat_flag = false;
    int idx = 0;
    while(files > 0)
    {
        char buf[16];
        
        get_nlg_file(file, idx);
        
        sprintf(path, "/sd/%s", file);
        printf("path=%s\n",path);
        
        lcd_cls();
        lcd_printStrY(0, file);

        sprintf(buf, "%2d ", idx + 1);
        lcd_printStrY(1, buf);

        nlg_play(path);
        
        // wait for chattering
        wait(0.25);

        // wait release        
        waitButtonRelease();
                
        // if play button is pressed
        if (g_stop)
        {
            g_stop = false;
            g_prev = false;                

            lcd_printStrY(1, "  STOP  ");

            // wait any button is pressed
            waitButtonPress();
            
            // 
            if (!sw_prev) {
                lcd_printStrY(1, "  PREV  ");
                g_prev = true;
            }
            if (!sw_play)
            {
                lcd_printStrY(1, "  PLAY  ");
                repeat_flag = true;
            }
            if (!sw_next)
            {
                lcd_printStrY(1, "  NEXT  ");
                repeat_flag = false;
            }
            
            // wait release
            waitButtonRelease();
            
            // reset board
            boardInit();
        }
        
        // play prev or next song
        if (g_prev)
        {
            g_prev = false;

            if (idx - 1 >= 0)
                idx--;
            else
                idx = files - 1;

            continue;
        }
        
        if (!repeat_flag) 
        {
            if (idx + 1 < files)
                idx++;
            else
                idx = 0;
        }
        repeat_flag = false;
    }
}