top of page

- Detail -

RADNODZのUnreal Engine 5用プラグインを使用してRIGDOCKSのAPIを使用し、Reaperを操作するアプリを作成します。

本プロジェクトではReaperのトラック情報を取得し、それをUnreal Engine 5で作成したUIパーツで表示します。

- Images -

- Link -

- Youtube -

- Sources -

RadnodzDemoWidget.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "RadnodzDemoWidget.generated.h"

class UButton;
class UEditableTextBox;
class UTextBlock;
class URadnodzClient;
class UScrollBox;
class UTrackCardWidget;

/**
 * REAPER に接続し、選択中のメディアアイテムのうち 1 つ目の名前を変更するデモ画面。
 */
UCLASS()
class REAPERCONTROLLER_API URadnodzDemoWidget : public UUserWidget
{
	GENERATED_BODY()

protected:
    virtual void NativeConstruct() override;
    virtual void NativeDestruct() override;

    // ---- UMG のパーツと結びつける ----
    // ここの変数名は、Widget Blueprint 側のパーツ名と完全に一致させること
    UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
    TObjectPtr<UEditableTextBox> IpTextBox;

    UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
    TObjectPtr<UEditableTextBox> PortTextBox;

    UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
    TObjectPtr<UButton> ConnectButton;

    UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
    TObjectPtr<UTextBlock> StatusText;

    UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
    TObjectPtr<UScrollBox> TrackList;

    UPROPERTY(BlueprintReadWrite)
    TObjectPtr<URadnodzClient> Client;

    UPROPERTY(EditDefaultsOnly, Category = "UI|TrackCard")
    TSubclassOf<UTrackCardWidget> TrackCardClass;

private:
    UFUNCTION()
    void OnConnectClicked();

    /** 未接続なら Connect する。成功したら true。失敗理由は StatusText に出す。 */
    bool EnsureConnected();

    void SetStatus(const FString& Message);

    // エラー情報からステータス表示
    void DisplayError();

    // トラックリスト取得
    void GetTrackList();


};

RadnodzDemoWidget.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "RadnodzDemoWidget.h"
#include "TrackCardWidget.h"

#include "RadnodzClient.h"
#include "RigdocksError.h"
#include "RigdocksSilver_Track.h" // トラック操作


#include "Components/Button.h"
#include "Components/EditableTextBox.h"
#include "Components/TextBlock.h"
#include "Components/ScrollBox.h"

namespace
{
    // 選択中のメディアアイテムのうち 1 つ目
    constexpr int FirstSelectedItemID = 0;
}

void URadnodzDemoWidget::NativeConstruct()
{
    Super::NativeConstruct();

    StatusText->SetText(FText::GetEmpty());

    // ボタンが押されたら OnRenameClicked を呼ぶよう登録する
    if (ConnectButton)
    {
        ConnectButton->OnClicked.AddDynamic(this, &URadnodzDemoWidget::OnConnectClicked);
    }
}

void URadnodzDemoWidget::NativeDestruct()
{
    if (Client && Client->IsConnected())
    {
        Client->Disconnect();
    }

    Super::NativeDestruct();
}

void URadnodzDemoWidget::SetStatus(const FString& Message)
{
    if (StatusText)
    {
        StatusText->SetText(FText::FromString(Message));
    }

    UE_LOG(LogTemp, Log, TEXT("[RADNODZ] %s"), *Message);
}

bool URadnodzDemoWidget::EnsureConnected()
{
    // クライアントは初回だけ生成し、以降は使い回す
    if (!Client)
    {
        Client = URadnodzClient::CreateRadnodzClient();
    }

    if (Client->IsConnected())
    {
        return true;
    }

    if (!IpTextBox || !PortTextBox)
    {
        SetStatus(TEXT("IP / ポートの入力欄が見つかりません"));
        return false;
    }

    const FString Ip = IpTextBox->GetText().ToString().TrimStartAndEnd();
    const FString PortString = PortTextBox->GetText().ToString().TrimStartAndEnd();

    if (Ip.IsEmpty())
    {
        SetStatus(TEXT("IP を入力してください"));
        return false;
    }

    if (!PortString.IsNumeric())
    {
        SetStatus(TEXT("ポートは数字で入力してください"));
        return false;
    }

    const int Port = FCString::Atoi(*PortString);

    Client->Connect(Ip, Port);

    // 成否は戻り値ではなく GetError() で判定する
    const FRigdocksError Error = Client->GetError();
    if (Error.ErrorCode != static_cast<int>(AZ_ErrorCode::Success) || !Client->IsConnected())
    {
        SetStatus(FString::Printf(TEXT("接続失敗 (%d): %s"), Error.ErrorCode, *Error.Message));
        return false;
    }

    return true;
}

void URadnodzDemoWidget::DisplayError()
{
    FRigdocksError error = Client->GetError();

    SetStatus(FString::Printf(TEXT("接続失敗 (%d): %s %s"), error.ErrorCode, *error.Message, *error.Details));
}

// トラックリスト取得
void URadnodzDemoWidget::GetTrackList()
{
    // Designer で作った WBP_TrackCard を生成しないと、画面構成が無く何も表示されない
    if (!TrackCardClass)
    {
        SetStatus(TEXT("TrackCardClass が未設定です"));
        return;
    }

    // リスト全削除
    TrackList->ClearChildren();

	TArray<FTrackDetail> TrackDetails = URigdocksSilver_Track::AZ_GetTrackListDetail(Client, 0);

    // 成否判定
    if (Client->GetError().ErrorCode != static_cast<int>(AZ_ErrorCode::Success))
    {
        DisplayError();
        return;
    }

    for (const FTrackDetail& trackDetail : TrackDetails)
    {
        UTrackCardWidget* Card = CreateWidget<UTrackCardWidget>(this, TrackCardClass);

        Card->SetTrackDetail(trackDetail);

        TrackList->AddChild(Card);

    }

    SetStatus(FString::Printf(TEXT("トラックを %d 件取得しました"), TrackDetails.Num()));

}


void URadnodzDemoWidget::OnConnectClicked()
{
    // ---- 1. クライアントを用意する(初回だけ生成し、以降は使い回す)----
    if (!Client)
    {
        Client = URadnodzClient::CreateRadnodzClient();
    }

    // ---- 2. 接続する ----
    const FString Ip = IpTextBox->GetText().ToString();
    const FString Port = PortTextBox->GetText().ToString();

    if (!Client->Connect(Ip, FCString::Atoi(*Port)))
    {
        DisplayError();
        return;
    }
    SetStatus(TEXT("接続に成功しました"));

    //  ---- 3. トラックリスト表示 ----
    GetTrackList();

	// ---- 4. 切断 ----
    Client->Disconnect();

}

TrackCardWidget.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "DataClass/FTrackDetail.h"
#include "TrackCardWidget.generated.h"


class UTextBlock;
class UEditableTextBox;

UCLASS()
class REAPERCONTROLLER_API UTrackCardWidget : public UUserWidget
{
	GENERATED_BODY()

public:

	UFUNCTION(BlueprintCallable)
	void SetTrackDetail(FTrackDetail trackDetail);

	/** トラックのインデックス */
	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
	TObjectPtr<UTextBlock> IndexText;

	/** トラック名 */
	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
	TObjectPtr<UEditableTextBox> TrackNameText;

	/** 音量 */
	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
	TObjectPtr<UTextBlock> VolumeText;

	/** トラック詳細(保存用) */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn = true))
	FTrackDetail TrackDetail;

protected:
	virtual void NativePreConstruct() override;

private:

	// 表示内容更新
	void RefreshTexts();
};

TrackCardWidget.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "TrackCardWidget.h"

#include "Components/TextBlock.h"
#include "Components/EditableTextBox.h"


// ============================================================
//  表示内容
// ============================================================


void UTrackCardWidget::NativePreConstruct()
{
	Super::NativePreConstruct();

	RefreshTexts();
}

void UTrackCardWidget::SetTrackDetail(FTrackDetail trackDetail)
{
	TrackDetail = trackDetail;

	RefreshTexts();
}


void UTrackCardWidget::RefreshTexts()
{
	if (IndexText)
	{
		IndexText->SetText(FText::FromString(FString::FromInt(TrackDetail.Index + 1)));
	}

	if (TrackNameText)
	{
		TrackNameText->SetText(FText::FromString(TrackDetail.Name));
	}


	if (VolumeText)
	{
		FNumberFormattingOptions Opts;
		Opts.SetMinimumFractionalDigits(0)   // 最低0桁
			.SetMaximumFractionalDigits(2);  // 最大0桁

		VolumeText->SetText(FText::AsNumber(TrackDetail.Volume, &Opts));
	}
}

Introduction

Reaperからトラックリストを取得、画面への表示

01_BRONZE_ss_edited.png
01_BRONZE_ss_edited.png
01_SILVER_edited_edited.png
03_GOLD_edited_edited.png

RADNODZを使用してReaperからトラックリストを取得し、取得内容を表すアイテムをUIに表示する

Refresh
bottom of page