├── .gitignore
├── ArduinoProject
├── ArduinoProject.ino
└── test_other_timers_sn76489
├── CIrcuit Screen Grab.png
├── Example VGM Tunes
├── 13 Amps.vgm
├── 1942.vgm
├── Addicts Anthem 3.vgm
├── Addicts Anthem Miami Remix 89.vgm
├── Addicts Anthem Remix.vgm
├── Addicts Anthem.vgm
├── Blue Monday Remix.vgm
├── Chariots.vgm
├── Cheers.vgm
├── Circle in the Sand.vgm
├── Cold Tea.vgm
├── Dambuster March.vgm
├── Disk User.vgm
├── Domino Dancing.vgm
├── Dreamscape.vgm
├── Enjoy the silence.vgm
├── Equinoxe5.vgm
├── Flash Dance.vgm
├── Ghostbusters.vgm
├── Golden Brown.vgm
├── Good King.vgm
├── Newsong.vgm
├── Only You.vgm
├── Pride.vgm
├── Raiders of the lost ark.vgm
├── Rent.vgm
├── Sweet Dreams.vgm
├── Thriller.vgm
├── True Colors.vgm
├── Valkeris.vgm
├── Win Again.vgm
├── airwolf.vgm
├── ateam.vgm
├── dallas.vgm
├── doctor doctor.vgm
├── elsewhere.vgm
├── in the mood.vgm
├── its a sin.vgm
├── stop.vgm
└── tubular bells.vgm
├── LICENSE
├── Main Circuit.bmp
├── Main Circuit.dwg
├── Main Circuit.dxf
├── Pictures
├── DSC_0740.JPG
├── DSC_0741.JPG
├── DSC_0742.JPG
├── DSC_1048.JPG
└── DSC_1049.JPG
├── README.md
└── VGMPlayer
├── HighResTimer.cs
├── Program.cs
├── SerialSender.cs
├── VGMPlayer.csproj
├── VGMPlayer.sln
├── VgmFile.cs
└── VgmFileHeader.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # .NET Core
46 | project.lock.json
47 | project.fragment.lock.json
48 | artifacts/
49 | **/Properties/launchSettings.json
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # Visual Studio code coverage results
117 | *.coverage
118 | *.coveragexml
119 |
120 | # NCrunch
121 | _NCrunch_*
122 | .*crunch*.local.xml
123 | nCrunchTemp_*
124 |
125 | # MightyMoose
126 | *.mm.*
127 | AutoTest.Net/
128 |
129 | # Web workbench (sass)
130 | .sass-cache/
131 |
132 | # Installshield output folder
133 | [Ee]xpress/
134 |
135 | # DocProject is a documentation generator add-in
136 | DocProject/buildhelp/
137 | DocProject/Help/*.HxT
138 | DocProject/Help/*.HxC
139 | DocProject/Help/*.hhc
140 | DocProject/Help/*.hhk
141 | DocProject/Help/*.hhp
142 | DocProject/Help/Html2
143 | DocProject/Help/html
144 |
145 | # Click-Once directory
146 | publish/
147 |
148 | # Publish Web Output
149 | *.[Pp]ublish.xml
150 | *.azurePubxml
151 | # TODO: Comment the next line if you want to checkin your web deploy settings
152 | # but database connection strings (with potential passwords) will be unencrypted
153 | *.pubxml
154 | *.publishproj
155 |
156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
157 | # checkin your Azure Web App publish settings, but sensitive information contained
158 | # in these scripts will be unencrypted
159 | PublishScripts/
160 |
161 | # NuGet Packages
162 | *.nupkg
163 | # The packages folder can be ignored because of Package Restore
164 | **/packages/*
165 | # except build/, which is used as an MSBuild target.
166 | !**/packages/build/
167 | # Uncomment if necessary however generally it will be regenerated when needed
168 | #!**/packages/repositories.config
169 | # NuGet v3's project.json files produces more ignorable files
170 | *.nuget.props
171 | *.nuget.targets
172 |
173 | # Microsoft Azure Build Output
174 | csx/
175 | *.build.csdef
176 |
177 | # Microsoft Azure Emulator
178 | ecf/
179 | rcf/
180 |
181 | # Windows Store app package directories and files
182 | AppPackages/
183 | BundleArtifacts/
184 | Package.StoreAssociation.xml
185 | _pkginfo.txt
186 |
187 | # Visual Studio cache files
188 | # files ending in .cache can be ignored
189 | *.[Cc]ache
190 | # but keep track of directories ending in .cache
191 | !*.[Cc]ache/
192 |
193 | # Others
194 | ClientBin/
195 | ~$*
196 | *~
197 | *.dbmdl
198 | *.dbproj.schemaview
199 | *.jfm
200 | *.pfx
201 | *.publishsettings
202 | orleans.codegen.cs
203 |
204 | # Since there are multiple workflows, uncomment next line to ignore bower_components
205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
206 | #bower_components/
207 |
208 | # RIA/Silverlight projects
209 | Generated_Code/
210 |
211 | # Backup & report files from converting an old project file
212 | # to a newer Visual Studio version. Backup files are not needed,
213 | # because we have git ;-)
214 | _UpgradeReport_Files/
215 | Backup*/
216 | UpgradeLog*.XML
217 | UpgradeLog*.htm
218 |
219 | # SQL Server files
220 | *.mdf
221 | *.ldf
222 | *.ndf
223 |
224 | # Business Intelligence projects
225 | *.rdl.data
226 | *.bim.layout
227 | *.bim_*.settings
228 |
229 | # Microsoft Fakes
230 | FakesAssemblies/
231 |
232 | # GhostDoc plugin setting file
233 | *.GhostDoc.xml
234 |
235 | # Node.js Tools for Visual Studio
236 | .ntvs_analysis.dat
237 | node_modules/
238 |
239 | # Typescript v1 declaration files
240 | typings/
241 |
242 | # Visual Studio 6 build log
243 | *.plg
244 |
245 | # Visual Studio 6 workspace options file
246 | *.opt
247 |
248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
249 | *.vbw
250 |
251 | # Visual Studio LightSwitch build output
252 | **/*.HTMLClient/GeneratedArtifacts
253 | **/*.DesktopClient/GeneratedArtifacts
254 | **/*.DesktopClient/ModelManifest.xml
255 | **/*.Server/GeneratedArtifacts
256 | **/*.Server/ModelManifest.xml
257 | _Pvt_Extensions
258 |
259 | # Paket dependency manager
260 | .paket/paket.exe
261 | paket-files/
262 |
263 | # FAKE - F# Make
264 | .fake/
265 |
266 | # JetBrains Rider
267 | .idea/
268 | *.sln.iml
269 |
270 | # CodeRush
271 | .cr/
272 |
273 | # Python Tools for Visual Studio (PTVS)
274 | __pycache__/
275 | *.pyc
276 |
277 | # Cake - Uncomment if you are using it
278 | # tools/**
279 | # !tools/packages.config
280 |
281 | # Telerik's JustMock configuration file
282 | *.jmconfig
283 |
284 | # BizTalk build output
285 | *.btp.cs
286 | *.btm.cs
287 | *.odx.cs
288 | *.xsd.cs
289 |
--------------------------------------------------------------------------------
/ArduinoProject/ArduinoProject.ino:
--------------------------------------------------------------------------------
1 |
2 | #define CLOCK 10
3 | #define WE 2
4 |
5 | #define PIN_D0 29
6 | #define PIN_D1 28
7 | #define PIN_D2 27
8 | #define PIN_D3 26
9 | #define PIN_D4 25
10 | #define PIN_D5 24
11 | #define PIN_D6 23
12 | #define PIN_D7 22
13 |
14 | int dataPtr = 0;
15 | int delayCounter = 0;
16 | int delayCounterTimer = 0;
17 | int delayCounterTimerMax = 4;
18 |
19 | void setup() {
20 |
21 | // This is only used if you want to attempt to let the Arduino generate the 4Mhz clock
22 | // that the SN76489 requires to operate. NOTE: Beacuse this uses a custom PWM to generate it
23 | // It MUST use pin 10 as in the clock define above. If you have a hardware 4mhz clock
24 | // device on the board however, you can leave the next line commented and not bother connecting a wire
25 | //setupFourMhzTimer();
26 |
27 | // Set up the R/W pin
28 | pinMode(WE, OUTPUT);
29 | digitalWrite(WE, HIGH);
30 |
31 | // Set up the Data I/O pins
32 | pinMode(PIN_D0, OUTPUT);
33 | pinMode(PIN_D1, OUTPUT);
34 | pinMode(PIN_D2, OUTPUT);
35 | pinMode(PIN_D3, OUTPUT);
36 | pinMode(PIN_D4, OUTPUT);
37 | pinMode(PIN_D5, OUTPUT);
38 | pinMode(PIN_D6, OUTPUT);
39 | pinMode(PIN_D7, OUTPUT);
40 |
41 | // Set them all to logic 0
42 | digitalWrite(PIN_D0, LOW);
43 | digitalWrite(PIN_D1, LOW);
44 | digitalWrite(PIN_D2, LOW);
45 | digitalWrite(PIN_D3, LOW);
46 | digitalWrite(PIN_D4, LOW);
47 | digitalWrite(PIN_D5, LOW);
48 | digitalWrite(PIN_D6, LOW);
49 | digitalWrite(PIN_D7, LOW);
50 |
51 | // Initialise the SN76489
52 | InitiliseSoundChip();
53 |
54 | // and the inbound default serial port
55 | Serial.begin(115200);
56 | }
57 |
58 | void setupFourMhzTimer()
59 | {
60 | // Iv'e no idea how this works :-) I grabbed it off the arduino forums
61 | // I made an attempt to understand it, but every tweak I made I killed it!!!
62 | // it works, it gives 4Mhz, so I'm just leaving it at that.
63 | pinMode(CLOCK, OUTPUT);
64 | TCCR2A = ((1 << WGM21) | (1 << COM2A0)); //0x23;
65 | TCCR2B = (1 << CS20); //0x09;
66 |
67 | OCR2A = 0x02;
68 | TIMSK2 = 0x00;
69 |
70 | OCR2B = 0x01;
71 | }
72 |
73 | void InitiliseSoundChip()
74 | {
75 | // Do the well known BOOO-BEEP BBC Initialisation sound.
76 | // Yes, it does actually serve a purpose :-)
77 |
78 | // The low tone that all BBCs play on startup (Before the higher beep all owners are used to) is actually
79 | // The default tone on channel 2 of the SN76489 sound chip. It produces this to let you know that it's working
80 | // and ready to recieve commands, and starts to produce it as soon as power is applied to the device.
81 |
82 | // We allow the default tone to be heard for half a second or so...
83 | delay(500);
84 |
85 | // Now we send the first batch of commands the BBC does on start up to the sound chip
86 | SendByteToSoundChip(0x82); // First byte of set Frequency on Tone 3 to decimal 1010
87 | SendByteToSoundChip(0x3F); // Second byte of set Frequency on Tone 3 to decimal 1010
88 | SendByteToSoundChip(0xBF); // Silence Tone Channel 2 (Volume is inverted on the beeb so 15 is silence, 0 is Loud)
89 | SendByteToSoundChip(0xA1); // First byte of set Frequency on Tone 2 to decimal 1009
90 | SendByteToSoundChip(0x3F); // Second byte of set Frequency on Tone 2 to decimal 1009
91 | SendByteToSoundChip(0xDF); // Silence Tone Channel 1 (Volume is inverted on the beeb so 15 is silence, 0 is Loud)
92 | SendByteToSoundChip(0xC0); // First byte of set Frequency on Tone 1 to decimal 1008
93 | SendByteToSoundChip(0x3F); // Second byte of set Frequency on Tone 1 to decimal 1008
94 | SendByteToSoundChip(0xFF); // Silence Noise Channel
95 | SendByteToSoundChip(0xE0); // Set noise channel to periodic noise with a low frequency
96 |
97 | // Now we wait a 50th of a second (I got these timings from the VGM file I recorded from a BBC Emulator)
98 | delay(20);
99 |
100 | // Next block of commands to the sound chip
101 | SendByteToSoundChip(0x92); // Set volume on tone channel 3 to 2
102 | SendByteToSoundChip(0x8F); // First byte of set Frequency on Tone 3 to decimal 239
103 | SendByteToSoundChip(0x0E); // Second byte of set Frequency on Tone 3 to decimal 239
104 |
105 | // Delay 15 50ths of a second (Approx 1 3rd of a sec)
106 | delay(300);
107 |
108 | // And finally the last sound chip command
109 | SendByteToSoundChip(0x9F); // Silence Tone 3
110 | }
111 |
112 | void SendByteToSoundChip(byte b)
113 | {
114 | // First split up the byte to send onto the individual I/O pins of the
115 | // chip's 8 bit data bus.
116 | digitalWrite(PIN_D0, (b&1)?HIGH:LOW);
117 | digitalWrite(PIN_D1, (b&2)?HIGH:LOW);
118 | digitalWrite(PIN_D2, (b&4)?HIGH:LOW);
119 | digitalWrite(PIN_D3, (b&8)?HIGH:LOW);
120 | digitalWrite(PIN_D4, (b&16)?HIGH:LOW);
121 | digitalWrite(PIN_D5, (b&32)?HIGH:LOW);
122 | digitalWrite(PIN_D6, (b&64)?HIGH:LOW);
123 | digitalWrite(PIN_D7, (b&128)?HIGH:LOW);
124 |
125 | // Then pulse the write enable line low for one millisecond. The R/W line on the SN76489 is inverted
126 | // that is it commonly spoken as "Read NOT Write" which essentially means that to write the data on the Dx
127 | // input pins you briefly have to pull the write line low, then return it high for normal read operation.
128 | digitalWrite(WE, LOW);
129 | delay(1);
130 | digitalWrite(WE, HIGH);
131 |
132 | }
133 |
134 | // This is the main program runtime loop fo the Arduino firmware.
135 | void loop() {
136 |
137 | // If there's anything coming down from the PC, just throw it
138 | // straight to the sound chip. No ceremony here......
139 | if(Serial.available() > 0)
140 | {
141 | byte b = Serial.read();
142 | SendByteToSoundChip(b);
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/ArduinoProject/test_other_timers_sn76489:
--------------------------------------------------------------------------------
1 | void setupFourMhzTimer()
2 | {
3 | // Iv'e no idea how this works :-) I grabbed it off the arduino forums
4 | // I made an attempt to understand it, but every tweak I made I killed it!!!
5 | // it works, it gives 4Mhz, so I'm just leaving it at that.
6 | pinMode(CLOCK, OUTPUT);
7 | TCCR2A = 0x23;// timer control configuration register А
8 | TCCR2B = 0x09; //timer control configuration register B
9 | OCR2A = 3; //7; //2mhz //5;//2.67mhz //4; //3.2mhz //3; //4mhz //8; //1.78mhz //I left this one - 4mhz ///comparison register A
10 | OCR2B = 1; //3; // //1; //1; //40:60 //1; //50:50 //3; //duty+46%/-54% } ///comparison register B
11 |
--------------------------------------------------------------------------------
/CIrcuit Screen Grab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/CIrcuit Screen Grab.png
--------------------------------------------------------------------------------
/Example VGM Tunes/13 Amps.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/13 Amps.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/1942.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/1942.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Addicts Anthem 3.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Addicts Anthem 3.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Addicts Anthem Miami Remix 89.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Addicts Anthem Miami Remix 89.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Addicts Anthem Remix.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Addicts Anthem Remix.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Addicts Anthem.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Addicts Anthem.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Blue Monday Remix.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Blue Monday Remix.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Chariots.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Chariots.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Cheers.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Cheers.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Circle in the Sand.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Circle in the Sand.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Cold Tea.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Cold Tea.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Dambuster March.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Dambuster March.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Disk User.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Disk User.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Domino Dancing.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Domino Dancing.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Dreamscape.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Dreamscape.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Enjoy the silence.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Enjoy the silence.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Equinoxe5.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Equinoxe5.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Flash Dance.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Flash Dance.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Ghostbusters.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Ghostbusters.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Golden Brown.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Golden Brown.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Good King.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Good King.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Newsong.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Newsong.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Only You.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Only You.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Pride.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Pride.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Raiders of the lost ark.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Raiders of the lost ark.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Rent.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Rent.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Sweet Dreams.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Sweet Dreams.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Thriller.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Thriller.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/True Colors.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/True Colors.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Valkeris.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Valkeris.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/Win Again.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/Win Again.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/airwolf.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/airwolf.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/ateam.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/ateam.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/dallas.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/dallas.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/doctor doctor.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/doctor doctor.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/elsewhere.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/elsewhere.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/in the mood.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/in the mood.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/its a sin.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/its a sin.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/stop.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/stop.vgm
--------------------------------------------------------------------------------
/Example VGM Tunes/tubular bells.vgm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Example VGM Tunes/tubular bells.vgm
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Peter "Shawty" Shaw
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Main Circuit.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Main Circuit.bmp
--------------------------------------------------------------------------------
/Main Circuit.dwg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Main Circuit.dwg
--------------------------------------------------------------------------------
/Pictures/DSC_0740.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Pictures/DSC_0740.JPG
--------------------------------------------------------------------------------
/Pictures/DSC_0741.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Pictures/DSC_0741.JPG
--------------------------------------------------------------------------------
/Pictures/DSC_0742.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Pictures/DSC_0742.JPG
--------------------------------------------------------------------------------
/Pictures/DSC_1048.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Pictures/DSC_1048.JPG
--------------------------------------------------------------------------------
/Pictures/DSC_1049.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shawty/sn76489arduino/855851493ffde33baeacbad3d28ba1e77153f88c/Pictures/DSC_1049.JPG
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sn76489arduino
2 | Project and Supporting files to turn an arduino into a BBC Micro music player.
3 |
4 | In this repository, you will find everything you need to create a BBC Micro Music player, using an Arduino, and a PC.
5 |
6 | I'll provide
7 |
8 | * Circuit Diagrams (To connect a Texas Instruments SN76489 *[The BBC Micro's hardware sound chip]* to your Arduino)
9 | * The code/Arduino IDE project, to run in the Arduino IDE and program into your arduino
10 | * A VGM Streamer application written in C# using dotnet core, to stream the music data from a PC to the Arduino
11 |
12 | ## A Few Notes
13 |
14 | The project was built on and designed to work with an arduino mega. It may take a little work to adapt it to work on other arduinos.
15 |
16 | The main thing you will have to do to use it on different devices will be to remap the pin numbers you use for the connections to the sound chip. Iv'e followed good arduino practice however, and provided easy to read pin number constants/defines at the top of the file, so it should be trivial with anyone who has a smattering of arduino experience to change it.
17 |
18 | MOST diagrams for the SN76489 sound chip that are available online, are taken from the original Texas Instruments data sheet. This data sheet **IS WRONG** in the original data sheet, data pin d0 was labeled as being on pin 3 of the chip, when in fact it is actually on pin 10.
19 |
20 | Once you've set the pin numbers up, build the circuit, then use the Arduino IDE, load the arduino project, and upload it to your chosen arduino device.
21 |
22 | The c# program has only been tested on a PC running under windows, however since it's dotnet core, then it should be possible to run it on ANY MACHINE that you have dot net core installed on. Dot Net core currently runs on Windows, Linux AND MacOS, instructions to install dotnet core are outside the scope of this project, the place to look for instructions for your platform is as follows:
23 |
24 | https://www.microsoft.com/net/learn/get-started/windows
25 |
26 | Once you have dotnet core installed and running correctly, use the command line of your system, change to the folder called "VGMPlayer" in the place you cloned this project, and type
27 |
28 | **dotnet run**
29 |
30 | Running the player is simple, but you'll need to change a few things first, the name of the file to play is in the file 'Program.cs' on line 69, you'll need to change this line to the file you want to play, Iv'e deliberately not made any effort to make this windows player user aware, as it's really only a proof of concept, if you know C# and you want to go changing it to ask for the file name or anything like that, then feel free to do so.
31 |
32 | In the file "SerialSender.cs", on line 10 is the name of the serial port your arduino is connected too, you will need to change this to whatever port your OS tells you your arduino is using in the arduino IDE tools menu, it will be something like "COMxx" for windows and "/dev/ttyxxxx" or "/dev/....." on linux. DOn't ask me about MacOS I don't own one, and I ain't got a clue :-)
33 |
34 | To hear the sound output, you **WILL NEED** an amplifed speaker. Personally (As you can see in the YouTube video here: https://www.youtube.com/watch?v=rOjFRLOkblk ) I use a small USB Powered Xmi mobile phone speaker, but connecting a jack plug to the line in on your PC works just as well, and gives you the added bonus that you can record it too.
35 |
36 | What ever you do, you cannot connect headphones directly to the output as it's too weak to drive them correctly so you'll not be able to hear what's being played.
37 |
38 | Once these 2 changes are correct, and you dotnet run the app, then assuming your wiring is correct, and you got the arduino programmed correctly, and you have your sound output hooked up, you should hear the dulcet tones of a genuine 1980's era BBC Sound chip playing back actual song data from an actual 80's era BBC Micro.
39 |
40 | ### How I created the VGM files......
41 | Quite easily as it happens.
42 |
43 | Make sure you have Tom Sneddons "model-b" BBC Micro Emulator installed ( http://archive.retro-kit.co.uk/bbc.nvg.org/emulators.php3.html#Model-B ) it has to be this specific emulator as this is the only one I can find that records VGM, rather than recording the wave audio output which is what all the others do.
44 |
45 | Once you have the emulator running, click "special" on the menu bar, then "Start Recording Sound", run the program who's music you want to record and let it play util you record everything you want to record.
46 |
47 | Once done, click "special" again and "Stop recording sound", wait a few seconds and the emulator will ask you where you want to save the VGM file.
48 |
49 | Iv'e provided a bunch of VGM test files that I recorded from the emulator in the "Example VGM Tunes" folder, please be aware though that some of them are well, awfull :-) Simply beacuse some of them used some rather bizare tricks to try and get things like argeggios and complex harmonies going (Every trick in Ian Waughs excelent - "Making music on the BBC Micro Computer" book basically) and the emulator bless it's little socks did try to record things as faithfully as it could, but just didn't manage to get a good quality grab for some of them. I'm sure with patience and time I'll be able to make some better ones.
50 |
51 | You'll also find the soundtrack to my multipart BBC Model B demo "DreamScape" ( https://www.youtube.com/watch?v=_mrOAB4UBcM ) in there too, and that plays rather quite well :-)
52 |
53 | **Why a VGM file**
54 |
55 | When you record a VGM file, you physically record the instructions sent to the emulated BBC sound chip, these are the exact same instructions that the real SN76489 also understands, and obeys to make sounds. If you download a copy of the BBC Advanced User Guide, it has an entire chapter on writing these instructions on an actual BBC Micro direct to the real chip in a BBC Micro (One of the first ever 6502 Machine Code Programs I wrote on a BBC all those years ago was a direct hardware music player), the VGM file is a standard emulator format used by a number of different emulators for recording this chip level music data containing chip commands.
56 |
57 | The PC Player is in actual fact a mini VGM format file player, but it only looks at and uses the parts that are specific to the BBC Micro or SN76489 chip. The SN chip was added to the spec as it was used in a number of consoles in the 1990's, the most famous of wich was the Sega Mega Drive, so it suits our purposes perfectly.
58 |
59 |
--------------------------------------------------------------------------------
/VGMPlayer/HighResTimer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | // C# High Resolution Timer Class by Ken Loveday
4 | // see: https://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer
5 | // for more info.
6 |
7 | namespace VGMPlayer
8 | {
9 | ///
10 | /// MicroStopwatch class
11 | ///
12 | public class MicroStopwatch : System.Diagnostics.Stopwatch
13 | {
14 | readonly double _microSecPerTick =
15 | 1000000D / System.Diagnostics.Stopwatch.Frequency;
16 |
17 | public MicroStopwatch()
18 | {
19 | if (!System.Diagnostics.Stopwatch.IsHighResolution)
20 | {
21 | throw new Exception("On this system the high-resolution " +
22 | "performance counter is not available");
23 | }
24 | }
25 |
26 | public long ElapsedMicroseconds
27 | {
28 | get
29 | {
30 | return (long)(ElapsedTicks * _microSecPerTick);
31 | }
32 | }
33 | }
34 |
35 | ///
36 | /// MicroTimer class
37 | ///
38 | public class MicroTimer
39 | {
40 | public delegate void MicroTimerElapsedEventHandler(
41 | object sender,
42 | MicroTimerEventArgs timerEventArgs);
43 | public event MicroTimerElapsedEventHandler MicroTimerElapsed;
44 |
45 | System.Threading.Thread _threadTimer = null;
46 | long _ignoreEventIfLateBy = long.MaxValue;
47 | long _timerIntervalInMicroSec = 0;
48 | bool _stopTimer = true;
49 |
50 | public MicroTimer()
51 | {
52 | }
53 |
54 | public MicroTimer(long timerIntervalInMicroseconds)
55 | {
56 | Interval = timerIntervalInMicroseconds;
57 | }
58 |
59 | public long Interval
60 | {
61 | get
62 | {
63 | return System.Threading.Interlocked.Read(
64 | ref _timerIntervalInMicroSec);
65 | }
66 | set
67 | {
68 | System.Threading.Interlocked.Exchange(
69 | ref _timerIntervalInMicroSec, value);
70 | }
71 | }
72 |
73 | public long IgnoreEventIfLateBy
74 | {
75 | get
76 | {
77 | return System.Threading.Interlocked.Read(
78 | ref _ignoreEventIfLateBy);
79 | }
80 | set
81 | {
82 | System.Threading.Interlocked.Exchange(
83 | ref _ignoreEventIfLateBy, value <= 0 ? long.MaxValue : value);
84 | }
85 | }
86 |
87 | public bool Enabled
88 | {
89 | set
90 | {
91 | if (value)
92 | {
93 | Start();
94 | }
95 | else
96 | {
97 | Stop();
98 | }
99 | }
100 | get
101 | {
102 | return (_threadTimer != null && _threadTimer.IsAlive);
103 | }
104 | }
105 |
106 | public void Start()
107 | {
108 | if (Enabled || Interval <= 0)
109 | {
110 | return;
111 | }
112 |
113 | _stopTimer = false;
114 |
115 | System.Threading.ThreadStart threadStart = delegate ()
116 | {
117 | NotificationTimer(ref _timerIntervalInMicroSec,
118 | ref _ignoreEventIfLateBy,
119 | ref _stopTimer);
120 | };
121 |
122 | _threadTimer = new System.Threading.Thread(threadStart);
123 | _threadTimer.Priority = System.Threading.ThreadPriority.Highest;
124 | _threadTimer.Start();
125 | }
126 |
127 | public void Stop()
128 | {
129 | _stopTimer = true;
130 | }
131 |
132 | public void StopAndWait()
133 | {
134 | StopAndWait(System.Threading.Timeout.Infinite);
135 | }
136 |
137 | public bool StopAndWait(int timeoutInMilliSec)
138 | {
139 | _stopTimer = true;
140 |
141 | if (!Enabled || _threadTimer.ManagedThreadId ==
142 | System.Threading.Thread.CurrentThread.ManagedThreadId)
143 | {
144 | return true;
145 | }
146 |
147 | return _threadTimer.Join(timeoutInMilliSec);
148 | }
149 |
150 | public void Abort()
151 | {
152 | _stopTimer = true;
153 |
154 | if (Enabled)
155 | {
156 | _threadTimer.Abort();
157 | }
158 | }
159 |
160 | void NotificationTimer(ref long timerIntervalInMicroSec,
161 | ref long ignoreEventIfLateBy,
162 | ref bool stopTimer)
163 | {
164 | int timerCount = 0;
165 | long nextNotification = 0;
166 |
167 | MicroStopwatch microStopwatch = new MicroStopwatch();
168 | microStopwatch.Start();
169 |
170 | while (!stopTimer)
171 | {
172 | long callbackFunctionExecutionTime =
173 | microStopwatch.ElapsedMicroseconds - nextNotification;
174 |
175 | long timerIntervalInMicroSecCurrent =
176 | System.Threading.Interlocked.Read(ref timerIntervalInMicroSec);
177 | long ignoreEventIfLateByCurrent =
178 | System.Threading.Interlocked.Read(ref ignoreEventIfLateBy);
179 |
180 | nextNotification += timerIntervalInMicroSecCurrent;
181 | timerCount++;
182 | long elapsedMicroseconds = 0;
183 |
184 | while ((elapsedMicroseconds = microStopwatch.ElapsedMicroseconds)
185 | < nextNotification)
186 | {
187 | System.Threading.Thread.SpinWait(10);
188 | }
189 |
190 | long timerLateBy = elapsedMicroseconds - nextNotification;
191 |
192 | if (timerLateBy >= ignoreEventIfLateByCurrent)
193 | {
194 | continue;
195 | }
196 |
197 | MicroTimerEventArgs microTimerEventArgs =
198 | new MicroTimerEventArgs(timerCount,
199 | elapsedMicroseconds,
200 | timerLateBy,
201 | callbackFunctionExecutionTime);
202 | MicroTimerElapsed(this, microTimerEventArgs);
203 | }
204 |
205 | microStopwatch.Stop();
206 | }
207 | }
208 |
209 | ///
210 | /// MicroTimer Event Argument class
211 | ///
212 | public class MicroTimerEventArgs : EventArgs
213 | {
214 | // Simple counter, number times timed event (callback function) executed
215 | public int TimerCount { get; private set; }
216 |
217 | // Time when timed event was called since timer started
218 | public long ElapsedMicroseconds { get; private set; }
219 |
220 | // How late the timer was compared to when it should have been called
221 | public long TimerLateBy { get; private set; }
222 |
223 | // Time it took to execute previous call to callback function (OnTimedEvent)
224 | public long CallbackFunctionExecutionTime { get; private set; }
225 |
226 | public MicroTimerEventArgs(int timerCount,
227 | long elapsedMicroseconds,
228 | long timerLateBy,
229 | long callbackFunctionExecutionTime)
230 | {
231 | TimerCount = timerCount;
232 | ElapsedMicroseconds = elapsedMicroseconds;
233 | TimerLateBy = timerLateBy;
234 | CallbackFunctionExecutionTime = callbackFunctionExecutionTime;
235 | }
236 |
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/VGMPlayer/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | // VGM Player written by
5 | // !Shawty!/DS in 2017
6 | // NOTE: This player is designed only to playback BBC Model B VGM Files
7 |
8 | namespace VGMPlayer
9 | {
10 | class Program
11 | {
12 | static VgmFile vgmFile = new VgmFile();
13 | static MicroTimer timer;
14 | static bool displayRunning = false;
15 |
16 | static void DisplayThread()
17 | {
18 | Console.Clear();
19 | while (displayRunning)
20 | {
21 | Console.SetCursorPosition(0, 0);
22 | Console.Write("BBC Micro VGM Player (Serial Port Arduino Version)");
23 |
24 | Console.SetCursorPosition(0, 2);
25 | Console.Write("Delay {0} ", vgmFile.DelayCounter);
26 |
27 | Console.SetCursorPosition(0, 3);
28 | Console.Write("Last Byte Sent: 0x{0:X} ", vgmFile.LastByteSent);
29 |
30 | Console.SetCursorPosition(0, 4);
31 | Console.Write("Tone 3 Volume: {0}".PadRight(60, ' '), vgmFile.Tone3Volume);
32 |
33 | Console.SetCursorPosition(0, 5);
34 | Console.Write("Tone 2 Volume: {0}".PadRight(60, ' '), vgmFile.Tone2Volume);
35 |
36 | Console.SetCursorPosition(0, 6);
37 | Console.Write("Tone 1 Volume: {0}".PadRight(60, ' '), vgmFile.Tone1Volume);
38 |
39 | Console.SetCursorPosition(0, 7);
40 | Console.Write(" Noise Volume: {0}".PadRight(60, ' '), vgmFile.NoiseVolume);
41 |
42 | Console.SetCursorPosition(0, 9);
43 | Console.WriteLine("3 {0}".PadRight(60, ' '), new String('*', vgmFile.Tone3Bar));
44 |
45 | Console.SetCursorPosition(0, 10);
46 | Console.WriteLine("2 {0}".PadRight(60, ' '), new String('*', vgmFile.Tone2Bar));
47 |
48 | Console.SetCursorPosition(0, 11);
49 | Console.WriteLine("1 {0}".PadRight(60, ' '), new String('*', vgmFile.Tone1Bar));
50 | }
51 | Console.SetCursorPosition(0, 14);
52 | Console.WriteLine("Song Finished, Please Press Return to Exit");
53 | }
54 |
55 | static void CallPlayer(object stateInfo, MicroTimerEventArgs e)
56 | {
57 | if(vgmFile.SongLooping)
58 | {
59 | displayRunning = false;
60 | timer.Enabled = false;
61 | timer.Stop();
62 | }
63 |
64 | vgmFile.PlayNext();
65 | }
66 |
67 | static void Main(string[] args)
68 | {
69 | vgmFile.Load(@"C:\BBC Micro Emulation\Embeded Stuff\dreamscape.vgm"); // CHANGE THIS TO THE FILE YOU WANT TO PLAY
70 |
71 | timer = new MicroTimer();
72 | timer.MicroTimerElapsed += new MicroTimer.MicroTimerElapsedEventHandler(CallPlayer);
73 | timer.Interval = 22; // NOTE: This is 22 MICROSECONDS, NOT MILLISECONDS as is usually the case in windows/dotnet
74 | timer.Enabled = true;
75 |
76 | Thread songInfoThread = new Thread(new ThreadStart(DisplayThread));
77 | displayRunning = true;
78 | songInfoThread.Start();
79 |
80 | Console.SetCursorPosition(0, 13);
81 | Console.WriteLine("Press return to exit.");
82 |
83 | // Wait for return to be pressed.....
84 | Console.ReadLine();
85 | displayRunning = false;
86 | timer.Enabled = false;
87 | timer.Stop();
88 |
89 | }
90 |
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/VGMPlayer/SerialSender.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO.Ports;
3 |
4 | namespace VGMPlayer
5 | {
6 | public class SerialSender: IDisposable
7 | {
8 | private SerialPort _serialPort = new SerialPort();
9 |
10 | private string _portName = "COM11"; // Change this to the com port your arduino is connected too
11 | private int _baudRate = 115200;
12 |
13 | public SerialSender()
14 | {
15 | _serialPort.PortName = _portName;
16 | _serialPort.BaudRate = _baudRate;
17 | _serialPort.DataBits = 8;
18 | _serialPort.Parity = Parity.None;
19 | _serialPort.StopBits = StopBits.One;
20 | _serialPort.Open();
21 | }
22 |
23 | public void Send(byte b)
24 | {
25 | byte[] buf = new byte[1];
26 | buf[0] = b;
27 | _serialPort.Write(buf, 0, 1);
28 | }
29 |
30 | public void Dispose()
31 | {
32 | if(_serialPort.IsOpen)
33 | {
34 | _serialPort.Close();
35 | }
36 | }
37 |
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/VGMPlayer/VGMPlayer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/VGMPlayer/VGMPlayer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.2010
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VGMPlayer", "VGMPlayer.csproj", "{EB6E23BC-60B7-4FA3-AA53-67DBB6AF4103}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {EB6E23BC-60B7-4FA3-AA53-67DBB6AF4103}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {EB6E23BC-60B7-4FA3-AA53-67DBB6AF4103}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {EB6E23BC-60B7-4FA3-AA53-67DBB6AF4103}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {EB6E23BC-60B7-4FA3-AA53-67DBB6AF4103}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {008C4A21-7E50-4FA3-B9D0-302CC9818B4E}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/VGMPlayer/VgmFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 |
6 | // VGM Player written by
7 | // !Shawty!/DS in 2017
8 | // NOTE: This player is designed only to playback BBC Model B VGM Files
9 |
10 | namespace VGMPlayer
11 | {
12 | public class VgmFile
13 | {
14 | // This class is based on the 1.50 VGM File format
15 |
16 | private SerialSender _serialSender = new SerialSender();
17 |
18 | private VgmFileHeader _header = new VgmFileHeader();
19 | private byte[] _chipData;
20 |
21 | // The player process is quite simple.
22 | // All we care about are VGM Commands 0x61, 0x63, 0x50 & 0x66
23 | // 61 means set a delay, if we see one of these we set the delay counter to that value, then each time the player is called
24 | // we decrement that delay until it reaches 0, we do not allow the vgm data reader to progress to the next byte if this
25 | // delay counter is not 0. Command 63 is a shortcut for a delay of 1/50th of a second
26 | // Command 50, means the next byte is to be sent directly to the sound chip, so thats exactly what we do :-)
27 | // Command 66, means end of song data, so we set the variable to show that it's looped, then reset the song data pointer to 0
28 |
29 | private int _dataPointer = 0; // Current position in our chip data array
30 | private int _delayCounter = 0; // Counts down to 0, and is set by a delay command in the chip data
31 | //private int _delayCounterTimer = 0; // Used to control how fast the delay counter counts down
32 | //private int _delayCounterTimerMax = 500; // Used to set the speed on the delay counter timer. Higher is slower
33 | // NOTE: You WILL need to fiddle with the above max value to get the correct playback speed, remember the PC you
34 | // run this in is likley to be orders of magnitude faster than the 16Mhz arduino recieving the data to send to
35 | // the sound chip, which itself is only clocked at 4Mhz.
36 | // The serial connection in the Arduino code is set at 115200, and the Arduino's inbound buffer is only 128 bytes
37 | // so timing will need to be adjusted!!!
38 |
39 | private byte barMax = 32; // Maximum size a bar value can be
40 | private byte barSpeed = 0; // Counter to track bar speed
41 | private byte barSpeedMax = 192; // How many itterations of the play loop before we reduce a bar value;
42 |
43 | // These 3 vars are public (That is they can be accessed outside the player class) and are read only.
44 | public bool SongLooping { get; private set; }
45 | public int DelayCounter { get { return _delayCounter; } }
46 | public byte LastByteSent { get; private set; }
47 |
48 | public byte Tone3Volume { get; private set; }
49 | public byte Tone2Volume { get; private set; }
50 | public byte Tone1Volume { get; private set; }
51 | public byte NoiseVolume { get; private set; }
52 |
53 | public byte Tone3Bar { get; private set; }
54 | public byte Tone2Bar { get; private set; }
55 | public byte Tone1Bar { get; private set; }
56 |
57 | public void Load(string fileName)
58 | {
59 | Stream fileStream = new FileStream(fileName, FileMode.Open);
60 | BinaryReader reader = new BinaryReader(fileStream);
61 |
62 | LoadHeader(reader);
63 | LoadChipData(reader);
64 |
65 | fileStream.Close();
66 |
67 | SongLooping = false;
68 | }
69 |
70 | public void PlayNext()
71 | {
72 | // Plays the next available entry in the chip data array, this is designed to be called repeatedly
73 | // from a loop outside the class, so each call of it will decode one command only.
74 |
75 | // Adjust our tone counters
76 | if (barSpeed == 0)
77 | {
78 | Tone3Bar--; if (Tone3Bar < 1) { Tone3Bar = 1; }; if (Tone3Bar > barMax) { Tone3Bar = barMax; }
79 | Tone2Bar--; if (Tone2Bar < 1) { Tone2Bar = 1; }; if (Tone2Bar > barMax) { Tone2Bar = barMax; }
80 | Tone1Bar--; if (Tone1Bar < 1) { Tone1Bar = 1; }; if (Tone1Bar > barMax) { Tone1Bar = barMax; }
81 | barSpeed = barSpeedMax;
82 | }
83 | else
84 | {
85 | barSpeed--;
86 | }
87 |
88 | // If we have a delay set
89 | if (_delayCounter > 0)
90 | {
91 | // THIS WAS THE OLD WAY OF DOING THE TIMING UNTIL I MANAGED TO USE A HIGH RES TIMER
92 | // IVE NOT TRIED THIS ON ANYTHING OTHER THAN WINDOWS 7 YET, SO MAY HAVE TO Go
93 | // BACK TO USING THIS ON OTHER PLATFORMS
94 | // Increment our delay timer
95 | //_delayCounterTimer++;
96 | // And if it's hit our max.....
97 | //if(_delayCounterTimer > _delayCounterTimerMax)
98 | //{
99 | // Decrement the actual delay and reset the timer
100 | _delayCounter--;
101 | //_delayCounterTimer = 0;
102 | //}
103 | return;
104 | }
105 |
106 | // if our delayCounter is 0 then we get here, ready to decode the next command
107 |
108 | byte currentDataByte = _chipData[_dataPointer];
109 |
110 | // Now we decode our VGM chip data commands.
111 | // In the case of the SN76489 in the BBC not all the commands are used, so we decode
112 | // only the one we are interested in.
113 |
114 | switch (currentDataByte)
115 | {
116 | case 0x61:
117 | // Set a delay counter value, low byte is in the next byte, high in the one following that
118 | int delayVal = (_chipData[_dataPointer + 2] << 8) + _chipData[_dataPointer + 1];
119 | _delayCounter = delayVal;
120 | _dataPointer = _dataPointer + 3; // Increase pointer to next data entry
121 |
122 | //Console.WriteLine("Set Delay {0}", _delayCounter);
123 | break;
124 |
125 | case 0x63:
126 | // Short cut for a 1/50th of a second delay
127 | _delayCounter = 0x7203; // See VGM spec for these values (These are 20ms on a PAL System running at 4Mhz)
128 | _dataPointer++; // Increase pointer to next data entry;
129 |
130 | //Console.WriteLine("Set 1/50th Second Delay");
131 | break;
132 |
133 | case 0x50:
134 | // This is an actual raw byte to send to the sound chip
135 | byte chipByte = _chipData[_dataPointer + 1];
136 | _dataPointer = _dataPointer + 2; // Increase pointer to the next entry
137 |
138 | _serialSender.Send(chipByte);
139 |
140 | // Extract some usefull info from the command
141 | // Where not going to do frequencies or anything like that, beacuse they are 2 byte
142 | // we risk slowing down the high presicion send loop too much.
143 |
144 | // Volumes however are simple.
145 | if ((chipByte & 0x90) == 0x90)
146 | {
147 | Tone3Volume = (byte)(chipByte & 0x0F);
148 | }
149 |
150 | if ((chipByte & 0xB0) == 0xB0)
151 | {
152 | Tone2Volume = (byte)(chipByte & 0x0F);
153 | }
154 |
155 | if ((chipByte & 0xD0) == 0xD0)
156 | {
157 | Tone1Volume = (byte)(chipByte & 0x0F);
158 | }
159 |
160 | if ((chipByte & 0xF0) == 0xF0)
161 | {
162 | NoiseVolume = (byte)(chipByte & 0x0F);
163 | }
164 |
165 | // and we can at least do some simple counters, that we set to max
166 | // when a frequency on a channel is triggered, and count down
167 | // so while not frequency related, we can at least do a simple
168 | // kind of VU :-)
169 |
170 | if ((chipByte & 0x80) == 0x80)
171 | {
172 | Tone3Bar = barMax;
173 | }
174 |
175 | if ((chipByte & 0xA0) == 0xA0)
176 | {
177 | Tone2Bar = barMax;
178 | }
179 |
180 | if ((chipByte & 0xC0) == 0xC0)
181 | {
182 | Tone1Bar = barMax;
183 | }
184 |
185 | //Console.WriteLine("Send Chip Byte {0}", chipByte);
186 | LastByteSent = chipByte;
187 | break;
188 |
189 | case 0x66:
190 | // End of current song data
191 | _dataPointer = 0; // Reset and loop our song
192 | SongLooping = true; // Set this so that calling program knows if song is looping
193 |
194 | //Console.WriteLine("Reset Data Pointer");
195 | break;
196 |
197 | default:
198 | // This shouldn't be needed, well at least the data pointer check shouldn't be needed anyway
199 | // beacuse in theory we should always hit a 0x66 at the end. However if the 66 is not found, then
200 | // this stops a program crash due to trying to access data outside the array.
201 | // The increment is still needed however, as that allows us to skip over VGM commands that we don't yet implement.
202 | _dataPointer++;
203 | if(_dataPointer > (_chipData.Length - 1))
204 | {
205 | _dataPointer = 0;
206 | }
207 |
208 | //Console.WriteLine("Unknown VGM Command {0}", currentDataByte);
209 | break;
210 | }
211 | }
212 |
213 | private void LoadHeader(BinaryReader reader)
214 | {
215 | byte[] magicBytes = reader.ReadBytes(4);
216 | string magic = Encoding.Default.GetString(magicBytes).Trim();
217 |
218 | if(magic != "Vgm")
219 | {
220 | throw new ApplicationException("Specifed file is NOT a VGM file");
221 | }
222 |
223 | _header.VgmMagic = magic;
224 |
225 | _header.EofOffset = reader.ReadUInt32() + 4; // Eof val is 4 bytes in, so the actual offset is this + 4
226 | _header.Version = reader.ReadUInt32();
227 | _header.Sn76489Clock = reader.ReadUInt32();
228 | _header.Ym2413Clock = reader.ReadUInt32();
229 | _header.Gd3Offset = reader.ReadUInt32();
230 | _header.TotalSamples = reader.ReadUInt32();
231 | _header.LoopOffset = reader.ReadUInt32();
232 | _header.LoopOffset = reader.ReadUInt32();
233 | _header.Rate = reader.ReadUInt32();
234 | _header.SnFb = reader.ReadUInt16();
235 | _header.Snw = reader.ReadByte();
236 | _header.Reserved = reader.ReadByte();
237 | _header.Ym2612Clock = reader.ReadUInt32();
238 | _header.Ym2151Clock = reader.ReadUInt32();
239 |
240 | // From this point in the file, the following offset + the current file pointer is the ACTUAL location of the chip data
241 | // so we need to work out and record what that location actually is.
242 | // In most cases it will be 0x40 for v1.50 and prior, but it CAN be different, esp in the V1.50 VGM spec
243 | // yes this is a messy way of doing it, and if the data position is greater than 32 bits then where screwed
244 | // however, 32 bits tends to suggest a 4gb file, and Iv'e not seen one break the 10mb barrier yet, so I think where
245 | // fairly safe. :-)
246 | long currentFilePointer = reader.BaseStream.Position;
247 | _header.VgmDataOffset = reader.ReadUInt32();
248 | if (_header.VgmDataOffset == 0)
249 | {
250 | // No offset was specified, so we default to 0x40 which is the known start offset.
251 | _header.VgmDataOffset = 0x40;
252 | }
253 | else
254 | {
255 | // There is a positive offset, so lets take the file pos, add that on and store that.
256 | _header.VgmDataOffset = (uint)((int)currentFilePointer + _header.VgmDataOffset);
257 | }
258 |
259 | // We don't use the last 2 reserved words in the header
260 | var reserved = reader.ReadUInt32();
261 | reserved = reader.ReadUInt32();
262 |
263 | }
264 |
265 | private void LoadChipData(BinaryReader reader)
266 | {
267 | List result = new List();
268 |
269 | // Move the file stream to the start of our data
270 | reader.BaseStream.Seek(_header.VgmDataOffset, SeekOrigin.Begin);
271 |
272 | var dataSize = _header.EofOffset - _header.VgmDataOffset;
273 |
274 | result.AddRange(reader.ReadBytes((int)dataSize));
275 | _chipData = result.ToArray();
276 | }
277 |
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/VGMPlayer/VgmFileHeader.cs:
--------------------------------------------------------------------------------
1 | namespace VGMPlayer
2 | {
3 | public class VgmFileHeader
4 | {
5 | public string VgmMagic { get; set; }
6 | public uint EofOffset { get; set; }
7 | public uint Version { get; set; }
8 | public uint Sn76489Clock { get; set; }
9 | public uint Ym2413Clock { get; set; }
10 | public uint Gd3Offset { get; set; }
11 | public uint TotalSamples { get; set; }
12 | public uint LoopOffset { get; set; }
13 | public uint LoopSamples { get; set; }
14 | public uint Rate { get; set; }
15 | public ushort SnFb { get; set; }
16 | public byte Snw { get; set; }
17 | public byte Reserved { get; set; }
18 | public uint Ym2612Clock { get; set; }
19 | public uint Ym2151Clock { get; set; }
20 | public uint VgmDataOffset { get; set; }
21 |
22 | }
23 | }
24 |
--------------------------------------------------------------------------------