#include #include #include #include #include #include #include #include #include #include #include #include #include // dbcorelib includes #include "waverecorder.h" #include "fifostream.h" #include "utils/datastreamutils.h" // dbguilib includes #include "canvaswidget.h" // nescorelib includes #include "nesemulator.h" #include "emusettings.h" // local includes #include "memorymodel.h" int main(int argc, char **argv) { QApplication app(argc, argv); NesEmulator emulator; const QString path = app.arguments().count() > 1 ? app.arguments().at(1) : QFileDialog::getOpenFileName(nullptr, "Select ROM file...", QString(), "ROM file (*.nes)"); if(path.isEmpty()) return 0; Rom rom; try { rom = Rom::fromFile(path); emulator.load(rom); } catch (const std::exception &e) { QMessageBox::warning(nullptr, "Error while loading rom!", QString::fromStdString(e.what())); return 1; } // Audio recorder WaveRecorder recorder(1, emulator.apu().sampleRate(), "sound.wav"); QObject::connect(&emulator.apu(), &Apu::samplesFinished, &recorder, &WaveRecorder::addSamples); // Live audio playback FifoStream stream; QObject::connect(&emulator.apu(), &Apu::samplesFinished, [&stream](const QVector &samples){ QByteArray buf; buf.reserve(samples.size() * sizeof(qint32)); QDataStream dataStream(&buf, QIODevice::WriteOnly); dataStream.setByteOrder(QDataStream::BigEndian); for(auto sample : samples) dataStream << sample; Q_ASSERT(buf.size() == samples.size() * sizeof(qint32)); stream.write(buf); }); // Display CanvasWidget canvas; canvas.setWindowTitle(QString("%0 - Mapper: %1").arg(QFileInfo(path).fileName()).arg(rom.mapperNumber)); canvas.show(); quint64 frameCounter {}; QObject::connect(&emulator.ppu(), &Ppu::frameFinished, [&canvas, &frameCounter](const std::array &frame){ canvas.setImage(QImage(reinterpret_cast(&frame[0]), Ppu::SCREEN_WIDTH, Ppu::SCREEN_HEIGHT, QImage::Format_RGB32)); QFile file(QString("frames/%0.bmp").arg(frameCounter++)); if(!file.open(QIODevice::WriteOnly)) return; constexpr quint32 bitmapSize = Ppu::SCREEN_WIDTH * Ppu::SCREEN_HEIGHT * sizeof(quint32); QDataStream dataStream(&file); dataStream.setByteOrder(QDataStream::LittleEndian); //BMP Header dataStream << quint16(0x4D42); //BM-Header dataStream << quint32(54 + bitmapSize); //File size dataStream << quint32(0); //Unused dataStream << quint32(54); //Offset to bitmap data //DIB Header dataStream << quint32(40); //DIP Header size dataStream << Ppu::SCREEN_WIDTH; //width dataStream << Ppu::SCREEN_HEIGHT; //height dataStream << quint16(1); //Number of color planes dataStream << quint16(32); //Bits per pixel dataStream << quint32(0); //No compression; dataStream << bitmapSize; //Size of bitmap data dataStream << quint32(2835); //Horizontal print resolution dataStream << quint32(2835); //Horizontal print resolution dataStream << quint32(0); //Number of colors in palette dataStream << quint32(0); //Important colors dataStream << frame; }); MemoryModel model(emulator); QObject::connect(&emulator.ppu(), &Ppu::frameFinished, &model, &MemoryModel::refresh); QTableView tableView; { QHeaderView *horizontalHeader = tableView.horizontalHeader(); horizontalHeader->setSectionResizeMode(QHeaderView::Fixed); horizontalHeader->setDefaultSectionSize(0); } { QHeaderView *verticalHeader = tableView.verticalHeader(); verticalHeader->setSectionResizeMode(QHeaderView::Fixed); verticalHeader->setDefaultSectionSize(0); } tableView.setModel(&model); tableView.show(); QTimer timer; QObject::connect(&timer, &QTimer::timeout, &emulator, &NesEmulator::emuClockFrame); timer.setInterval(EmuSettings::emuTimeFramePeriod); // HACK: overclocking a little bit when audio device reaches near end QObject::connect(&emulator.ppu(), &Ppu::frameFinished, [&stream, &timer](){ int speed = EmuSettings::emuTimeFramePeriod; if(stream.bytesAvailable() / sizeof(qint32) < 4096) speed = double(speed) * 0.9; if(timer.interval() != speed) timer.setInterval(speed); }); QAudioFormat format; format.setSampleRate(emulator.apu().sampleRate()); format.setChannelCount(1); format.setSampleSize(sizeof(qint32) * 8); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::SignedInt); { QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); if (!info.isFormatSupported(format)) { qFatal("Your soundcard does not support the needed output format!"); return -1; } } QAudioOutput output(format); output.start(&stream); timer.start(); return app.exec(); }