編集履歴一覧に戻る
shigobuのアイコン画像

shigobu が 2022年02月02日10時01分49秒 に編集

コメント無し

本文の変更

# YMZ294 秋月電子で音源ICのページを見ると、はじめから曲が内蔵されているメロディーICが連なる中、異彩を放つ(ように見えた)ヤマハ音源ICの文字。データシートを見ると、Arduinoで簡単に制御できそうだったので即購入。これを使って、ベートーヴェンの月光を演奏させることにしました。 # 用意したもの |部品|数量|購入先| |---|:---:|---|

-

|YMZ294|3個|[秋月電子](https://akizukidenshi.com/catalog/g/gI-12141/)| |クリスタルオシレータ 8MHz (2分周して4MHzで使用)|1個|[秋月電子](https://akizukidenshi.com/catalog/g/gP-01566/)| |2回路Dフリップフロップ TC74HC74AP(クロックの分周用)|1個|[秋月電子](https://akizukidenshi.com/catalog/g/gI-10879/)| |3.5mmステレオミニジャックDIP化キット|1個|[秋月電子](https://akizukidenshi.com/catalog/g/gK-05363/)

+

|YMZ294|3個| 秋月電子 I-12141| |クリスタルオシレータ 8MHz (2分周して4MHzで使用)|1個| 秋月電子 P-01566 | |2回路Dフリップフロップ TC74HC74AP(クロックの分周用)|1個| 秋月電子 I-10879| |3.5mmステレオミニジャックDIP化キット|1個| 秋月電子 K-05363|

|Arduino UNO|1個||

-

|抵抗10Ω|1個|[秋月電子](https://akizukidenshi.com/catalog/g/gR-25100/)| |抵抗1kΩ|1個|[秋月電子](https://akizukidenshi.com/catalog/g/gR-25102/)|

+

|抵抗10Ω|1個| 秋月電子 R-25100 | |抵抗1kΩ|1個| 秋月電子 R-25102 |

|ジャンプワイヤ(付け根が細いもの)|適量|[千石電商](https://www.sengoku.co.jp/mod/sgk_cart/detail.php?code=33MT-35HC)| # クロック周波数 YMZ294を駆動させるマスタークロックは、4MHzか6MHzを選択できます。 データシートを見ると、はじめの方にある特徴の項目には、「4MHzまたは、8MHzから選択」と書いてありますが、これは間違いです。 実際に8MHzをつないで音を出すと、少し高い音が出ました。 データシートには、8MHzと6MHzの記述が混在していますが、6MHzが正解です。 今回は、8MHzのオシレーターを購入してしまったので、Dフリップフロップで2分周して4MHzで駆動させました。 # YMZ294のライブラリ [ライブラリを作成して公開している方](https://wp.hrmux.com/?p=91#comments)がいますが、作りたかったので自作しました。 ```C++:YMZ294.h #ifndef YMZ294_H #define YMZ294_H enum AddressData{ Address = 0, Data = 1 }; enum Addresses{ ChA1, ChA2, ChB1, ChB2, ChC1, ChC2, Noise, Mixer, VolumeA, VolumeB, VolumeC, EnvelopeFreqLSB, EnvelopeFreqMSB, EnvelopeShape }; enum Channel{ ChA, ChB, ChC, Noise1, Noise2, Noise3, }; enum MixerOnOff{ On, Off, }; enum EnvelopeShapes{ sh1 = 0b1000, sh2, sh3, sh4, sh5, sh6, sh7, sh8, }; class YMZ294{ private: YMZ294(); public: YMZ294(int wr, int cs, int ad, int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7); YMZ294(int wr, int ad, int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7); void begin(); void noteOn(Channel ch, uint8_t noteNum); void noteOff(Channel ch); void selectAddressData(AddressData ad); void setDataBus(uint8_t data); void setData(uint8_t data); void setAddress(Addresses data); void setFrequency(Channel ch, uint16_t TP); void setVolume(Channel ch, uint8_t volume); void setMixer(Channel ch, MixerOnOff mixer); void setEnvelopeFrequency(uint16_t freq) ; void setEnvelopeShape(EnvelopeShapes shape); private: int WR; int CS; int AD; int D0; int D1; int D2; int D3; int D4; int D5; int D6; int D7; bool isCsAvailable; uint8_t currentMixerVal = 0xFF; const int16_t TP[128]; }; #endif ``` ```Arduino:YMZ294.cpp #include <arduino.h> #include "YMZ294.h" YMZ294::YMZ294() : TP{ 15289,14431,13621,12856,12135,11454,10811,10204,9631,9091,8581,8099,7645,7215,6810,6428,6067,5727, 5405,5102,4816,4545,4290,4050,3822,3608,3405,3214,3034,2863,2703,2551,2408,2273,2145,2025,1911,1804, 1703,1607,1517,1432,1351,1276,1204,1136,1073,1012,956,902,851,804,758,716,676,638,602,568,536,506,478, 451,426,402,379,358,338,319,301,284,268,253,239,225,213,201,190,179,169,159,150,142,134,127,119,113,106, 100,95,89,84,80,75,71,67,63,60,56,53,50,47,45,42,40,38,36,34,32,30,28,27,25,24,22,21,20,19,18,17,16,15,14,13,13,12,11,11,10 }{} YMZ294::YMZ294(int wr, int cs, int ad, int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7) : YMZ294() { WR = wr; CS = cs; AD = ad; D0 = d0; D1 = d1; D2 = d2; D3 = d3; D4 = d4; D5 = d5; D6 = d6; D7 = d7; isCsAvailable = true; } YMZ294::YMZ294(int wr, int ad, int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7) : YMZ294() { WR = wr; AD = ad; D0 = d0; D1 = d1; D2 = d2; D3 = d3; D4 = d4; D5 = d5; D6 = d6; D7 = d7; isCsAvailable = false; } void YMZ294::begin(){ pinMode(D0, OUTPUT); pinMode(D1, OUTPUT); pinMode(D2, OUTPUT); pinMode(D3, OUTPUT); pinMode(D4, OUTPUT); pinMode(D5, OUTPUT); pinMode(D6, OUTPUT); pinMode(D7, OUTPUT); pinMode(AD, OUTPUT); pinMode(WR, OUTPUT); if (isCsAvailable) { pinMode(CS, OUTPUT); digitalWrite(CS, HIGH); } setVolume(Channel::ChA, 0x0f); setVolume(Channel::ChB, 0x0f); setVolume(Channel::ChC, 0x0f); setMixer(Channel::Noise1, MixerOnOff::Off); setMixer(Channel::Noise2, MixerOnOff::Off); setMixer(Channel::Noise3, MixerOnOff::Off); } void YMZ294::noteOn(Channel ch, uint8_t noteNum){ setFrequency(ch, TP[noteNum]); setMixer(ch, MixerOnOff::On); } void YMZ294::noteOff(Channel ch){ setMixer(ch, MixerOnOff::Off); } void YMZ294::selectAddressData(AddressData ad){ digitalWrite(AD, ad); } void YMZ294::setDataBus(uint8_t data){ if (isCsAvailable) { digitalWrite(CS, LOW); } digitalWrite(WR, LOW); digitalWrite(D7, data & 0b10000000); digitalWrite(D6, data & 0b01000000); digitalWrite(D5, data & 0b00100000); digitalWrite(D4, data & 0b00010000); digitalWrite(D3, data & 0b00001000); digitalWrite(D2, data & 0b00000100); digitalWrite(D1, data & 0b00000010); digitalWrite(D0, data & 0b00000001); digitalWrite(WR, HIGH); if (isCsAvailable) { digitalWrite(CS, HIGH); } } void YMZ294::setData(uint8_t data){ selectAddressData(AddressData::Data); setDataBus(data); } void YMZ294::setAddress(Addresses data){ selectAddressData(AddressData::Address); setDataBus(data); } void YMZ294::setFrequency(Channel ch, uint16_t TP){ Addresses MSBaddr; Addresses LSBaddr; switch (ch) { case Channel::ChA: LSBaddr = Addresses::ChA1; MSBaddr = Addresses::ChA2; break; case Channel::ChB: LSBaddr = Addresses::ChB1; MSBaddr = Addresses::ChB2; break; case Channel::ChC: LSBaddr = Addresses::ChC1; MSBaddr = Addresses::ChC2; break; default: return; } setAddress(LSBaddr); setData(TP & 0x00ff); setAddress(MSBaddr); setData((TP & 0xff00) >> 8); } void YMZ294::setVolume(Channel ch, uint8_t volume){ Addresses addr; switch (ch) { case Channel::ChA: addr = Addresses::VolumeA; break; case Channel::ChB: addr = Addresses::VolumeB; break; case Channel::ChC: addr = Addresses::VolumeC; break; default: return; } setAddress(addr); setData(volume & 0x0f); } void YMZ294::setMixer(Channel ch, MixerOnOff mixer){ uint8_t mixerValue = 0; setAddress(Addresses::Mixer); switch (ch) { case Channel::ChA: mixerValue = 0b1; break; case Channel::ChB: mixerValue = 0b10; break; case Channel::ChC: mixerValue = 0b100; break; case Channel::Noise1: mixerValue = 0b1000; break; case Channel::Noise2: mixerValue = 0b10000; case Channel::Noise3: mixerValue = 0b100000; break; default: return; } if (mixer == MixerOnOff::On){ currentMixerVal &= ~mixerValue; } else{ currentMixerVal |= mixerValue; } setData(currentMixerVal); } void YMZ294::setEnvelopeFrequency(uint16_t freq) { setAddress(Addresses::EnvelopeFreqLSB); setData(freq & 0x00ff); setAddress(Addresses::EnvelopeFreqMSB); setData(freq >> 8); } void YMZ294::setEnvelopeShape(EnvelopeShapes shape){ setAddress(Addresses::EnvelopeShape); setData(shape); setAddress(Addresses::VolumeA); setData(0xff); } ``` GitHubにソースの全体を上げました。 https://github.com/shigobu/Arduino_YMZ294Library # 回路図 ![回路図](https://camo.elchika.com/a57db30557dc4c70e9e4eb4fb5d4eacc3205120c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33646436646464392d626266392d343865302d396437302d6134313133343436333130322f34646639313763622d626136312d343063312d626338352d636161333239666661663064/) # 配線 配線はブレッドボードで行いました。 はじめは、秋月電子で購入した[激安のジャンパーワイヤ](https://akizukidenshi.com/catalog/g/gC-05159/)を使用しました。しかしこのジャンパーワイヤは、ピンの付け根が太く一列に連続でブレッドボードに刺すと、隣と干渉して抜き差しがしづらくなりました。 なので、ちょっと高いですが千石電商で[サンハヤト製](https://www.sengoku.co.jp/mod/sgk_cart/detail.php?code=33MT-35HC)のものを購入しました。ピンの付け根が細く使いやすいです。 ![全体像](https://camo.elchika.com/10cd8eb124585a5422bd7998bfb945df5f5e2a58/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33646436646464392d626266392d343865302d396437302d6134313133343436333130322f36653631303333332d616233302d343838342d613731322d626364333238633331616462/) # MIDIデータの受信と再生 MIDIデータの受信には、「Arduino MIDI Library」を使用しました。 [Arduino MIDI Library(GitHub)](https://github.com/FortySevenEffects/arduino_midi_library) ```arduino:MIDI再生コード #include <MIDI.h> #include <YMZ294.h> #define D0 12 #define D1 11 #define D2 10 #define D3 9 #define D4 8 #define D5 7 #define D6 6 #define D7 5 #define AD 13 #define WR 4 #define CS1 3 #define CS2 2 #define CS3 A5 YMZ294 ymz1(WR, CS1, AD, D0, D1, D2, D3, D4, D5, D6, D7); YMZ294 ymz2(WR, CS2, AD, D0, D1, D2, D3, D4, D5, D6, D7); YMZ294 ymz3(WR, CS3, AD, D0, D1, D2, D3, D4, D5, D6, D7); MIDI_CREATE_DEFAULT_INSTANCE(); void setup() { ymz1.begin(); ymz2.begin(); ymz3.begin(); delay(10); MIDI.begin(MIDI_CHANNEL_OMNI); } void loop() { if(MIDI.read()) { midi::Channel ch = MIDI.getChannel(); switch(MIDI.getType()) // Get the type of the message we caught { case midi::NoteOn: switch (ch) { case 1: case 2: case 3: if (MIDI.getData2() == 0) { ymz1.noteOff(static_cast<Channel>(ch - 1)); } else { ymz1.noteOn(static_cast<Channel>(ch - 1), MIDI.getData1()); } break; case 4: case 5: case 6: if (MIDI.getData2() == 0) { ymz2.noteOff(static_cast<Channel>(ch - 4)); } else { ymz2.noteOn(static_cast<Channel>(ch - 4), MIDI.getData1()); } break; case 7: case 8: case 9: if (MIDI.getData2() == 0) { ymz3.noteOff(static_cast<Channel>(ch - 7)); } else { ymz3.noteOn(static_cast<Channel>(ch - 7), MIDI.getData1()); } break; default: break; } break; case midi::NoteOff: switch (ch) { case 1: case 2: case 3: ymz1.noteOff(static_cast<Channel>(ch - 1)); case 4: case 5: case 6: ymz2.noteOff(static_cast<Channel>(ch - 4)); break; case 7: case 8: case 9: ymz3.noteOff(static_cast<Channel>(ch - 7)); break; default: break; } case midi::ControlChange: midi::DataByte data1 = MIDI.getData1(); switch (data1) { case midi::ExpressionController: //エクスプレッション int vol = map(MIDI.getData2(), 0, 0x7f, 0, 0x0f); switch (ch) { case 1: case 2: case 3: ymz1.setVolume(static_cast<Channel>(ch - 1), vol); case 4: case 5: case 6: ymz2.setVolume(static_cast<Channel>(ch - 4), vol); break; case 7: case 8: case 9: ymz3.setVolume(static_cast<Channel>(ch - 7), vol); break; default: break; } break; default: break; } default: break; } } } ``` # PCとの接続 実は、これはMIDI規格に準じた音源では有りません。AruduinoをPCにつなぐとMIDIデバイスとしてではなく、普通にCOMポートとして認識されます。COMポートを使いArduinoとはUARTで通信します。 タイトルに「MIDI(っぽい)」と書いたのは、これが理由です。 :::plantuml:MIDI信号の流れ @startuml :MIDI再生アプリ Cubase; :仮想MIDIポート loopMIDI; :自作アプリ MIDI→COMポート; :Arduino UNO; @enduml ::: # 月光の再生 早速楽譜を購入し、打ち込みを行いました。 楽譜は、「ぷりんと楽譜」のサービスを使用し、セブンイレブンのコピー機でプリントしました。 [ピアノソナタ第14番-月光<第1楽章>(楽譜)- ヤマハ「ぷりんと楽譜」](https://www.print-gakufu.com/score/detail/33468/) 打ち込みはCubaseを使いました。 すべて同じトラックに打ち込み、ノートエクスプレッションの機能を使用しノートをMIDIチャンネルに分配しました。 エンベロープを使いたいのですが、YMZ294のエンベロープは1チャンネルしかなく、すべてのチャンネルで同じエンベロープが使用されてしまいます。 なので、エンベロープはMIDIデータにエクスプレッションとして入力するようにしました。 ノート毎にエンベロープを手作業で書くのは大変なので、自動でエンベロープを挿入するアプリを作成しました。 こうしてMIDIデータが完成しました。 MIDIデータをCubaseに読み込ませ、再生しながら録音を行います。 録音したデータをYouTubeにアップしました。 @[youtube](https://www.youtube.com/watch?v=2-4NtLBAO4Q) # まとめ 今回は、ブレッドボード上に回路を構成しましたが、次は基盤に実装してみたいと思います。