惰性Log

技術系の話題を中心に書いています

NeovimからVSCodeのlaunch.jsonやtasks.jsonでタスクを実行

概要

Visual Studio Code(以下VSCode)ではタスク管理に.vscode/tasks.jsonを、起動やデバッグに.vscode/launch.jsonへ書いて実行することが多いと思います。当然ですがNeovimではこれらのタスクをそのまま実行することはできませんので、Neovimを使う際はmakefileシェルスクリプトなどにタスクを書いているのではないでしょうか。
VSCodeとNeovim(拡張機能でない)を同時に使用している筆者のような場合や共同プロジェクトの場合、NeovimからVSCodeのタスクランナーが使用できれば管理しやすいのではないかと思います。
今回は、NeovimからVSCode用のタスクを実行する方法を紹介します。

使用するプラグインなど

zenn.dev

インストール

筆者はプラグインマネージャにLazy.nvimを使用しているのでその例です。
またDapの設定はC++で使用するCodeLLDBの設定です。

{
    { -- debug
        "mfussenegger/nvim-dap",
        dependencies = {
            -- 無くても可
            "rcarriga/nvim-dap-ui",
        },
        config = function()
            -- 設定1
        end,
    },
    { -- vscode tasks
        "EthanJWright/vs-tasks.nvim",
        dependencies = {
            "nvim-lua/popup.nvim",
            "nvim-lua/plenary.nvim",
            "nvim-telescope/telescope.nvim",
        },
    },
    -- 設定2
}

設定

nvim-dap.nvim(設定1)

  config = function()
    -- 設定1
    local dap = require("dap")
    dap.adapters = {
        -- LSPの設定
        lldb = {
            type = "server",
            port = "${port}",
            executable = {
                command = vim.fn.stdpath("data") .. "/mason/packages/codelldb/extension/adapter/codelldb",
                args = { "--port", "${port}" },
            },
        },
    }
    dap.configurations = {
        cpp = {},
    }

    -- 
    -- キーマップの登録
    vim.keymap.set("n", "<F5>", dap.continue, { desc = "Debug: Start/Continue" })
    vim.keymap.set("n", "<F1>", dap.step_into, { desc = "Debug: Step Into" })
    vim.keymap.set("n", "<F2>", dap.step_over, { desc = "Debug: Step Over" })
    vim.keymap.set("n", "<F3>", dap.step_out, { desc = "Debug: Step Out" })
    vim.keymap.set("n", "<leader>b", dap.toggle_breakpoint, { desc = "Debug: Toggle Breakpoint" })
    vim.keymap.set("n", "<leader>B", function()
        dap.set_breakpoint(vim.fn.input("Breakpoint condition: "))
    end, { desc = "Debug: Set Breakpoint" })

    -- .vscode/launch.jsonの読み込み
    require("dap.ext.vscode").load_launchjs(nil, { lldb = { "c", "cpp", "" } })


    -- DAP-UIの設定(省略可)
    local dapui = require("dapui") -- 任意
    vim.fn.sign_define("DapBreakpoint", { text = "", texthl = "DapBreakpointTextHl" })
    vim.fn.sign_define("DapStopped", { text = "", texthl = "DapStoppedTextHl" })
    dapui.setup({
        controls = {
            element = "repl",
            enabled = true,
            icons = {
                pause = "⏸",
                play = "▶",
                step_into = "⏎",
                step_over = "⏭",
                step_out = "⏮",
                step_back = "b",
                run_last = "▶▶",
                terminate = "⏹",
                disconnect = "⏏",
                enabled = "●",
            },
        },
        element_mappings = {},
        expand_lines = true,
        floating = {
            border = "single",
            mappings = {
                close = { "q", "<Esc>" },
            },
        },
        force_buffers = true,
        icons = {
            collapsed = "",
            current_frame = "",
            expanded = "",
        },
        layouts = {
            {
                elements = {
                    {
                        id = "scopes",
                        size = 0.25,
                    },
                    {
                        id = "breakpoints",
                        size = 0.25,
                    },
                    {
                        id = "stacks",
                        size = 0.25,
                    },
                    {
                        id = "watches",
                        size = 0.25,
                    },
                },
                position = "left",
                size = 40,
            },
            {
                elements = {
                    {
                        id = "repl",
                        size = 0.5,
                    },
                    {
                        id = "console",
                        size = 0.5,
                    },
                },
                position = "bottom",
                size = 10,
            },
        },
        mappings = {
            edit = "e",
            expand = { "<CR>", "<2-LeftMouse>" },
            open = "o",
            remove = "d",
            repl = "r",
            toggle = "t",
        },
        render = {
            indent = 1,
            max_value_lines = 100,
        },
    })
    vim.keymap.set("n", "<F7>", dapui.toggle, { desc = "Debug: See last session result." })
    dap.listeners.after.event_initialized["dapui_config"] = dapui.open
    dap.listeners.before.event_terminated["dapui_config"] = dapui.close
    dap.listeners.before.event_exited["dapui_config"] = dapui.close
 end

require("dap.ext.vscode").load_launchjs(nil, { lldb = { "c", "cpp", "" } })ですが、lldbの部分、VSCodeのCodelldbのtype名はlldbだったのでそちらに合わせます。

vs-tasks.nvim(設定2)

ショートカットからtaskを実行出来るようにします。

-- 設定2
    vim.cmd([[
nnoremap <Leader>ta :lua require("telescope").extensions.vstask.tasks()<CR>
nnoremap <Leader>ti :lua require("telescope").extensions.vstask.inputs()<CR>
nnoremap <Leader>th :lua require("telescope").extensions.vstask.history()<CR>
nnoremap <Leader>tl :lua require('telescope').extensions.vstask.launch()<cr>
]]),

実行

F5でデバッグ(launch.json)の選択・開始、(leader)taでタスク(tasks.json)を実行出来ます。デバッガが不要で起動だけするなら(leader)tlで可能です。

参考

github.com

zenn.dev

コンピュータの計算誤差の種類のまとめ

もう記憶にないが、以前何かしらの講義のメモを下書きに残しておいたらしい。勿体ないので公開する。
内容的に恐らく1年次の時の講義だろう。

計算誤差

代入誤差

 2進数で表せない値の代入  0.1, π, e(ネイピア数)など

丸め誤差

 有限桁の小数の範囲を超えるものを扱う際の切り捨て、切り上げ、0捨1入などによって有限桁に納める際に生じる誤差

打ち切り誤差

 計算を何らかの理由により途中で終了させた場合の誤差

オーバーフロー(overflow)

 数値の絶対値が大きすぎて、表現可能な範囲を超え生じる誤差。ゲームで経験値が65535を超えると0になるなど。

アンダーフロー(underflow)

 数値の絶対値が小さすぎて、表現ができないときに生じる誤差。floatで限りなく0に近い値など。

情報落ち

 絶対値の差が大きな2つの数の加減算で、絶対値の小さい方が無視され生じる誤差。

桁落ち

 値の近い2つの数の減算によって、有効数字の桁数が急激に減少し生じる誤差。

プログラマ向け音楽サイト「musicForProgramming」

少し前から気に入っているサイトがある。

https://musicforprogramming.net/

musicForProgrammingというサイト。
イカしたインタフェースとアニメーションをしており(主観)、執筆時点では64曲収録されている。
どんな曲があるかというと。主にアンビエントテクノ系がメインである。
インディーズ系の作曲者たちが曲を投稿していたらしい。現在は募集は停止している模様。
これらの楽曲が無償で提供され、ダウンロードも自由に行うことが出来る。
気に入ったものがあったら、作曲者の他の曲を買ってくれというスタンスである。
特別なにか曲がプログラムの記述することにフォーカスされたわけでもなく、単にアンビエントやテクノが作業に集中しやすいというだけのことだが、個人的に好みの曲が多いので気に入っている。
個人的には入眠にも使えるな。と思う。

VSCodeとWindows TerminalでDeveloper PowerShellを起動する

TL;DR

Windows Terminal

C:\Program Files\PowerShell\7\pwsh.exe  -noe -c                 "&{Import-Module 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/Tools/Microsoft.VisualStudio.DevShell.dll'; Enter-VsDevShell  677f3018}"

Visual Studio Code

  "terminal.integrated.profiles.windows": {
        "Developer PowerShell for VS 2019": {
            "source": "PowerShell",
            "icon": "terminal-powershell",
            "font": {
                "face": "CaskaydiaCove Nerd Font"
            },
            "args": [
                "-noe",
                "-c",
                "&{Import-Module 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/Tools/Microsoft.VisualStudio.DevShell.dll'; Enter-VsDevShell  677f3018}"
            ]
        }
    }

Windows Terminalは設定の「コマンドライン」に、VSCodeはsettings.jsonに記載する。

Effekseerをemscriptenで動かす その2 ファイルローダ編

前回、ファイルローダがどうのこうの言ってから2ヶ月が経過してしまった。
Effekseerの標準のテクスチャローダではテクスチャを読み込むことができない。が、インターフェースが提供されているので各々で実装することはできる。
NenEngineに実装されている。
インターフェースは以下のようになった。

https://github.com/Astomih/NenEngine/blob/main/src/Nen/Effect/CustomTextureLoader.hgithub.com

#include <Effekseer.h>
class CustomTextureLoader : public Effekseer::TextureLoader
{
private:
    ::Effekseer::Backend::GraphicsDevice* graphicsDevice_ = nullptr;

public:
    CustomTextureLoader(::Effekseer::Backend::GraphicsDevice* graphicsDevice) : graphicsDevice_(graphicsDevice) {}

    ~CustomTextureLoader() = default;

public:
    Effekseer::TextureRef Load(const EFK_CHAR* path, Effekseer::TextureType textureType) override;

    void Unload(Effekseer::TextureRef data) override {}
}

実装は以下。
https://github.com/Astomih/NenEngine/blob/main/src/Nen/Effect/CustomTextureLoader.cppgithub.com

#include "CustomTextureLoader.h"
#include <Nen.hpp>
#include <EffekseerRenderer/EffekseerRendererGL.MaterialLoader.h>
#include <EffekseerRenderer/EffekseerRendererGL.RendererImplemented.h>
#include <EffekseerRenderer/GraphicsDevice.h>
#include <fstream>
#include <SDL.h>
#include <SDL_image.h>

Effekseer::TextureRef CustomTextureLoader::Load(const EFK_CHAR* path, Effekseer::TextureType textureType)
{
    std::array<char, 260> path8;
    Effekseer::ConvertUtf16ToUtf8(path8.data(), static_cast<int32_t>(path8.size()), path);
    SDL_Surface* surf = IMG_Load(path8.data());
    if (!surf)
    {
        nen::Logger::Error("Failed to load \"%s\". IMG_Error: %s", std::string(path8.data()), IMG_GetError());
    }
    ::SDL_LockSurface(surf);
    auto formatbuf = ::SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888);
    formatbuf->BytesPerPixel = 4;
    auto imagedata = ::SDL_ConvertSurface(surf, formatbuf, 0);
    SDL_UnlockSurface(surf);
    // Generate a GL texture
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA,
        GL_UNSIGNED_BYTE, imagedata->pixels);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    std::string pathStr = path8.data();
    auto backend = static_cast<EffekseerRendererGL::Backend::GraphicsDevice*>(graphicsDevice_)
        ->CreateTexture(texture, false, [texture, pathStr]() -> void
            {
                glDeleteTextures(1, &texture);
            });
    auto textureData = Effekseer::MakeRefPtr<Effekseer::Texture>();
    textureData->SetBackend(backend);
    nen::Logger::Info("Loaded \"%s\".", pathStr);
    SDL_FreeSurface(surf);
    SDL_FreeSurface(imagedata);
    SDL_FreeFormat(formatbuf);
    return textureData;
}

基本的には、画像やマテリアルをこちら側で読み込み、各種APIの登録を済ませてからCreateTextureを呼ぶ。

Effekseerをemscriptenで動かす

はじめに

やり方としては2種類ある。(多分)

  1. jsファイルとして出力し、Three.jsなどのJavaScript用グラフィックスライブラリを使用して動かす
  2. 静的リンクライブラリとして出力し、既存のC++プロジェクトとともにリンクして直接動かす

今回は、2番の方法を紹介する。なお、1番の方法に関してはわざわざビルドする必要すら無く、ビルド済みのjsファイルが配布されているのでそちらを利用すれば良い。

github.com

2番の方法はやり方が載っていなかったので表示するまでに2日くらい掛かってしまった。

静的リンクライブラリのビルド

まずは普通に先程のリポジトリをcloneする。

git clone https://github.com/effekseer/EffekseerForWebGL
cd EffekseerForWebGL
git submodule update --init

src内にあるCMakeLists.txtを開き、

# Set output file extension
set(CMAKE_EXECUTABLE_SUFFIX ".js")

# Add build settings
add_executable(effekseer.core ${effekseer_src})

という記述を消し(このままビルドすると上記のjsファイルをビルド出来る)、代わりに

# Add build settings
add_library(effekseer STATIC ${effekseer_src})

としておく。 あとは

emsdk install latest
emsdk activate latest
emcmake cmake .
emmake make

とすればlibeffekseer.aが出力される

ファイルローダ

src/cpp/main.cppのような記述でなくても、本家のリポジトリのexampleのように記述すれば動く。しかし、ファイル読み込みについてはどうやら既存のローダでは読み込めないようなので自作する必要がある。
そこで、src/cpp/CustomFile.hを参考にした。EM_ASM内のコードが機能しなかったので、素直にstd::fstreamなどで読み込んだ。
テクスチャに関しては、読み込んだファイルをSDL_Imageを介して無理やり読み込ませた。マテリアルの読み込み等についてはまだ実装していない。

文字で説明なんかせずにコードを載せれば良いのだろうが、この辺りは未だ調査中なので不明な点が多い。
判明次第随時更新するつもりだが、とりあえず今回はこのへんで。

OpenAL実装ライブラリ、MojoAL

オーディオAPIとして有名なOpenALは、扱いやすくて人気があるがライセンスがLGPLなので扱いが難しい。
そこで近年、zlibライセンスで開発されているオープンソースのSDL2上でOpenALAPIが実装された。
開発したのはSDLの製作者でもあるRyan C Gordon氏。
使用するにはSDL2.0.6以降のオーディオAPIが必要となる。

icculus.org

SDL2の機能を使用しているため、対応している音声フォーマットはwavファイルのみである。 OpenAL自体はフォーマットの規定をしていない。何らかの形で音声データを読み込んで渡すだけである。 この実装の特徴は、SDL2に対応したプラットフォームならどこでも使える点(OpenAL自体移植されまくっているが、SDL2もモバイルからWebにゲーム機まで色々)とスレッドセーフな点だと思われる。
オーディオについて詳しくないので、詳細は上記のホームページとソースコードのコメントを読む方が良い。

3Dサウンドを単にミキシングするだけの私にとってはFMODなどのオーディオエンジンとか使うよりも敷居が低いし扱いやすいのでとてもありがたいライブラリ。

追記:  AL_ORIENTATIONでListenerの向きを変更できるはずだが、音源のLRがスムーズに切り替わらない。
SDL2のオーディオAPIか、OSか、ドライバか、はたまたMojoAL自体の問題だと思われるが、あまり使えそうにない。なお、純正のOpenAL実装で試したところこちらは上手く鳴ったので、プログラム側の問題ではないと思われる。