Qt 5.3.1をいじってみた その2

AndroidでもSDLが不要となったため、QtMultimediaを利用してWindowsAndroid両方で実行出来るソースコードに書き直して行きます。

f:id:tanam:20140816214144p:image:w360

qtmain.cpp

/*
	Skelton for retropc emulator

	Qt Version : tanam
	Date   : 2013.05.18 -
*/

#include "emu.h"

int main(int argc, char **argv)
{
    // qt init
    QApplication app(argc, argv);

    // load config
    load_config();

    // create window
    EMU* emu;
    emu=new EMU();
    emu->show();
    emu->resize(emu->get_window_width(config.window_mode), emu->get_window_height(config.window_mode));
    QSize size = emu->size();
    emu->x=(size.width() - emu->get_window_width(config.window_mode)) / 2;
    emu->y=(size.height() - emu->get_window_height(config.window_mode)) /2 ;
    // main loop
    return app.exec();
}

qt_input.cpp

/*
	Skelton for retropc emulator

    QT Version : tanam
	Date   : 2013.05.18 -
*/

#include "emu.h"
#include "vm/vm.h"
#include "fifo.h"
#include "fileio.h"

#define KEY_KEEP_FRAMES 3
#define COUNTOF(arr) (sizeof(arr) / sizeof((arr)[0]))

// QT -> VR Keycode Table
std::map<int, int> VKTable;
static const struct {
int InKey;       // QT Keycode
int VKey;        // VR Keycode
} VKeyDef[] = {
{ Qt::Key_unknown,      0xFF },
{ Qt::Key_1,            '1' }, // 1 !
{ Qt::Key_Exclam,       '1' }, // 1 !
{ Qt::Key_2,            '2' }, // 2 "
{ Qt::Key_QuoteDbl,     '2' }, // 2 "
{ Qt::Key_3,            '3' }, // 3 #
{ Qt::Key_NumberSign,   '3' }, // 3 #
{ Qt::Key_4,            '4' }, // 4 $
{ Qt::Key_Dollar,       '4' }, // 4 $
{ Qt::Key_5,            '5' }, // 5 %
{ Qt::Key_Percent,      '5' }, // 5 %
{ Qt::Key_6,            '6' }, // 6 &
{ Qt::Key_Ampersand,    '6' }, // 6 &
{ Qt::Key_7,            '7' }, // 7 '
{ Qt::Key_Apostrophe,   '7' }, // 7 '
{ Qt::Key_8,            '8' }, // 8 (
{ Qt::Key_ParenLeft,    '8' }, // 8 (
{ Qt::Key_9,            '9' }, // 9 )
{ Qt::Key_ParenRight,   '9' }, // 9 )
{ Qt::Key_0,            '0' }, // 0
{ Qt::Key_Minus,       0xBD }, // - =
{ Qt::Key_Equal,       0xBD }, // - =
{ Qt::Key_AsciiCircum, 0xDE }, // ^ ~
{ Qt::Key_AsciiTilde,  0xDE }, // ^ ~
{ Qt::Key_Backspace,   0x2E }, // Delete
{ Qt::Key_Insert,      0x2D }, // Insert
{ Qt::Key_Escape,      0x1B }, // ESC
{ Qt::Key_Q,            'Q' }, // q Q
{ Qt::Key_W,            'W' }, // w W
{ Qt::Key_E,            'E' }, // e E
{ Qt::Key_R,            'R' }, // r R
{ Qt::Key_T,            'T' }, // t T
{ Qt::Key_Y,            'Y' }, // y Y
{ Qt::Key_U,            'U' }, // u U
{ Qt::Key_I,            'I' }, // i I
{ Qt::Key_O,            'O' }, // o O
{ Qt::Key_P,            'P' }, // p P
{ Qt::Key_At,          0xC0 }, // @ `
{ Qt::Key_QuoteLeft,   0xC0 }, // @ `
{ Qt::Key_BracketLeft, 0xDB }, // [ {
{ Qt::Key_BraceLeft,   0xDB }, // [ {
{ Qt::Key_Return,      0x0D }, // CR
{ Qt::Key_Up,          0x26 }, // ↑
{ Qt::Key_Down,        0x28 }, // ↓
{ Qt::Key_Left,        0x25 }, // ←
{ Qt::Key_Right,       0x27 }, // →
{ Qt::Key_Control,     0x11 }, // L-Ctrl
{ Qt::Key_A,            'A' }, // a A
{ Qt::Key_S,            'S' }, // s S
{ Qt::Key_D,            'D' }, // d D
{ Qt::Key_F,            'F' }, // f F
{ Qt::Key_G,            'G' }, // g G
{ Qt::Key_H,            'H' }, // h H
{ Qt::Key_J,            'J' }, // j J
{ Qt::Key_K,            'K' }, // k K
{ Qt::Key_L,            'L' }, // l L
{ Qt::Key_Semicolon,   0xBB }, // ; +
{ Qt::Key_Plus,        0xBB }, // ; +
{ Qt::Key_Colon,       0xBA }, // : *
{ Qt::Key_Asterisk,    0xBA }, // : *
{ Qt::Key_BracketRight,0xDD }, // ] }
{ Qt::Key_BraceRight,  0xDD }, // ] }
{ Qt::Key_Shift,       0x10 }, // L-Shift
{ Qt::Key_Z,            'Z' }, // z Z
{ Qt::Key_X,            'X' }, // x X
{ Qt::Key_C,            'C' }, // c C
{ Qt::Key_V,            'V' }, // v V
{ Qt::Key_B,            'B' }, // b B
{ Qt::Key_N,            'N' }, // n N
{ Qt::Key_M,            'M' }, // m M
{ Qt::Key_Comma,       0xBC }, // , <
{ Qt::Key_Less,        0xBC }, // , <
{ Qt::Key_Period,      0xBE }, // . >
{ Qt::Key_Greater,     0xBE }, // . >
{ Qt::Key_Slash,       0xBF }, // / ?
{ Qt::Key_Question,    0xBF }, // / ?
{ Qt::Key_End,         0xDC }, // EN
{ Qt::Key_F1,          0x70 }, // F1
{ Qt::Key_F2,          0x71 }, // F2
{ Qt::Key_F3,          0x72 }, // F3
{ Qt::Key_F4,          0x73 }, // F4
{ Qt::Key_F5,          0x74 }, // F5
{ Qt::Key_F6,          0x75 }, // KANA
{ Qt::Key_F7,          0x76 }, // KATA
{ Qt::Key_F8,          0x77 }, // GRAPH
{ Qt::Key_F9,          0x78 }, // STOP
{ Qt::Key_Space,       0x20 }, // Space
{ Qt::Key_Home,        0x24 }, // Home
{ Qt::Key_Delete,      0x2E }, // Delete
{ Qt::Key_PageUp,      0x21 }, // PAGE
{ Qt::Key_PageDown,    0x22 }, // PAGE
{ Qt::Key_Tab,         0x09 }, // TAB
{ Qt::Key_Backslash,   0xE2 }, // RO
{ Qt::Key_Menu,        0x1B }, // ESCAPE
{ Qt::Key_Alt,         0x1B }, // ALT
{ Qt::Key_AudioRewind, 0x77 }, // GRAPH
{ Qt::Key_Forward,     0x78 }, // STOP
{ 0,                   0x1B }, // MENU
};

int OSD_ConvertKeyCode( int scode )
{
    if(VKTable.count(scode) == 0){
        qDebug("keycode %x unknown\n", scode);
        return 0xff;
    }
    return VKTable[scode];
}

void EMU::keyPressEvent(QKeyEvent *event)
{
	bool keep_frames = false;
	int code = OSD_ConvertKeyCode(event->key());
    key_status[code] = keep_frames ? KEY_KEEP_FRAMES : 0x80;
    if (code == 0xFF) ShowPopup();
    return;
}

void EMU::keyReleaseEvent(QKeyEvent *event)
{
	int code = OSD_ConvertKeyCode(event->key());
/// qDebug("r:keycode %x -> %x\n", event->key(), code);
	bool keep_frames = false;
	if (key_status[code]) key_status[code] &= 0x7f;
    return;
}

void EMU::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton) ShowPopup();
    return;
}

void EMU::initialize_input()
{
	// initialize status
	memset(key_status, 0, sizeof(key_status));
	memset(joy_status, 0, sizeof(joy_status));
	// initialize keycode convert table
	for( int i=0; i < COUNTOF(VKeyDef); i++ )
		VKTable[VKeyDef[i].InKey] = VKeyDef[i].VKey;
}

qt_screen.cpp

/*
	Skelton for retropc emulator

    QT Version : tanam
	Date   : 2013.05.18 -
*/

#include "emu.h"

QAction* addCommand(QMenu* menu, QString label, int id, bool checkable = false)
{
    QAction* action = menu->addAction(label);
    action->setProperty("MenuID", id);
    action->setCheckable(checkable);
    return action;
}

void EMU::ShowPopup()
{
    QSize size = this->size();
    QAction* selectedAction = NULL;
    QMenu menu;
    menu.setStyleSheet("* {background-color: white; color: black; selection-color: white; selection-background-color: black}");
    QMenu* systemMenu = menu.addMenu("Control");
    menu.addSeparator();
    addCommand(systemMenu, "Reset", ID_RESET);
    systemMenu->addSeparator();
    addCommand(systemMenu, "Exit", ID_EXIT);
    QMenu* cartMenu1 = menu.addMenu("CART");
    addCommand(cartMenu1, "Insert", ID_OPEN_CART1);
    addCommand(cartMenu1, "Eject", ID_CLOSE_CART1);
    QMenu* diskMenu1 = menu.addMenu("FD1");
    addCommand(diskMenu1, "Insert", ID_OPEN_FD1);
    addCommand(diskMenu1, "Eject", ID_CLOSE_FD1);
    QMenu* tapeMenu = menu.addMenu("CMT");
    addCommand(tapeMenu, "Play", ID_PLAY_TAPE);
    addCommand(tapeMenu, "Rec", ID_REC_TAPE);
    tapeMenu->addSeparator();
    addCommand(tapeMenu, "Close", ID_CLOSE_TAPE);
    QMenu* screenMenu = menu.addMenu("Screen");
    addCommand(screenMenu, "Window x1", ID_SCREEN_WINDOW1);
    addCommand(screenMenu, "Window x2", ID_SCREEN_WINDOW2);
    addCommand(screenMenu, "Window x3", ID_SCREEN_WINDOW3);
    addCommand(screenMenu, "Window x4", ID_SCREEN_WINDOW4);
    selectedAction = menu.exec(QCursor::pos());
    QString path=NULL;
    if (selectedAction != NULL) {
        int id = selectedAction->property("MenuID").value<int>();
        switch (id) {
        case ID_RESET:
            reset();
            break;
        case ID_EXIT:
            exit(0);
        case ID_OPEN_CART1:
            path=QFileDialog::getOpenFileName(NULL, "Game Cartridge", "/sdcard/rom/", "*.*");
            if (path!=NULL) open_cart(0, (_TCHAR *)path.toStdString().c_str());
            break;
        case ID_SCREEN_WINDOW1:
        case ID_SCREEN_WINDOW2:
        case ID_SCREEN_WINDOW3:
        case ID_SCREEN_WINDOW4:
            config.window_mode = (id - ID_SCREEN_WINDOW1) + 1;
            this->resize(get_window_width(config.window_mode), get_window_height(config.window_mode));
            size=this->size();
            x=(size.width() - get_window_width(config.window_mode)) / 2;
            y=(size.height() - get_window_height(config.window_mode)) /2 ;
            break;
        case ID_OPEN_FD1:
            path=QFileDialog::getOpenFileName(NULL, "Floppy Disk Image [Insert]", "/sdcard/disk/", "*.*");
            open_disk(0, (_TCHAR *)path.toStdString().c_str(),0);
            break;
        case ID_CLOSE_FD1:
            close_disk(0);
            break;
        case ID_PLAY_TAPE:
            path=QFileDialog::getOpenFileName(NULL, "Play Tape Image [Insert]", "/sdcard/tape/", "*.*");
            play_tape((_TCHAR *)path.toStdString().c_str());
            break;
        case ID_REC_TAPE:
            path=QFileDialog::getOpenFileName(NULL, "Rec Tape Image [Insert]", "/sdcard/tape/", "*.*");
            rec_tape((_TCHAR *)path.toStdString().c_str());
            break;
        case ID_CLOSE_TAPE:
            close_tape();
            break;
        }
    }
    return;
}

void EMU::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
#if defined(_PC6601) || defined(_PC6001MK2) || defined(_PC6601SR) || defined(_PC6001MK2SR)
#if defined(_PC6601SR) || defined(_PC6001MK2SR)
    QImage img(static_cast<uchar*>(scrbuf), 640, 400, QImage::Format_RGB32);
#else
    QImage img(static_cast<uchar*>(scrbuf), 320, 200, QImage::Format_RGB32);
#endif
#else
    QImage img(static_cast<uchar*>(scrbuf), 256, 192, QImage::Format_RGB32);
#endif
    QImage imgscaled=img.scaled(get_window_width(config.window_mode), get_window_height(config.window_mode));
    painter.drawPixmap(x, y, QPixmap::fromImage(imgscaled));
    return;
}

void EMU::draw_screen()
{
    vm->draw_screen();
}

scrntype* EMU::screen_buffer(int y)
{
    return (scrntype*)(scrbuf) + screen_width * y;
}

void EMU::initialize_screen()
{
    screen_width = SCREEN_WIDTH;
    screen_height = SCREEN_HEIGHT;
    window_width = WINDOW_WIDTH;
    window_height = WINDOW_HEIGHT;
    scrbuf=(unsigned char *)malloc(screen_width * screen_height * 4);
    memset(scrbuf, 0, screen_width * screen_height * 4);
}

 void EMU::release_screen()
{
    free(scrbuf);
}
 
int EMU::get_window_width(int mode)
{
    return window_width * mode;
}

int EMU::get_window_height(int mode)
{
    return window_height * mode;
}

qt_sound.cpp

/*
    Skelton for retropc emulator

    QT Version : tanam
    Date   : 2013.05.18 -
*/

#include "emu.h"
#include "vm/vm.h"
#include "fileio.h"

/* sound */
#include <QtCore>
#include <QtWidgets>
#include <QtMultimedia>

#define SOUND_FREQUENCY    48000
#define SOUND_SAMPLES_SIZE  2612

QPointer<QIODevice> audioBuffer = NULL;
QPointer<QAudioOutput> audioOutput = NULL;

void EMU::initialize_sound()
{
    sound_ok = sound_started = now_mute = false;

    qRegisterMetaType<QAudio::State>();

    QAudioFormat format;
    format.setCodec("audio/pcm");
    format.setChannelCount(2);
    format.setSampleRate(SOUND_FREQUENCY);
    format.setSampleSize(16);
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    if(audioOutput){
        audioOutput->deleteLater();
    }

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
    if (!info.isFormatSupported(format)) {
        qWarning()<<"raw audio format not supported by backend, cannot play audio.";
        format = info.nearestFormat(format);
    }

    audioOutput = new QAudioOutput(info, format);

    audioOutput->moveToThread(qApp->thread());
    audioOutput->setParent(qApp);

    if(audioOutput){
        if(audioOutput->state() == QAudio::SuspendedState){
            audioOutput->resume();
        } else {
            audioBuffer = audioOutput->start();
            sound_ok = sound_started = true;
        }
    }
}

void EMU::release_sound()
{
    if(audioOutput){
        audioOutput->stop();
    }

    return;
}

void EMU::update_sound(int* extra_frames)
{
    *extra_frames = 0;
    now_mute = false;
    if(sound_ok) {
        // sound buffer must be updated
        uint16* sound_buffer = vm->create_sound(extra_frames);
        if(audioBuffer){
            audioBuffer->write((const char*)sound_buffer, SOUND_SAMPLES_SIZE * 2 * sizeof(short));
        }
    }
    return;
}

void EMU::mute_sound()
{
    if(sound_ok) {
       if (now_mute) {
           if(audioOutput){
               audioOutput->suspend();
           }
           now_mute = false;
       } else {
           if(audioOutput){
               if(audioOutput->state() == QAudio::SuspendedState){
                   audioOutput->resume();
               } else {
                   audioBuffer = audioOutput->start();
               }
           }
       }
    }
}