Creating ZX SOUND

Started by Baggey, February 01, 2021, 08:17:48

Previous topic - Next topic

Baggey

#30
Hi Guy's,

Ive just Wrote Two small routine's to spit out the Music Data, Directly from the memory of the emulator. So no typo errors!! It comes from the "Manic.z80" file.
I Remember you saying the Data seemed to be strange towards the end!

I do apologize for typing it in wrong! :-[ The code should now be spot on!   ;D

Firstly is the byte held at each memory address location:-

Building Spectrum emulator
Compiling:ZXio.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 11429 bytes.
Compiling:Z80.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 279826 bytes.
Compiling:video.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 5916 bytes.
Compiling:videoMemory.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 9359 bytes.
Compiling:OO.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 143179 bytes.
Compiling:CB.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 107153 bytes.
Compiling:ED.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 39351 bytes.
Compiling:DD.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 46811 bytes.
Compiling:FD.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 55618 bytes.
Compiling:DDCB.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 114656 bytes.
Compiling:FDCB.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 159967 bytes.
Compiling:Spectrum emulator.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 61839 bytes.
Linking:Spectrum emulator.exe
Executing:Spectrum emulator.exe
start ADDRESS
PEEK(33902)=80
PEEK(33903)=128
PEEK(33904)=129
PEEK(33905)=80
PEEK(33906)=102
PEEK(33907)=103
PEEK(33908)=80
PEEK(33909)=86
PEEK(33910)=87
PEEK(33911)=50
PEEK(33912)=86
PEEK(33913)=87
PEEK(33914)=50
PEEK(33915)=171
PEEK(33916)=203
PEEK(33917)=50
PEEK(33918)=43
PEEK(33919)=51
PEEK(33920)=50
PEEK(33921)=43
PEEK(33922)=51
PEEK(33923)=50
PEEK(33924)=171
PEEK(33925)=203
PEEK(33926)=50
PEEK(33927)=51
PEEK(33928)=64
PEEK(33929)=50
PEEK(33930)=51
PEEK(33931)=64
PEEK(33932)=50
PEEK(33933)=171
PEEK(33934)=203
PEEK(33935)=50
PEEK(33936)=128
PEEK(33937)=129
PEEK(33938)=50
PEEK(33939)=128
PEEK(33940)=129
PEEK(33941)=50
PEEK(33942)=102
PEEK(33943)=103
PEEK(33944)=50
PEEK(33945)=86
PEEK(33946)=87
PEEK(33947)=50
PEEK(33948)=96
PEEK(33949)=86
PEEK(33950)=50
PEEK(33951)=171
PEEK(33952)=192
PEEK(33953)=50
PEEK(33954)=43
PEEK(33955)=48
PEEK(33956)=50
PEEK(33957)=43
PEEK(33958)=48
PEEK(33959)=50
PEEK(33960)=171
PEEK(33961)=192
PEEK(33962)=50
PEEK(33963)=48
PEEK(33964)=68
PEEK(33965)=50
PEEK(33966)=48
PEEK(33967)=68
PEEK(33968)=50
PEEK(33969)=171
PEEK(33970)=192
PEEK(33971)=50
PEEK(33972)=136
PEEK(33973)=137
PEEK(33974)=50
PEEK(33975)=136
PEEK(33976)=137
PEEK(33977)=50
PEEK(33978)=114
PEEK(33979)=115
PEEK(33980)=50
PEEK(33981)=76
PEEK(33982)=77
PEEK(33983)=50
PEEK(33984)=76
PEEK(33985)=77
PEEK(33986)=50
PEEK(33987)=171
PEEK(33988)=192
PEEK(33989)=50
PEEK(33990)=38
PEEK(33991)=48
PEEK(33992)=50
PEEK(33993)=38
PEEK(33994)=48
PEEK(33995)=50
PEEK(33996)=171
PEEK(33997)=192
PEEK(33998)=50
PEEK(33999)=48
PEEK(34000)=68
PEEK(34001)=50
PEEK(34002)=48
PEEK(34003)=68
PEEK(34004)=50
PEEK(34005)=171
PEEK(34006)=192
PEEK(34007)=50
PEEK(34008)=136
PEEK(34009)=137
PEEK(34010)=50
PEEK(34011)=136
PEEK(34012)=137
PEEK(34013)=50
PEEK(34014)=114
PEEK(34015)=115
PEEK(34016)=50
PEEK(34017)=76
PEEK(34018)=77
PEEK(34019)=50
PEEK(34020)=76
PEEK(34021)=77
PEEK(34022)=50
PEEK(34023)=171
PEEK(34024)=203
PEEK(34025)=50
PEEK(34026)=38
PEEK(34027)=51
PEEK(34028)=50
PEEK(34029)=38
PEEK(34030)=51
PEEK(34031)=50
PEEK(34032)=171
PEEK(34033)=203
PEEK(34034)=50
PEEK(34035)=51
PEEK(34036)=64
PEEK(34037)=50
PEEK(34038)=51
PEEK(34039)=64
PEEK(34040)=50
PEEK(34041)=171
PEEK(34042)=203
PEEK(34043)=50
PEEK(34044)=128
PEEK(34045)=129
PEEK(34046)=50
PEEK(34047)=128
PEEK(34048)=129
PEEK(34049)=50
PEEK(34050)=102
PEEK(34051)=103
PEEK(34052)=50
PEEK(34053)=86
PEEK(34054)=87
PEEK(34055)=50
PEEK(34056)=64
PEEK(34057)=65
PEEK(34058)=50
PEEK(34059)=128
PEEK(34060)=171
PEEK(34061)=50
PEEK(34062)=32
PEEK(34063)=43
PEEK(34064)=50
PEEK(34065)=32
PEEK(34066)=43
PEEK(34067)=50
PEEK(34068)=128
PEEK(34069)=171
PEEK(34070)=50
PEEK(34071)=43
PEEK(34072)=51
PEEK(34073)=50
PEEK(34074)=43
PEEK(34075)=51
PEEK(34076)=50
PEEK(34077)=128
PEEK(34078)=171
PEEK(34079)=50
PEEK(34080)=128
PEEK(34081)=129
PEEK(34082)=50
PEEK(34083)=128
PEEK(34084)=129
PEEK(34085)=50
PEEK(34086)=102
PEEK(34087)=103
PEEK(34088)=50
PEEK(34089)=86
PEEK(34090)=87
PEEK(34091)=50
PEEK(34092)=64
PEEK(34093)=65
PEEK(34094)=50
PEEK(34095)=128
PEEK(34096)=152
PEEK(34097)=50
PEEK(34098)=32
PEEK(34099)=38
PEEK(34100)=50
PEEK(34101)=32
PEEK(34102)=38
PEEK(34103)=50
PEEK(34104)=128
PEEK(34105)=152
PEEK(34106)=50
PEEK(34107)=38
PEEK(34108)=48
PEEK(34109)=50
PEEK(34110)=38
PEEK(34111)=48
PEEK(34112)=50
PEEK(34113)=0
PEEK(34114)=0
PEEK(34115)=50
PEEK(34116)=114
PEEK(34117)=115
PEEK(34118)=50
PEEK(34119)=114
PEEK(34120)=115
PEEK(34121)=50
PEEK(34122)=96
PEEK(34123)=97
PEEK(34124)=50
PEEK(34125)=76
PEEK(34126)=77
PEEK(34127)=50
PEEK(34128)=76
PEEK(34129)=153
PEEK(34130)=50
PEEK(34131)=76
PEEK(34132)=77
PEEK(34133)=50
PEEK(34134)=76
PEEK(34135)=77
PEEK(34136)=50
PEEK(34137)=76
PEEK(34138)=153
PEEK(34139)=50
PEEK(34140)=91
PEEK(34141)=92
PEEK(34142)=50
PEEK(34143)=86
PEEK(34144)=87
PEEK(34145)=50
PEEK(34146)=51
PEEK(34147)=205
PEEK(34148)=50
PEEK(34149)=51
PEEK(34150)=52
PEEK(34151)=50
PEEK(34152)=51
PEEK(34153)=52
PEEK(34154)=50
PEEK(34155)=51
PEEK(34156)=205
PEEK(34157)=50
PEEK(34158)=64
PEEK(34159)=65
PEEK(34160)=50
PEEK(34161)=102
PEEK(34162)=103
PEEK(34163)=100
PEEK(34164)=102
PEEK(34165)=103
PEEK(34166)=50
PEEK(34167)=114
PEEK(34168)=115
PEEK(34169)=100
PEEK(34170)=76
PEEK(34171)=77
PEEK(34172)=50
PEEK(34173)=86
PEEK(34174)=87
PEEK(34175)=50
PEEK(34176)=128
PEEK(34177)=203
PEEK(34178)=25
PEEK(34179)=128
PEEK(34180)=0
PEEK(34181)=25
PEEK(34182)=128
PEEK(34183)=129
PEEK(34184)=50
PEEK(34185)=128
PEEK(34186)=203
PEEK(34187)=255
end ADDRESS




Secondly A CUT and PASTE STRING  :)

Building Spectrum emulator
Compiling:ZXio.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 11429 bytes.
Compiling:Z80.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 279855 bytes.
Compiling:video.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 5916 bytes.
Compiling:videoMemory.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 9359 bytes.
Compiling:OO.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 143179 bytes.
Compiling:CB.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 107153 bytes.
Compiling:ED.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 39351 bytes.
Compiling:DD.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 46811 bytes.
Compiling:FD.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 55618 bytes.
Compiling:DDCB.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 114656 bytes.
Compiling:FDCB.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 159967 bytes.
Compiling:Spectrum emulator.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 61839 bytes.
Linking:Spectrum emulator.exe
Executing:Spectrum emulator.exe
Start ADDRESS 33902
80,128,129,80,102,103,80,86,87,50,86,87,50,171,203,50,43,51,50,43,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,96,86,50,171,192,50,43,48,50,43,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,192,50,38,48,50,38,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,203,50,38,51,50,38,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,171,50,32,43,50,32,43,50,128,171,50,43,51,50,43,51,50,128,171,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,152,50,32,38,50,32,38,50,128,152,50,38,48,50,38,48,50,0,0,50,114,115,50,114,115,50,96,97,50,76,77,50,76,153,50,76,77,50,76,77,50,76,153,50,91,92,50,86,87,50,51,205,50,51,52,50,51,52,50,51,205,50,64,65,50,102,103,100,102,103,50,114,115,100,76,77,50,86,87,50,128,203,25,128,0,25,128,129,50,128,203,255
End ADDRESS 34187


Looking very forward to hearing this. Thought id post a Screen shot of the ZX emulator.

Kudos if you can make this work!

Kind Regards Baggey
Running a PC that just Aint fast enough!? i7 Quad core 16GB ram 1TB SSD and NVIDIA Quadro K620 . DID Technology stop! Or have we been assimulated!

ZX Spectrum 48k, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip. Im Misunderstood!

Baggey

#31
Hi Si,

Tried running you're routine for checking data but got error's from the output window LOL. Not sure wether it's the version of Blitzmax im using thou?

So i changed it slightly to test the new Data and it tally's now with 285 Bytes. Except the Len(data)=286 Counting ZERO i presume?

Code (Blitzmax) Select
SuperStrict

' Music Data (Blue Danube from Manic Miner)
Global data:Int[] = [80,128,129,80,102,103,80,86,87,50,86,87,50,171,203,50,43,51,50,43,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,96,86,50,171,192,50,43,48,50,43,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,192,50,38,48,50,38,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,203,50,38,51,50,38,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,171,50,32,43,50,32,43,50,128,171,50,43,51,50,43,51,50,128,171,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,152,50,32,38,50,32,38,50,128,152,50,38,48,50,38,48,50,0,0,50,114,115,50,114,115,50,96,97,50,76,77,50,76,153,50,76,77,50,76,77,50,76,153,50,91,92,50,86,87,50,51,205,50,51,52,50,51,52,50,51,205,50,64,65,50,102,103,100,102,103,50,114,115,100,76,77,50,86,87,50,128,203,25,128,0,25,128,129,50,128,203,255]
Print( "DATA LENGTH: "+Len(data) )
Local lenOfdata:Int=Len(data)
Print "Length of Data="+LenOfdata
Print
Print "Press Enter" ; Input

Global cursor:Int = 0
Global duration:Int
Global note:Byte[2]
Global RepeatLoop:Byte=True

ReadMusic()

Print
Print "is the Data Length some how counting 0? ie (0 to 285) = 286"
Print
Print "Address 34187 - Address 33902 = "+(34187-33902)
Print "cursor on next run = "+cursor+"  Byte will be "+data[cursor]

Function ReadMusic()
While RepeatLoop
If data[cursor]=255 Then
Print cursor+") "+data[cursor] ; cursor=0 ; Exit ' Reached the end?
Else
' Read music data
duration = data[cursor]
note[0]  = data[cursor+1]
note[1]  = data[cursor+2]
Print cursor+") "+duration+", "+note[0]+", "+note[1]
cursor :+ 3
End If
Wend
End Function

Rem
19 37596 FN37596 LD A,(IY+0) PLAY MUSIC ' Let A=PEEK(IY+0)
7 37599         CP 255                 ' If (A-255)=0 Then fZERO=TRUE
11 37601         RET Z                  ' If fZERO=TRUE Then Return/Exit/End/Gosub
end rem


Some of the Updating of key press routines may need to be taken into account for the timming of the note playing. If i can help in anyway just let me know.  ;D

Again Hope that help's.

Kind Regards Baggey

Running a PC that just Aint fast enough!? i7 Quad core 16GB ram 1TB SSD and NVIDIA Quadro K620 . DID Technology stop! Or have we been assimulated!

ZX Spectrum 48k, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip. Im Misunderstood!

Scaremonger

Cool, we are getting there.

Because the frequency is not actually passed to the speaker; I have been playing around with how we get the timings for the audio and come up with a way that counts the duration between calls to OUT(254,A) and then generates an audio sample to match the frequency of the calls 

The idea is that your Emulator will be running at the same speed as the ZX Spectrum 48K (3.500MHz), so the "pulses" to the Speaker should match the frequency we need to make the correct sound. The note values in the data appear to have been calculated relative to the Spectrum CPU speed and the TStates of the music code loop which is why the values do not match the desired frequency.

My first run-through made a series of strange crackles, so I have a bit of work to do, but will post when I have ironed out a few bugs.





Baggey

#33
Quote from: Scaremonger on February 10, 2021, 17:17:40
Cool, we are getting there.

Because the frequency is not actually passed to the speaker; I have been playing around with how we get the timings for the audio and come up with a way that counts the duration between calls to OUT(254,A) and then generates an audio sample to match the frequency of the calls 

The idea is that your Emulator will be running at the same speed as the ZX Spectrum 48K (3.500MHz), so the "pulses" to the Speaker should match the frequency we need to make the correct sound. The note values in the data appear to have been calculated relative to the Spectrum CPU speed and the TStates of the music code loop which is why the values do not match the desired frequency.

My first run-through made a series of strange crackles, so I have a bit of work to do, but will post when I have ironed out a few bugs.

Cool. Since this started, ive been brushing up on my Maths. Been Looking into Ampliude/Pressure, Sin functions, Propogation down the line etc. Music theory is more complicated than i imagend. Ive since realised a Duration of sound can have many samples of the wave it's self sort of added together. Even been looking at Audacity to look at The waves in realtime to get a better understanding of what's happening. notes played simultaneously adding together. Which start to go in to Trignometric Functions 1st, 2nd and 3rd Harmonics :o

Thought id stop there.

Kind Regards Baggey
Running a PC that just Aint fast enough!? i7 Quad core 16GB ram 1TB SSD and NVIDIA Quadro K620 . DID Technology stop! Or have we been assimulated!

ZX Spectrum 48k, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip. Im Misunderstood!

iWasAdam

Have a look at QasarBeach
https://adamstrange.itch.io/qasarbeach

It will allow you to visualize any kind of waveform - allow you to draw waveforms, etc and view/hear the results in realtime.

You can also create sounds, load sounds and generally bugger them up to your hearts content.

Sound theory is simple, but doing it in realtime over multiple voices is more of a challenge, but I'll help where you need it ;)

Baggey

#35
Quote from: iWasAdam on February 10, 2021, 19:53:28
Have a look at QasarBeach
https://adamstrange.itch.io/qasarbeach

It will allow you to visualize any kind of waveform - allow you to draw waveforms, etc and view/hear the results in realtime.

You can also create sounds, load sounds and generally bugger them up to your hearts content.

Sound theory is simple, but doing it in realtime over multiple voices is more of a challenge, but I'll help where you need it ;)

Hi Adam,
Off Topic slightly! Tried Quasr Beach theres alot to go through. Love the retro feel. I need more time to read through and work out how to play something.  :-[

But Ohh my god! Ive just come across Viva Mortis. Absolutley Superb.  8)

I love Retro Stuff Almost reminds me of TikiTaka. By the way whats MX2. Ahh Just realised Monkey2.

Kind Regards Baggey

Running a PC that just Aint fast enough!? i7 Quad core 16GB ram 1TB SSD and NVIDIA Quadro K620 . DID Technology stop! Or have we been assimulated!

ZX Spectrum 48k, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip. Im Misunderstood!

Scaremonger

#36
Quote from: iWasAdam on February 03, 2021, 08:44:23
the simple way is to create a single sample say 128frames in length
fill this with a square wave (< 64 sample=-1 > 64 sample =1)
play this as a sound with a loop - thats your base

play the sound through a channel to allow for direct manipulation

use pitch, pan, vol to do all the other stuff <-these are channel controls

Spectrum only used square waves

Using your idea above, I have put together this little bit of code to create a small Square Wave and allow you to turn it on/off (SPACE) and change the pitch (UP/DOWN).

buzzer-1.bmx

SuperStrict

' Simple Square Wave Player

' Create a Buzzer
AppTitle = "BUZZER.1"
Global buzzer:TChannel = CreateBuzzer()

' Create one "wave" of the supplied frequency
Function CreateBuzzer:TChannel()
Const amplitude:Float= 1.0 ' Volume 0.0 to 1.0
Const frequency:Int  = 261.63 'Hz = C4 = Middle-C
Const samplerate:Int = 44000
Local sample:TAudioSample = CreateAudioSample( frequency, 44000, SF_MONO8 )
Print( "Sample length "+sample.length )
Local half:Float = frequency/2.0
For Local n:Int = 0 Until sample.length
Local level:Float = n Mod frequency
If level<half
sample.samples[n] =  amplitude
Else
sample.samples[n] = -amplitude
EndIf
Next
' Cue a buzz sound ready to be played
Return CueSound( LoadSound( sample, True ) )
End Function

Local state:Int = False, rate:Float = 1.0
Local text:String

Graphics 200,50
Repeat
Cls
text = "SPACE = ON/OFF"
DrawText( text,(GraphicsWidth()-TextWidth(text))/2,(GraphicsHeight()-TextHeight(text))/3 )
text = "UP/DOWN = PITCH"
DrawText( text,(GraphicsWidth()-TextWidth(text))/2,(GraphicsHeight()-TextHeight(text))/3*2 )
If KeyHit( KEY_SPACE )
state = Not state
If state
buzzer.setpaused( False )
Else
buzzer.setpaused( True )
EndIf
EndIf
If KeyHit( KEY_UP )
rate :+ 0.1
buzzer.setrate( rate )
EndIf
If KeyHit( KEY_DOWN)
rate :- 0.1
buzzer.setrate( rate )
EndIf
Flip
Until KeyHit( KEY_ESCAPE )


I have no idea what sample rate to use for the spectrum but I decided to start with Middle C (C4, 261.63 Hz) for the frequency.

I guess the sample rate should probably be relative to the speed of the Spectrum CPU (3500000 Hz) but until I figure out what to use, I stuck 44000 in there.

Next step is to use the Wave and an oscillating function (similar to how the Manic Miner turns the speaker on and off repeatedly) to see if I can reproduce different notes.

Baggey

#37
Hi Si,

Ive been messing with this. I couldn't get the Function to work however! I see you've passed it as a Tchannel. Shall have a play later with that idea. Functions and pointers arn't my strong point :))


' CreateSquareWave

'SuperStrict

Graphics 640,480

' Higher the Duration the BASSier Tone is
Global Duration:Byte=128 ' Or total samples per One Cycle

' Max Frequency for Ear so dosent need to be higher!?
' However the Lower it is the duller or Bassier sound we get.
Const SamplesPerSecond:Float=22000
'Global Hertz:Float=HumanEarMAX

Global AudioSample:TAudioSample=CreateAudioSample( duration, SamplesPerSecond, SF_MONO8 )

Function OUT_Piezo_(Duration:Byte, Hertz:Short)

AudioSample:TAudioSample=CreateAudioSample( Length, SamplesPerSecond, SF_MONO8 )

For Local K:Float=0 Until Duration
' ZX Spectrum could only play a Square Wave
' So a Work around For a very crude Square Wave!
If K<(Duration/2) Then AudioSample.samples[K] = 1 Else AudioSample.samples[K] = 0
Next

End Function

Global audio:TSound=LoadSound( AudioSample, True )
channel=CueSound(audio)

While Not (AppTerminate() Or KeyDown(key_space))


If MouseHit(1) PlaySound audio,channel


Local Pan# = MouseX () / (GraphicsWidth () / 2.0)
Local Vol# = 1 - (MouseY () *.0023)
Local Rate#=Pan#

' Not needed for Specy, As it was always MONO! But Pan#=0 for CENTER
'Pan#=0 ;

SetChannelPan channel,0

' Turning Piezo ON/OFF  Vol=0 for OFF and Vol=1 for ON
SetChannelVolume channel,Vol#

SetChannelRate channel,rate#

'StopChannel channel
'Audiosample.length=Duration
'Audiosample.hertz=hertz
'Hertz=Rnd (0.0, 44000)
'OUT_Piezo_(Duration,Hertz)


Cls
DrawText "Left Click to Play", 240, 200
DrawText "Pan   : " + pan, 240, 220
DrawText "Volume: " + vol, 240, 240

Flip

Wend


If you wobble the mouse around and up/down it sounds like galaxians and pleiads even Pacman dying. LOL  :))

I think im out of my depth here! But hey it's Great Fun  :)

Kind Regards Baggey
Running a PC that just Aint fast enough!? i7 Quad core 16GB ram 1TB SSD and NVIDIA Quadro K620 . DID Technology stop! Or have we been assimulated!

ZX Spectrum 48k, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip. Im Misunderstood!

Midimaster

There are a lot of bugs in your code....
All  values are INTEGER!!!

To create a Square-Signal the both values are 0 and 255!!!
128 is the Zero-Signal
255 is full positiv +127
0 is full negativ   -128

The Hertz is the Frequency to alter from 0 to 255
Code (BlitzMax) Select

Global Duration:INT=1000
Const SamplesPerSecond:INT=11025

Global AudioSample:TAudioSample=CreateAudioSample( duration, SamplesPerSecond, SF_MONO8 )
OUT_Piezo_(Duration,80)

Function OUT_Piezo_(Duration%, Hertz%)
Local w%
For Local K%=0 Until Duration-1 
w=w+1
If (w Mod Hertz)< Hertz/2
AudioSample.samples[K] = 255
Else
AudioSample.samples[K] = 0
EndIf
Next
End Function

Global audio:TSound=LoadSound( AudioSample)
While Not (AppTerminate() Or KeyDown(key_space))


If MouseHit(1)
PlaySound audio
EndIf


this code is only for first tests and pointing you in a new direction....
...on the way to Egypt

Scaremonger

#39
The ZX Spectrum code sends ON / OFF pulses to the Buzzer, but not the frequency or duration.

I have timed and counted calls to OUT(254,A) and plan to use this information to create the square wave; however I was getting values less than a millisecond and BlitzMax doesn't have a more accurate function, so I've written one in C.

microsecs.c

#include "time.h"
#include <sys/time.h>

long MicroSecs(void) {
struct timeval tv;
gettimeofday( &tv, NULL );
return (( (long)tv.tv_sec )*1000000 )+( tv.tv_usec );
}


To use it, simply add this to the top of your Blitzmax code:


Import "microsecs.c"
Extern
Function MicroSecs:Long()
EndExtern


I may add another version that returns a double at a later time, but this will do for now.

So far, my OUT() emulator looks like this:


Function _OUT( port:Byte, value:Byte)
Global savestate:Int=False
Global savetime:Long, counter:Int

If port=254
Local speaker:Int = (value&$10)
counter :+ 1
If speaker = savestate Return ' State has not changed
Local now:Long = MicroSecs()
If speaker ' Speaker turned ON, play last wave
Local duration:Long = now-savetime
duration = Min(duration,500) ' Allow for clock creep
'Print( "- duration: "+duration+" us" )
Print( "CREATE SAMPLE "+counter+", "+ duration+"us")
savetime = now
counter = 0
End If
savestate = speaker
End If
End Function


I've not thoroughly tested it yet, but I'll have another play when I get time.

Si...

Scaremonger

Quote from: Midimaster on February 11, 2021, 19:43:08
All  values are INTEGER!!!
To create a Square-Signal the both values are 0 and 255!!!
128 is the Zero-Signal
255 is full positiv +127
0 is full negativ   -128

I was using amplitude of -1.0 to +1.0 in my code and it was probably the cause of the weird clicks that I got as a result. Thanks for clearing that one up @midimaster.

Quote from: Midimaster on February 11, 2021, 19:43:08
The Hertz is the Frequency to alter from 0 to 255

Interesting... So a middle C at 261.63 Hz would need to be converted in some way?

Scaremonger

This is my conversion so far...

It makes a sound and there seems to be something deep in the noise, but its still got a serious problem in there somewhere.

I would appreciate if any of you guys can spot anything.

(You will need "millisecs.c" that I posted in an earlier message to run this).


SuperStrict
' Manic Miner Audio Output

Import "microsecs.c"
Extern
Function MicroSecs:Long()
EndExtern

' ZX Spectrum 16K/48K = 3.500 Mhz
' ZX Spectrum 128K    = 3.547 Mhz
Const CLOCKSPEED:Float = 3.500 'Mhz
Const __CPUCYCLE__:Float = (1.0/(CLOCKSPEED)) 'in Microseconds
Print( "1 Cycle   = "+(__CPUCYCLE__)+" microseconds")
Print( "108 Cycles= "+(__CPUCYCLE__*108)+" microseconds")

' 16 Bit Regsiters
Global BC16:Short
Global DE16:Short
Global HL16:Short
Global BC:Byte Ptr = Varptr BC16
Global DE:Byte Ptr = Varptr DE16
Global HL:Byte Ptr = Varptr HL16
Const B:Byte=1, C:Byte=0, D:Byte=1, E:Byte=0, H:Byte=1, L:Byte=0

' 8 Bit Registers
Global A:Byte
Global IY:Byte, IX:Byte

' FLAGS
Global F:Byte[8]
Const CF:Byte=0
Const ZR:Byte=1

' Music Data
Global data:Byte[] = [80,128,129,80,102,103,80,86,87,50,86,87,50,171,203,50,43,51,50,43,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,96,86,50,171,192,50,43,48,50,43,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,192,50,38,48,50,38,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,203,50,38,51,50,38,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,171,50,32,43,50,32,43,50,128,171,50,43,51,50,43,51,50,128,171,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,152,50,32,38,50,32,38,50,128,152,50,38,48,50,38,48,50,0,0,50,114,115,50,114,115,50,96,97,50,76,77,50,76,153,50,76,77,50,76,77,50,76,153,50,91,92,50,86,87,50,51,205,50,51,52,50,51,52,50,51,205,50,64,65,50,102,103,100,102,103,50,114,115,100,76,77,50,86,87,50,128,203,25,128,0,25,128,129,50,128,203,255]

' Direct Code Conversion
Function CALL_37596:Int()
Local __TSTATES__:Int

While True
A = data[IY] '37596 LD A,(IY+0) Pick up the next byte of tune data from the table at 33902
If A=255 '37599 CP 255 Has the tune finished?
Return True '37601 RET Z Return (with the zero flag set) if so
End If
BC[C] = A '37602 LD C,A Copy the first byte of data for this note (which determines the duration) to C
BC[B] = 0 '37603 LD B,0 Initialise B, which will be used as a delay counter in the note-producing loop
A = 0 '37605 XOR A Set A=0 (for no apparent reasaon)
DE[D] = data[IY+1] '37606 LD D,(IY+1) Pick up the second byte of data for this note
'##### UPDATE AN ONSCREEN KEYBOARD TO SHOW NOTE
'A = DE[D] '37609 LD A,D Copy it to A
'CALL_37675() '37610 CALL 37675 Calculate the attribute file address for the corresponding piano key
'mem(HL,80) '37613 LD (HL),80 Set the attribute byte for the piano key to 80 (INK 0: PAPER 2: BRIGHT 1)
DE[E] = data[IY+2] '37615 LD E,(IY+2) Pick up the third byte of data for this note
'##### UPDATE AN ONSCREEN KEYBOARD TO SHOW NOTE
'A = DE[E] '37618 LD A,E Copy it to A
'CALL_37675() '37619 CALL 37675 Calculate the attribute file address for the corresponding piano key
'mem(HL,40) '37622 LD (HL),40 Set the attribute byte for the piano key to 40 (INK 0: PAPER 5: BRIGHT 0)
'Print( "A ---"+Right(Hex(A),2)+"  BC-"+Right(Hex(BC16),4)+"  DE-"+Right(Hex(DE16),4)+"  HL-"+Right(Hex(HL16),4) )
Repeat ' until C=0 (at 37645)
Repeat ' until B=0 (at 37642)
_OUT(254,A) '37624 OUT (254),A Produce a sound based on the frequency parameters in the second and third bytes of data for this note (copied into D and E)
__TSTATES__ = 0
DE[D] :- 1 '37626 DEC D
If DE[D]=0 '37627 JR NZ,37634
DE[D] = data[IY+1] '37629 LD D,(IY+1)
A = A~24 '37632 XOR 24
__TSTATES__ :+ 26
End If
DE[E] :- 1 '37634 DEC E
If DE[E]=0 '37635 JR NZ,37642
DE[E] = data[IY+2] '37637 LD E,(IY+2)
A = A~24 '37640 XOR 24
__TSTATES__ :+ 26
End If
BC[B] :- 1 '37642 DJNZ 37624

' SLOW DOWN BLITZMAX SO CODE RUNS AT SAME SPEED AS ZX SPECTRUM
__TSTATES__ :+ 56
'Print(microsecs())
Local wait:Long = MicroSecs()+Long(__TSTATES__*__CPUCYCLE__)
'Print("wait "+wait)
'Print("tstates "+__TSTATES__)
While wait>MicroSecs() ; Wend

'Print( "A ---"+Right(Hex(A),2)+"  BC-"+Right(Hex(BC16),4)+"  DE-"+Right(Hex(DE16),4)+"  HL-"+Right(Hex(HL16),4) )
Until BC[B]=0 ' ""    ""
BC[C] :- 1 '37644 DEC C



Until C=0 '37645 JR NZ,37624
'##### KEYBOARD INTERACTION
'If CALL_37687() '37647 CALL 37687 Check whether ENTER or the fire button is being pressed
' Return False '37650 RET NZ Return (with the zero flag reset) if it is
'End If
'##### UPDATE AN ONSCREEN KEYBOARD TO SHOW NOTE
'A = data[IY+1] '37651 LD A,(IY+1) Pick up the second byte of data for this note
'CALL_37675() '37654 CALL 37675 Calculate the attribute file address for the corresponding piano key
'mem(HL,56) '37657 LD (HL),56 Set the attribute byte for the piano key back to 56 (INK 0: PAPER 7: BRIGHT 0)
'##### UPDATE AN ONSCREEN KEYBOARD TO SHOW NOTE
'A = data[IY+2] '37659 LD A,(IY+2) Pick up the third byte of data for this note
'CALL_37675() '37662 CALL 37675 Calculate the attribute file address for the corresponding piano key
'mem(HL,56) '37665 LD (HL),56 Set the attribute byte for the piano key back to 56 (INK 0: PAPER 7: BRIGHT 0)
IY :+ 3 '37667 INC IY Move IY along to the data for the next note in the tune
'37669 INC IY
'37671 INC IY
Wend '37673 JR 37596 Jump back to play the next note
End Function

' Direct Code Conversion
' Calculates which on-screen piano key will be shown pressed
' We dont need this, so it has been commented out
'Function CALL_37675()
' A :- 8 '37675 SUB 8 Compute the piano key index (K) based on the frequency parameter (F), and store it in bits 0-4 of A: K=31-INT((F-8)/8)
' _RRCA() '37677 RRCA
' _RRCA() '37678 RRCA
' _RRCA() '37679 RRCA
' A  = ~A '37680 CPL
' A :| 224 '37681 OR 224 A=224+K; this is the LSB
' HL[L] = A '37683 LD L,A Set HL to the attribute file address for the piano key
' HL[H] = 89 '37684 LD H,89
' Return '37686 RET
'End Function

' Keyboard input
' Not required, so has been commented out
'Function CALL_37687:Int()
' Return False
'End Function

' Z80 Mnumonic Rotate Bits Right with Carry
'Function _RRCA()
' Local x:Byte = A & $01
' A = ( A Shr 1 | ( A & $01 ) Shl 7 )
' F[CF] = x
'End Function

' Z80 Mnumonic


' bitmap of value is xxxSMBBB
' S=Sound M=Microphone B=Border

Function _OUT( port:Byte, value:Byte)
Global savestate:Int=False
Global savetime:Long, counter:Int

If port=254
Local speaker:Int = (value&$10)
counter :+ 1
If speaker = savestate Return ' State has not changed
Local now:Long = MicroSecs()
If speaker ' Speaker turned ON, play last wave
Local duration:Int = now-savetime
duration = Min(duration,500)*10
'Print( "- duration: "+duration+" us" )
Print( "CREATE SAMPLE "+counter+" Hz, "+ duration)
Buzz( duration, counter )
savetime = now
counter = 0
End If
savestate = speaker
End If
End Function

' Create one "wave" of the supplied frequency
Function Buzz:TChannel( duration:Int, frequency:Int )
'Const amplitude:Float= 1.0 ' Volume 0.0 to 1.0
'Const frequency:Int  = 261.63 'Hz = C4 = Middle-C
Const SAMPLERATE:Int = 11025

Local sample:TAudioSample = CreateAudioSample( duration, SAMPLERATE, SF_MONO8 )
Local half:Int = frequency/2
Local w%
For Local n:Int=0 Until sample.length -1
w=w+1         
If (w Mod frequency)< half
sample.samples[n] = 255
Else
sample.samples[n] = 0
EndIf
Next
Local audio:TSound = LoadSound( sample )
PlaySound( audio )

End Function

' Play Music
Print( "Starting" )
IY = 0 ' Set pointer to start of music
CALL_37596()

Midimaster

#42
If you feed the function with real frequencies you need to inverse them to be able to manipulate pulse lengths.

Dont know the correct factor, but this shows the way (yellow line):


....
Function Buzz:TChannel( duration:Int, frequency:Int )
   Const SAMPLERATE:Int = 11025
        frequency=11025/frequency
   Local sample:TAudioSample = CreateAudioSample( duration, SAMPLERATE, SF_MONO8 )
   Local half:Int = frequency/2
   For Local n:Int=0 Until sample.length -1   
               If (n Mod frequency)< half
                    sample.samples[n] = 255
               Else
                     sample.samples[n] =0
               Endif
   Next
   ....
...on the way to Egypt

Baggey

Quote from: Scaremonger on February 11, 2021, 22:02:49

To use it, simply add this to the top of your Blitzmax code:

microsecs.c

#include "time.h"
#include <sys/time.h>

long MicroSecs(void) {
struct timeval tv;
gettimeofday( &tv, NULL );
return (( (long)tv.tv_sec )*1000000 )+( tv.tv_usec );
}




Import "microsecs.c"
Extern
Function MicroSecs:Long()
EndExtern


Hi Si, Not interfaced with C before. Ive worked out you use the C snipet of your code and save as microsecs.c file. Ive saved it the local directory and in BlitzMax tmp folder.
Ive then used the 2nd piece of code to import "microsecs.C". But when i compile i get the following.

Building Import microsecs
Compiling:microsecs.c
Build Error: failed to compile C:/Spectrum7/Sound Stuff/microsecs.c
Process complete


I think i need to do something else but i dunno what.  :-[

Kind Regards Baggey
Running a PC that just Aint fast enough!? i7 Quad core 16GB ram 1TB SSD and NVIDIA Quadro K620 . DID Technology stop! Or have we been assimulated!

ZX Spectrum 48k, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip. Im Misunderstood!

Baggey

Just wondering if anyone else is following this and is interested in Emulation or Creating Sound. He or She may be interested in some of the math.

The Z80 runs at 3.5Mhz and to emulate it and produce sound on the fly so to speak. Timming is crucial.

So ive added a little info program so others can see how to interpret, how to calculate the timming of each Instruction according to it's Tstates.
sometimes, something that seems simple in principle can get bogged down in the math. Just Trying to create a sound on the fly or even white noise in BlitzMax dosent seem to be that easy?

Thanks to everyone who's having ago.

All ideas welcome  ;D


Const ClockSpeedDOUBLE:Double = 3.5*10^6 ' ie, 3500000  or ( 3.5 EXP 6 Calculator )
Const ClockSpeedFLOAT:Float   = 3.5*10^6 ' ie, 3500000  or ( 3.5 EXP 6 Calculator )
Const ClockSpeed=3500000
Const Z80_CPU_Cycle:Float=0.000000285 ' Cycles per second. Most instructions take a minimum of 4 TStates
Global OpCode_Time:Float

Print
Print "ClockSpeed as a DOUBLE="+ClockSpeedDOUBLE
Print
Print " Or"
Print
Print "ClockSpeed as a  FLOAT="+ClockSpeedFLOAT
Print
Print
Print "BLITZ MAX Dosen't make the numbers nice!"
Print
Print " Clock Speed is = "+ClockSpeed+" Hz ... Maybe more familiar as 3,500,000 Hz or 3.5 Mhz"
Print
Print " Which is 3.5*10^6 or ( 3.5 EXP 6 Calculator )"
Print
Print
Print " So, One Z80 CPU takes 1/3.5*10^6 or ( 1/3.5 EXP 6 Calulator )"
Print "ie,"
Print " One CPU cycle = 0.000000285 Secs ... 0.285 uSecs or micro Seconds ... 0.285*10^(-6) or ( 0.285 EXP -6 Calulator )"
Print "                                     Or 285 nSecs or nano Seconds  ... 285*10^(-9)   or (  285 EXP -6 Calculator )"
Print
Print "So, Blitzmax notation "
Print
Const __CPUCYCLE__:Float = (1/ClockSpeedFloat)
Print " 1 CPU cycle   = "+(__CPUCYCLE__)+"  Which as i say isn't nice!"
Print
Print "or, Z80_CPU_Cycle = 0.000000285"
Print
Print "Therefore,"
Print
Print "108 Cycles= "+(__CPUCYCLE__*108)
Print
Print "Which is 0.000030857 Secs ... or 30.857uSecs micro Seconds ... 30.857*10^(-6) or ( 30.857 EXP -6 Calculator )"
Print
Print "Consider a NOP Instruction 4 Tstates"
Print
Print "4 Cycles= "+(__CPUCYCLE__*4)+" microseconds"
Print
Print "Which is 0.000001142 Secs ... or 1.142uSecs micro Seconds ... 1.142*10^(-6) or ( 1.142 EXP -6 Calculator )"
Print
Print "Anyone wishing to work out how many Seconds a TCycle takes, or instruction can take."
Print "We can use the following Formulae"
Print
Print " Tstates of Instruction * 1 / 3.5*10^6 = Time Taken in Seconds"
Print
Print "ie,  Our NOP Instruction has   4 * 1 / 3.5*10^6 = Time Taken in Seconds   or   BlitzMax Time Taken = 4*(1/(3.5*10^6))"
Print
Print "Time for NOP OpCode = "+4*(1/(3.5*10^6))+" ... Which is 1.142*10^6 Seconds"
Print
Print "Incidently the Brackets are there forcing a Mathamatical rule called BIDMAS. To make BlitzMax Enforce this RULE"
Print "the Brackets are there. Meaning anything representing the letter is done first! or within the Brackets"
Print "Otherwise are Sum is going to be wrong!"
Print
Print " B = Brackets"
Print " I = Indicies"
Print " D = Division"
Print " M = Multiplication"
Print " A = Addition"
Print " S = Subtraction"
Print "                                Time Taken to execute = Tstates for Instruction *  (1/(3.5*10^6))"


Kind Regards Baggey
Running a PC that just Aint fast enough!? i7 Quad core 16GB ram 1TB SSD and NVIDIA Quadro K620 . DID Technology stop! Or have we been assimulated!

ZX Spectrum 48k, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip. Im Misunderstood!