C# で ChatGPT API： AI で作曲して MIDI ファイルを作成する

# C# で ChatGPT API： AI で作曲して MIDI ファイルを作成する

こんにちは、@studio_meowtoon です。今回は、WSL Ubuntu 22.04 の C# / .NET 環境にて、ChatGPT API で作曲を行い MIDI ファイルを作成する方法を紹介します。
![chat-gpt_with_csharp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/e76ecf1d-a6e7-9f9e-75bf-8c291cf1eb6a.png)

## 実現すること

ローカル環境の Ubuntu で、ChatGPT API を C# から使い、作曲をさせて MIDI ファイルとして出力します。

:::note warn
この記事では、**従来のプログラム**では**難しい**とされる**クリエイティブな作業**である作曲において、**AI がどのように役立つか**を実際に試してみることを目的の一つとしています。
:::

:::note alert
C# には本来[厳格なコーディング規則](https://learn.microsoft.com/ja-jp/dotnet/csharp/fundamentals/coding-style/coding-conventions)がありますが、この記事では可読性のために、一部規則に沿わない表記方法を使用しています。ご注意ください。
:::

## 開発環境

+ Windows 11 Home 22H2 を使用しています。  

:::note info
[WSL](https://learn.microsoft.com/ja-jp/windows/wsl/about) の Ubuntu を操作していきますので macOS の方も参考にして頂けます。  
:::

<details>

WSL (Microsoft Store アプリ版) [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
```

Ubuntu [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
```

.NET SDK [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/d68031c91462f6808dff)

```sh
$ dotnet --list-sdks
7.0.202 [/usr/share/dotnet/sdk]
$ dotnet --version
7.0.202
```

</details>

## AI にどのようにアプローチするか？

:::note info
ここでは AI に音楽理論の[**チャーチモード**](https://ja.wikipedia.org/wiki/%E6%95%99%E4%BC%9A%E6%97%8B%E6%B3%95)を説明し、そちらに基づいて AI にメロディの作成を依頼します。
:::

<details>
<summary>音楽理論については、こちらを展開してご覧いただけます。</summary>

### チャーチモード(教会旋法)

|旋法|表記|スケール|
|:--|:--|:--|
|リディアン     |Lydian    |1, 2, 3, #4, 5, 6, 7|
|イオニアン     |Ionian    |1, 2, 3, 4, 5, 6, 7|
|ミクソリディアン|Mixolydian|1, 2, 3, 4, 5, 6, b7|
|ドリアン       |Dorian    |1, 2, b3, 4, 5, 6, b7|
|エオリアン     |Aeolian   |1, 2, b3, 4, 5, b6, b7	|
|フリジアン     |Phrygian  |1, b2, b3, 4, 5, b6, b7|
|ロクリアン     |Locrian   |1, b2, b3, 4, b5, b6, b7|

上記の音楽理論を用いて、AI がある程度秩序を保ったメロディを作成するように試みます。

</details>

## AI で作曲する手順

### OpenAI API のキーを取得します

こちらの以前の記事を参考にして頂けます。

https://qiita.com/studio_meowtoon/items/2675b596191e4fa2d0e3

**Ubuntu** に **OPENAI_API_KEY** 環境変数を作成します。

```sh
$ export OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

:::note alert
この環境変数が**一時的なもの**であることに注意してください。
:::

### プロジェクトの作成

プロジェクトルートフォルダに移動します。
※ ~/tmp をプロジェクトルートフォルダとします。

```sh
$ cd ~/tmp
```

コンソールアプリを作成します。
※ GPTMIDIApp がアプリ名です。

```sh
$ dotnet new console -o GPTMIDIApp -f net7.0
```

一度コンソールアプリをビルド・実行します。

```sh
$ cd ~/tmp/GPTMIDIApp
$ dotnet run
Hello, World!
```

:::note info
ここまでの手順で、C# / .NET コンソールアプリの雛形が作成できました😋
:::

### ライブラリの追加

ChatGPT API を使用する為、[**OpenAI**](https://www.nuget.org/packages/OpenAI) のパッケージを [**NuGet**](https://learn.microsoft.com/ja-jp/nuget/what-is-nuget) で取得します。

```sh
$ dotnet add package OpenAI
```

また、ChatGPT API に渡すパラメータを JSON ファイルから読み込む為、[**Microsoft.Extensions.Configuration**](https://www.nuget.org/packages/Microsoft.Extensions.Configuration/) のパッケージを [**NuGet**](https://learn.microsoft.com/ja-jp/nuget/what-is-nuget) で取得します。

```sh
$ dotnet add package Microsoft.Extensions.Configuration
$ dotnet add package Microsoft.Extensions.Configuration.Json
$ dotnet add package Microsoft.Extensions.Configuration.Binder
```

パッケージを確認します。

```sh
$ dotnet list package
プロジェクト 'GPTMIDIApp' に次のパッケージ参照が含まれています
   [net7.0]:
   最上位レベル パッケージ                                     要求済み    解決済み
   > Microsoft.Extensions.Configuration             7.0.0   7.0.0
   > Microsoft.Extensions.Configuration.Binder      7.0.4   7.0.4
   > Microsoft.Extensions.Configuration.Json        7.0.0   7.0.0
   > OpenAI
```

:::note info
ここまでの手順で、プロジェクトに **OpenAI** と **Microsoft.Extensions.Configuration** のパッケージを組み込むことができました😋
:::

### ChatGPT API に渡すプロンプトについて

:::note warn
ChatGPT API に関連して耳にする**プロンプト**とは何でしょうか？🤔
:::

https://platform.openai.com/docs/guides/gpt

**OpenAI の Web サイトにこのように記されています。**

> The inputs to GPTs are also referred to as "prompts". Designing a prompt is essentially how you “program” a GPT model, usually by providing instructions or some examples of how to successfully complete a task.

:::note into
GPT への入力は**プロンプト**とも呼ばれます。プロンプトの設計は基本的に、GPT モデルを**プログラムする方法**であり、通常はタスクを正常に完了するための**手順や例を提供**します。
:::

**プロンプトメッセージの構成**

```mermaid
graph LR;
messages---role;
role---system;
role---user;
role---assistant;
```

|役割|内容|
|:--|:--|
|**システム**|アシスタントの動作を設定するのに役立ちます。たとえば、アシスタントの性格を設定したり、会話全体でアシスタントがどのように動作するかについて具体的な指示を設定します。|
|**ユーザー**|アシスタントが応答するためのリクエストまたはコメントを提供します。|
|**アシスタント**|通常、以前のアシスタントの応答が保存されています。望ましい動作の例を示すためにユーザーが作成することもできます。|

:::note info
このように、GhatGPT に対して**システム**プロンプトで**手順**を、また、**アシスタント**プロンプトで**例**を提供することが可能となっています😋
:::

### ChatGPT API に渡すプロンプトの作成

:::note warn
実際にプログラムを開発する前に、**Playground** で**システム**、**アシスタント**プロンプトの内容を検討した方がスムーズに開発が進みます😐
:::

**ChatGPT API Playground**

https://platform.openai.com/playground

今回の記事では以下のようなプロンプトを作成しました。

:::note warn
この記事では音の音程を後述する**MIDI ノート番号**で表しています。
:::

<details>
<summary>こちらを展開してご覧いただけます。</summary>

```txt:システム
数値群から数値を選択してメロディとして連続32個出力せよ。

数値群:
A: {60,62,64,66,67,69,71,72}
B: {60,62,64,65,67,69,71,72}
C: {60,62,64,65,67,69,70,72}
D: {60,62,63,65,67,69,70,72}
E: {60,62,63,65,67,68,70,72}
F: {60,61,63,65,67,68,70,72}
G: {60,61,63,65,66,68,70,72}

特別な数値:
A: {60,66}
B: {65,71}
C: {64,70}
D: {63,69}
E: {62,68}
F: {61,67}
G: {60,66}

以下の条件で数値を連続32個並べる。

・数値群が選ばれる確率は対等である
・数値は必ず一つの数値群から選ぶ
・数値は重複することが出来る
・数値は連続することが出来る
・数値群の特別な音は出現する頻度が極端に低い
・数値の選択頻度はかなりバラツキがある
・数値は音楽のメロディのように並ぶ

どの数値群から選んだか出力する。
数値はそれぞれどれくらいの頻度で使用したか出力する。

以上の条件で連続32個並べた数値を以下のフォーマットで出力する。

数値群: X

頻度:
60:5
62:3
64:4
...

結果:
{ n,n,n,n... }
```

```txt:アシスタント
これが提示された条件に基づいて作成した、具体的な実行例です。

数値群: E

頻度:
60: 2
62: 5
63: 3
65: 6
67: 5
68: 3
70: 5
72: 3

結果:
{62, 65, 70, 65, 62, 63, 65, 68, 70, 65, 67, 68, 67, 63, 68, 67, 65, 72, 67, 60, 65, 70, 67, 62, 70, 67, 68, 65, 70, 68, 72, 72}
```

</details>

```txt:ChatGPT API のレスポンスの一例
注：この問題は、ランダム性が含まれているため、異なる回答が得られる場合があります。以下の回答は、一つの例です。

数値群: C

頻度:
60: 6
62: 2
64: 3
65: 4
67: 5
69: 3
70: 4
72: 5

結果:
{65, 67, 64, 60, 67, 70, 69, 70, 60, 69, 67, 72, 67, 60, 72, 67, 69, 60, 65, 70, 65, 72, 70, 64, 72, 67, 60, 65, 70, 67, 69, 67}
```

:::note info
ここまでの手順で、ChatGPT API Playground から音楽の旋律を得ることができました😋
:::
:::note warn
**プロンプトの組み方**によりさらに音楽的な旋律を自動生成させることが可能だと思われます。今回の例ではこのような単純な例で紹介しました。
:::

### コードの修正

プロジェクトフォルダに移動します。

```sh
$ cd ~/tmp/GPTMIDIApp
```

設定ファイルを読み込む AppSettings.cs を作成します。

```sh
$ vim AppSettings.cs
```

ファイルの内容

```csharp:AppSettings.cs
namespace GPTMIDIApp {
    public class AppSettings {
        public string? System { get; set; }
        public string? StartupAssistant { get; set; }
    }
}
```

appsettings.json を作成します。

```sh
$ vim appsettings.json
```

ファイルの内容

```json:appsettings.json
{
  "System": "数値群から数値を選択してメロディとして連続32個出力せよ。\n\n数値群:\nA: {60,62,64,66,67,69,71,72}\nB: {60,62,64,65,67,69,71,72}\nC: {60,62,64,65,67,69,70,72}\nD: {60,62,63,65,67,69,70,72}\nE: {60,62,63,65,67,68,70,72}\nF: {60,61,63,65,67,68,70,72}\nG: {60,61,63,65,66,68,70,72}\n\n特別な数値:\nA: {60,66}\nB: {65,71}\nC: {64,70}\nD: {63,69}\nE: {62,68}\nF: {61,67}\nG: {60,66}\n\n以下の条件で数値を連続32個並べる。\n\n・数値群が選ばれる確率は対等である\n・数値は必ず一つの数値群から選ぶ\n・数値は重複することが出来る\n・数値は連続することが出来る\n・数値群の特別な音は出現する頻度が極端に低い\n・数値の選択頻度はかなりバラツキがある\n・数値は音楽のメロディのように並ぶ\n\nどの数値群から選んだか出力する。\n数値はそれぞれどれくらいの頻度で使用したか出力する。\n\n以上の条件で連続32個並べた数値を以下のフォーマットで出力する。\n\n数値群: X\n\n頻度:\n60:5\n62:3\n64:4\n...\n\n結果:\n{ n,n,n,n... }",
  "StartupAssistant": "これが提示された条件に基づいて作成した、具体的な実行例です。\n\n数値群: E\n\n頻度:\n60: 2\n62: 5\n63: 3\n65: 6\n67: 5\n68: 3\n70: 5\n72: 3\n\n結果:\n{62, 65, 70, 65, 62, 63, 65, 68, 70, 65, 67, 68, 67, 63, 68, 67, 65, 72, 67, 60, 65, 70, 67, 62, 70, 67, 68, 65, 70, 68, 72, 72}"
}
```

appsettings.json をビルドの成果物に含める為、GPTMIDIApp.csproj を修正します。

```sh
$ vim GPTMIDIApp.csproj
```

ファイルの内容 (※以下の内容を追記します)

```xml:GPTMIDIApp.csproj の一部
<Project Sdk="Microsoft.NET.Sdk">
  <!-- 省略 -->
  <ItemGroup>
    <None Update="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>

</Project>
```

:::note info
ここまでの手順で、ChatGPT API に入力するプロンプトを含んだ appsettings.json の内容を AppSettings オブジェクトに読み込む設定ができました😋
:::

Program.cs を修正します。

```sh
$ vim Program.cs
```

ファイルの内容

```csharp:Program.cs
using static System.Console;
using static System.ConsoleColor;
using static System.Environment;

using Microsoft.Extensions.Configuration;
using OpenAI_API;
using OpenAI_API.Chat;
using OpenAI_API.Models;

namespace GPTMIDIApp {
    class Program {
        static async Task Main(string[] args) {
            // ChatGPT API から問い合わせの結果を取得
            string reply = await getAssistant();
        }
        // ChatGPT API に問い合わせます
        static async Task<String> getAssistant() {
            // AppSettings オブジェクトを取得
            IConfiguration configuration = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .Build();
            AppSettings? app_settings = configuration.Get<AppSettings>();
            // プロンプトを取得
            string? system = app_settings?.System;
            string? startup_assistant = app_settings?.StartupAssistant;
#if DEBUG
            ForegroundColor = Red;
            WriteLine($"SYSTEM:\n{system}\n");
            WriteLine($"STARTUP ASSISTANT:\n{startup_assistant}\n");
            ResetColor();
#endif
            // OpenAI の API キーを取得
            string? api_key = GetEnvironmentVariable("OPENAI_API_KEY");
            // OpenAIAPI オブジェクトを生成
            OpenAIAPI api = new(api_key);
            // ChatMessage オブジェクトのリストを作成
            List<ChatMessage> chat_message_list = new();
            // system プロンプトを設定
            chat_message_list.Add(new ChatMessage(ChatMessageRole.System, system));
            // assistant プロンプトを設定
            chat_message_list.Add(new ChatMessage(ChatMessageRole.Assistant, startup_assistant));
            // user プロンプトを設定
            chat_message_list.Add(new ChatMessage(ChatMessageRole.User, "次の実行例を表示する。"));
            // ChatGPT API にリクエストして結果を取得
            ChatResult result = await api.Chat.CreateChatCompletionAsync(new ChatRequest() {
                Model = Model.ChatGPTTurbo0301,
                Messages = chat_message_list
            });
#if DEBUG
            ForegroundColor = Green;
            string? reply = result.Choices[0].Message.Content.Trim();
            WriteLine($"ASSISTANT:\n{reply}\n");
            ResetColor();
#endif
            WriteLine($"get note completed.");
            return reply;
        }
    }
}
```

コンソールアプリを実行します。

```txt
$ dotnet run
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/c04fb6e5-0147-6aeb-a3b4-0d5becb02702.png)

:::note info
ここまでの手順で、ChatGPT API を使い、**システム**、**アシスタント**プロンプトに基づいた音楽の旋律を得ることができました😋
:::

## MIDI ファイルを出力する手順

### コードの修正

ChatGPT API から取得した文字列から **MIDI ノート番号**などを抽出する処理を作成します。

Program.cs を修正します。

```sh
$ vim Program.cs
```

ファイルの内容(※一部)

```csharp:Program.cs の一部
// MIDI ノート番号とモード名を取得します
static (string mode, int[] notes) getMIDINotes(string content) {
    Dictionary<string, string> type_to_mode = new() {
        { "A", "Lydian" }, { "B", "Ionian" }, { "C", "Mixolydian" },
        { "D", "Dorian" }, { "E", "Aeolian" }, { "F", "Phrygian" }, { "G", "Locrian" }
    };
    // MIDI ノート番号の配列切り出し
    string[] split_array1 = content.Split(new string[] { "結果:" }, StringSplitOptions.None);
    string notes_string = split_array1[1].Trim();
    int[] notes = notes_string.TrimStart('{').TrimEnd('}').Split(',').Select(int.Parse).ToArray();
    // 数値群のタイプ切り出し
    string[] split_array2 = content.Split(new string[] { "数値群:" }, StringSplitOptions.None);
    string type = split_array2[1].Trim().Substring(0, 1);
    // タプルで返す
    return (type_to_mode[type], notes);
}
```

:::note info
ここまでの手順で、ChatGPT API から取得した結果を、**後段のプログラムから利用できる状態**に**加工**することができました😋
:::

また、取り出した **MIDI ノート番号**の配列を MIDI ファイルとして出力する具体的な内容については、こちらの以前の記事を参照頂けます🙇‍♂️

https://qiita.com/studio_meowtoon/items/485c3888473319d770c2

<details>
<summary>コードの全体を表示する</summary>

```csharp:Program.cs
using static System.Console;
using static System.ConsoleColor;
using static System.Environment;

using Microsoft.Extensions.Configuration;
using OpenAI_API;
using OpenAI_API.Chat;
using OpenAI_API.Models;
using Sanford.Multimedia.Midi;
using static Sanford.Multimedia.Midi.ChannelCommand;
using static Sanford.Multimedia.Midi.MetaType;

using static GPTMIDIApp.Converter;

namespace GPTMIDIApp {
    class Program {
        static async Task Main(string[] args) {
            // ChatGPT API から問い合わせの結果を取得
            string reply = await getAssistant();
            // MIDI ノート番号とモード名を取得する
            (string mode, int[] notes) = getMIDINotes(reply);
            // MIDI ファイルを保存
            writeMIDIFile(mode, notes);
        }
        // ChatGPT API に問い合わせます
        static async Task<String> getAssistant() {
            // AppSettings オブジェクトを取得
            IConfiguration configuration = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .Build();
            AppSettings? app_settings = configuration.Get<AppSettings>();
            // プロンプトを取得
            string? system = app_settings?.System;
            string? startup_assistant = app_settings?.StartupAssistant;
#if DEBUG
            ForegroundColor = Red;
            WriteLine($"SYSTEM:\n{system}\n");
            WriteLine($"STARTUP ASSISTANT:\n{startup_assistant}\n");
            ResetColor();
#endif
            // OpenAI の API キーを取得
            string? api_key = GetEnvironmentVariable("OPENAI_API_KEY");
            // OpenAIAPI オブジェクトを生成
            OpenAIAPI api = new(api_key);
            // ChatMessage オブジェクトのリストを作成
            List<ChatMessage> chat_message_list = new();
            // system プロンプトを設定
            chat_message_list.Add(new ChatMessage(ChatMessageRole.System, system));
            // assistant プロンプトを設定
            chat_message_list.Add(new ChatMessage(ChatMessageRole.Assistant, startup_assistant));
            // user プロンプトを設定
            chat_message_list.Add(new ChatMessage(ChatMessageRole.User, "次の実行例を表示する。"));
            // ChatGPT API にリクエストして結果を取得
            ChatResult result = await api.Chat.CreateChatCompletionAsync(new ChatRequest() {
                Model = Model.ChatGPTTurbo0301,
                Messages = chat_message_list
            });
#if DEBUG
            ForegroundColor = Green;
            string? reply = result.Choices[0].Message.Content.Trim();
            WriteLine($"ASSISTANT:\n{reply}\n");
            ResetColor();
#endif
            WriteLine($"get note completed.");
            return reply;
        }
        // MIDI ノート番号とモード名を取得します
        static (string mode, int[] notes) getMIDINotes(string content) {
            Dictionary<string, string> type_to_mode = new() {
                { "A", "Lydian" }, { "B", "Ionian" }, { "C", "Mixolydian" },
                { "D", "Dorian" }, { "E", "Aeolian" }, { "F", "Phrygian" }, { "G", "Locrian" }
            };
            // MIDI ノート番号の配列切り出し
            string[] split_array1 = content.Split(new string[] { "結果:" }, StringSplitOptions.None);
            string notes_string = split_array1[1].Trim();
            int[] notes = notes_string.TrimStart('{').TrimEnd('}').Split(',').Select(int.Parse).ToArray();
            // 数値群のタイプ切り出し
            string[] split_array2 = content.Split(new string[] { "数値群:" }, StringSplitOptions.None);
            string type = split_array2[1].Trim().Substring(0, 1);
            // タプルで返す
            return (type_to_mode[type], notes);
        }
        // MIDI ファイルを出力します
        static void writeMIDIFile(string mode, int[] notes) {
            // シーケンスの作成
            Sequence sequence = new();
            sequence.Format = 1;

            int channel = 0; // MIDI チャンネル番号
            int velocity = 112; // ノートオン時のベロシティ
            int bpm = 120; // テンポ
            int eighth_note_length = 6000 / (bpm * 4); // 8分音符の長さ（ミリ秒）
            string song_name = $"{mode} Song {DateTime.Now.ToString("yyyy-MM-dd_HHmmss")}"; // 曲名
            string copyright = "STUDIO Awesome"; // 著作権

            // ノートの長さを 8分音符に設定
            int duration = eighth_note_length;

            // コンダクタートラックの設定
            Track conductor_track = new();
            conductor_track.Insert(position: 0, new MetaMessage(Tempo, ToByteTempo(bpm)));
            conductor_track.Insert(position: 0, new MetaMessage(TrackName, ToByteArray(song_name)));
            conductor_track.Insert(position: 0, new MetaMessage(Copyright, ToByteArray(copyright)));
            sequence.Add(item: conductor_track);

            // シーケンストラックの設定
            Track sequence_track = new();
            sequence_track.Insert(position: 0, new MetaMessage(TrackName, ToByteArray("Melody")));
            sequence.Add(item: sequence_track);

            // 音階の設定
            int tick = 0; // 時間のトラッキング用変数
            foreach (int note_number in notes) {
                // ノートオンイベントの作成
                sequence_track.Insert(position: tick, new ChannelMessage(NoteOn, channel, note_number, velocity));
                // 次のノートまでの時間を加算
                tick += duration;
                // ノートオフイベントの作成
                sequence_track.Insert(position: tick, new ChannelMessage(NoteOff, channel, note_number, 0));
            }

            // Bass 音の設定
            tick = 0;
            foreach (int note_number in new int[] { 36, 36, 36, 36 }) { // キーを C に指定
                sequence_track.Insert(position: tick, new ChannelMessage(NoteOn, channel, note_number, velocity));
                tick += (duration * 8); // 全音符
                sequence_track.Insert(position: (tick - 1), new ChannelMessage(NoteOff, channel, note_number, 0));
            }

            // MIDI ファイルの保存
            sequence.Save($"{song_name.Replace(" ", "_")}.mid");
            WriteLine("write midi file completed.");
        }
    }
}
```

</details>

## CahtGPT に作曲させてみる

コンソールアプリを実行します。

```sh
$ export OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$ cd ~/tmp/GPTMIDIApp
$ dotnet run
```

プロジェクトフォルダに MIDI ファイルが出力されました。

```
Dorian_Song_2023-06-17_231957.mid
```

:::note info
🎵実際に ChatGPT が作曲した音楽を聴いてみる😋

|モード|mp3 ファイル|
|:--|:--|
|**イオニアン**|🔊[Ionian_Song_2023-06-17_223247.mp3](https://drive.google.com/file/d/1XD4lNQ7VKrTNUwGJyxgWon62fCZsv4mA/view?usp=drive_link)|
|**ドリアン**  |🔊[Dorian_Song_2023-06-17_232103.mp3](https://drive.google.com/file/d/1u3ZUNw5J__hyWX8yLh8CqYSCd8OH16vE/view?usp=drive_link)|
:::

※ ChatGPT が作曲したメロディにプログラムからベース音を足しています。非常に粗削りではありますが、**AI 作曲への可能性**を感じて頂けたらと思います。

## まとめ
ローカル環境の Ubuntu C# / .NET 環境にて、ChatGPT に作曲をさせて MIDI ファイルとして出力することができました。

どうでしたか？ Window 11 の WSL Ubuntu に、C# / .NET の開発環境を手軽に構築することができます、ぜひお試しください。今後も .NET / OpenAI の技術トピックなどを紹介していきますので、ぜひお楽しみにしてください。

## 推奨コンテンツ

https://qiita.com/studio_meowtoon/items/7b2e75fa916cd6d4d804