Ooops
October 18, 2021, 00:18:01

Author Topic: Creating ZX SOUND  (Read 6860 times)

Offline Baggey

  • Full Member
  • ***
  • Posts: 182
Re: Creating ZX SOUND
« Reply #60 on: February 18, 2021, 20:16:46 »
WOW, Just had a listen  :o

How have you arrived at 750,000 is there a mathimatical explanation.

Kind Regards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #61 on: February 19, 2021, 00:49:22 »
If you want to write code it is always a good way not to write the complete code idea and then start testing. Better is to work step by step down into the problem and test each step before diving deeper into the final code.

In your case this means first to understand the DATA[] field and write a BlitzMax Code which  produces the song as expected. Then you know, what the goal is. And you can verify, that the DATA[] field really contains good datas. Here I recognized that your example ("Jig") is valid. In this step I did not define BYTE variables, but INTEGERs.

The Data[] field of "Danube" has 286 Byte which means 95 tones + one STOP command. Each of this tone make a variable C count down to 0. Depending on the tone C varies between 50-80 representing the length of the tone. Inside each of this 50-80 loops there is another inner loop, where B runs from 255 downto 0. In total this can be upto 1.938.000 samples.
95*80*255= 1.938.000

to investigate the total number of samples you let the code run without doing anything with audio, but only count the calls of the OUT-function. In my test code with B=max 150 the result was 736.543. It is always a good idea to insert a lot of PRINT command, so you can follow, what happens and what is the state of all this loops.

Already at this moment I recognized that Scaremongers code was infinite and never reached the song end. Defining IY as Byte allows not a song length>255.

Now, when you know that you will need 736.543 samples you create a Byte Array that will be big enough to contain all these loops. During testing it is never a good idea to create an array of 736.543 bytes if you will need this number. Think big! Take 750.000 (or 2.000.000!!). Later you can state it more precisely.

The target is always not to produce additional bugs, because of beeing too stingy.

If you have been able to get running your problem in a more tolerant "surrounding", you can now dive deeper and modify your code towards the real target. But you will see, that these steps are now very easy and fast to reach. One of this steps is to check whether all variables can be changed to BYTE. Next step can be to simulate this 16bit registers. and so on...

f.e. the step to use Chunks instead of one big TAudioSample needed only 5 min after knowing that the prior code was already working. At the end you will code faster if you divide your problem in more little stages.





« Last Edit: February 19, 2021, 00:56:09 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Scaremonger

  • Full Member
  • ***
  • Posts: 233
    • ITSpeedway - Ramblings of a geek!
Re: Creating ZX SOUND
« Reply #62 on: February 20, 2021, 10:37:50 »
@Midimaster: Nice one. I like the approach to build up the sound and then play it in chunks.

Offline Baggey

  • Full Member
  • ***
  • Posts: 182
Re: Creating ZX SOUND
« Reply #63 on: February 20, 2021, 16:35:31 »

The next step would be now to cut into single TAudioSample chunks of 100msecs. It is reacting faster but you will get more artifacts:
Code: BlitzMax
  1. ...
  2. 'workaround: now you can set B back to  255 (or remove this code line)
  3.         B=255
  4.         Repeat                                  ' until B=0 (at 37642)                         
  5.         OUT_CHUNKS(254,A)                       '37624  OUT (254),A    
  6. ...
  7.  
  8. Function OUT_CHUNKS(Port%, Value%)
  9.         Const SAMPLE_RATE%=44100
  10.         Global NextSample:TAudioSample, PlayTime%
  11.         If Counter=0
  12.                 Print "Sample created"
  13.                 NextSample = CreateAudioSample(4410, SAMPLE_RATE, SF_MONO8 )
  14.         EndIf
  15.  
  16.         Select Value & 8
  17.                 Case 0
  18.                         NextSample.Samples[Counter]=50 
  19.                 Case 8
  20.                         NextSample.Samples[Counter]=200
  21.         End Select
  22.         Counter=Counter + 1
  23.         If counter=4408
  24.                 NextSample.Samples[4408]=0     
  25.                 NextSample.Samples[4409]=0     
  26.                 Local Audio:TSound = LoadSound( NextSample )
  27.                 Repeat
  28.                         Delay 1
  29.                 Until PlayTime<MilliSecs()
  30.                 Print "play TSound"
  31.                 PlayTime=MilliSecs()+100
  32.                 PlaySound Audio
  33.                 Counter=0
  34.         EndIf
  35. End Function
  36.  
  37.  

WOW! We are really getting there  8)

Haven't had enough time to review everything! But had a good day coding and Playing with your routines. The closest ive got is, to using your OUT_CHUNKS Code successfully. Within my Emulator "SpecBlitz". Thanks to MIDI MASTER  ;D

Love the idea of the Sound Chip Type. But the Scretchy sound of Manic Miner just isnt there thou. Ive played with some off the values and got Bassier sounds and Higher Pitched sounds as well. the saw wave example is an excellent twist to the Zx Spectrum. It's almost like using Band Pass filters. Not sure how to alter the Sound Chip type to achieve the scretchy Sound of Manic Miner Thou.

So ive created a mini ZX Emulator to use your OUT_CHUNKS routine. This Routine is Directly Runable In BlitzMax 1.5 and here it is :-

Code: BlitzMax
  1. ' ManicMiner Player Using BlitzMax 1.5
  2.  
  3. SuperStrict
  4.  
  5. Graphics 800,600
  6.  
  7. Global RunSpeed:Int=16 ' Using Blitzmax 1.5 Gives roughly a 50Hz Frame Delay!
  8.  
  9. ' Registers 16 Bit
  10. Global IY:Short, BC:Short, DE:Short, HL:Short
  11.  
  12. ' Registers 8 Bit
  13. Global A:Byte, B:Byte, C:Byte, D:Byte, E:Byte, H:Byte, L:Byte
  14.  
  15. ' Control Registers
  16. Global TCycles:Int
  17. Global fZERO:Byte
  18. Global ENTER:Byte=0
  19. Global Counter:Int
  20.  
  21. ' Music Data
  22. Global MusicData: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]
  23. Print
  24. Print "Length of MusicData = "+(Len MusicData)
  25.  
  26. Init()
  27.  
  28. Function MainLoop()
  29.     Local starttime:Int = MilliSecs()
  30.        
  31.     While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)
  32.        
  33.                                 Run()
  34.         Local endtime:Int = MilliSecs()
  35.         Local diff:Int = endtime - starttime
  36.        
  37.         Local pausedelay:Int = RunSPEED ' About 50Hz ie one TV frame
  38.                        
  39.         If pausedelay > 0 Then
  40.             Delay(pausedelay)
  41.         Else
  42.             pausedelay = 0
  43.         End If
  44.         starttime = endtime+pausedelay
  45.     Wend
  46. End Function
  47.  
  48. Function Call_37596:Byte()
  49.  
  50.                 While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)
  51.  
  52.                 ' LD A,(IY+0)           '  Get next Byte of Tune Data from 33902
  53.                                 A=MusicData[IY]  ; TCycles:+19
  54.                 ' CP 255                                '  Has the tune finished?
  55.                                 If (A-255)=0 Then fZERO=1 Else fZERO=0 ; TCycles:+7
  56.                 ' RET Z         '        Return if ZERO flag is Set
  57.                                 If fZERO=1 Then
  58.                                                 TCycles:+11 ; Return fZERO
  59.                                         Else
  60.                                                 TCycles:+5
  61.                                 End If
  62.                 '       LD C,A                                          ' Copy the first byte of MusicData for this note (which determines the duration) into C
  63.                                 C=A ; BC=(B Shl 8)+C ; TCycles:+4
  64.                 ' LD B,0                                                ' Load B with 0, which will be used as a delay counter in the note-producing loop              
  65.                                 B=0 ; BC=(B Shl 8)+C ; TCycles:+7      
  66.                 ' XOR A                         ' XOR A  Equivalent to LOAD A with 0 ( Incerting a delay of 4 TCycles )
  67.                                 A=(A ~ A) ; TCycles:+4
  68.                 ' LD D,(IY+1)                           ' Get second byte of MusicData for this note
  69.                                 D=MusicData[IY+1] ; DE=(D Shl 8)+E ; TCycles:+19
  70.                 ' LD A,D                                                ' Load A with D
  71.                                 A=D ; TCycles:+4
  72.                 '              
  73.                 ' FIRST CALL
  74.                 '
  75.                 ' CALL 37675                            ' Update on screen piano Key
  76.                                 TCycles:+17             ' 17 Clock Cycles for the call
  77.                                 ' Code there is irelevant, but time must br acounted for!
  78.                 '       LD (HL),80                        ' Set the Atribute colour of piano key 80 (INK 0: PAPER 2: BRIGHT 1)
  79.                                 TCycles:+10             ' 10 Clock Cycles for the LD(HL),40
  80.                 '       LD E,(IY+2)                             ' Get Third byte of MusicData for this note
  81.                                 E=MusicData[IY+2] ; D=(D Shl 8)+E ; TCycles:+19
  82.                 ' LD A,E                                        '       Load A with E
  83.                                 A=E ; TCycles:+4
  84.                 '
  85.                 ' SECOND CALL
  86.                 '
  87.                 '       CALL 37675                              ' Note A is returned with the value for the OUT(254),A! Also Update's on screen piano Key
  88.                                 A=Call_37675()  ' A IS RETURNED ALTERD CRUCIAL FOR THE VALUE OF A!
  89.                                 TCycles:+17             ' 17 Clock Cycles for the call
  90.                                 ' Code there is irelevant, but time must br acounted for!
  91.                 ' LD (HL),40
  92.                                 TCycles:+10             ' 10 Clock Cycles for the LD(HL),40
  93.                 Repeat ' C LOOP until C=0
  94.                         ' Part of the DJNZ
  95.                                 Repeat ' B LOOP until B=0
  96.                                 'For Local DJNZ:Int=B To 0 Step -1 ' until B=0
  97.                                                 'Print "B="+B
  98.                                                 ' OUT (254),A           ' Turn Piezo on or off
  99.                                                         OUT_CHUNKS(254,A) ; TCycles:+11
  100.                                                 ' DEC D                                 ' Decrease D by One
  101.                                                         D:-1 ; DE=(D Shl 8)+E ; TCycles:+4
  102.                                                         If D=0 Then fZERO=1 Else fZERO=0 ' If D=0 then set Zero flag
  103.                                                 ' JR NZ,37634
  104.                                                         If fZERO=1 Then
  105.                                                                         ' LD D,(IY+1)
  106.                                                                                 D = MusicData[IY+1]     ; DE=(D Shl 8)+E ; TCycles:+19
  107.                                                                         ' XOR 24
  108.                                                                                 A = (A ~ 24)                                                                   
  109.                                                                                 TCycles:+7 ' Condition not met
  110.                                                                 Else
  111.                                                                                 TCycles:+12 ' Condition met
  112.                                                         End If
  113.                                                 '       DEC E
  114.                                                         E:-1 ; DE=(D Shl 8)+E ; TCycles:+4
  115.                                                         If E=0 Then fZERO=1 Else fZERO=0 ' If E=0 then set Zero flag
  116.                                                 ' JR NZ,37642
  117.                                                         If fZERO=1 Then
  118.                                                                         ' LD E,(IY+1)
  119.                                                                                 E = MusicData[IY+2] ; TCycles:+19
  120.                                                                         ' XOR 24
  121.                                                                                 A = (A ~ 24)
  122.                                                                                 TCycles:+7 ' Condition not met
  123.                                                                 Else
  124.                                                                                 TCycles:+12 ' Condition met
  125.                                                         End If
  126.                                                 ' DJNZ
  127.                                                                 'DisJump=(twosum[PC1]+2) ; B:-1 ; BC=(B Shl 8)+C
  128.                                                                 If B<>0 Then  
  129.                                                                                 TCycles:+13 ' Condition met
  130.                                                                         Else
  131.                                                                                 TCycles:+8 ' Condition not met
  132.                                                                 End If
  133.                                                                 B:-1 ; BC=(B Shl 8)+C
  134.                                                 '
  135.                                 Until (B=0) ' B=0
  136.                                 ' DEC C ' Decrease C by one
  137.                                         C:-1 ; BC=(B Shl 8)+C ; TCycles:+4
  138.                                         If C=0 Then fZERO=1 Else fZERO=0 ' If E=0 then set Zero flag
  139.                         Until (fZERO=1)
  140.                 ' Check KEYBOARD and JOYSTICK
  141.                         CALL_37687() ; TCycles:+17      ' Check wether Enter or Fire is being pressed Code ignored but timing needed!
  142.                 '       RET NZ                                                          '        Return if ZERO flag is ReSet, if it is
  143.                                 If fZERO=0 Then
  144.                                                 TCycles:+11 ; Return fZERO
  145.                                         Else
  146.                                                 TCycles:+5
  147.                                 End If
  148.                 ' Pick up Keys AGAIN! This time we are Painting the keys WHITE Updating them
  149.                 ' Code is irrelevant but the timming is!
  150.                 ' LD A,(IY+1)           '  Get 2nd Byte of Tune Data from 33902
  151.                         A=MusicData[IY+1]  ; TCycles:+19
  152.                 '       CALL 37675                              ' Note A is returned with the value for the OUT(254),A! Also Update's on screen piano Key
  153.                         A=Call_37675()  ' A IS RETURNED ALTERD CRUCIAL FOR THE VALUE OF A!
  154.                         TCycles:+17             ' 17 Clock Cycles for the call
  155.                         ' Code there is irelevant, but time must be acounted for!
  156.                 ' LD (HL),56
  157.                                 TCycles:+10             ' 10 Clock Cycles for the LD(HL),40. Paints Piano key WHITE
  158.                 ' LD A,(IY+2)           '  Get 3rd Byte of Tune Data from 33902
  159.                         A=MusicData[IY+2]  ; TCycles:+19
  160.                 '       CALL 37675                              ' Note A is returned with the value for the OUT(254),A! Also Update's on screen piano Key
  161.                         A=Call_37675()  ' A IS RETURNED ALTERD CRUCIAL FOR THE VALUE OF A!
  162.                         TCycles:+17             ' 17 Clock Cycles for the call
  163.                         ' Code there is irelevant, but time must be acounted for!
  164.                 ' LD (HL),56
  165.                         TCycles:+10             ' 10 Clock Cycles for the LD(HL),40    
  166.                 ' INC IY                                        ' Increase IY by one
  167.                         IY:+1 ; TCycles:+10
  168.                 ' INC IY                                        ' Increase IY by one
  169.                         IY:+1 ; TCycles:+10
  170.                 ' INC IY                                        ' Increase IY by one
  171.                         IY:+1 ; TCycles:+10
  172.                 ' JR 37596
  173.                         TCycles:+12
  174.                 Wend
  175. End Function
  176.  
  177. ' Check Wether ENTER Or FIRE is being pressed
  178. Function Call_37687:Byte()
  179.                  Print "Checking Keyboard"
  180.                 ' LD A,(33881)
  181.                         A=0     ; TCycles:+13                                   ' Load A with Peek(33881). This Value will be 0 for this Purpose
  182.                 '       OR A                                                                                    ' Is Joystick Connected
  183.                         A=(A | A)
  184.                         'Print "A is "+A ; Repeat Until KeyDown(Key_SPACE)
  185.                         If A=0 Then fZERO=1 Else fZERO=0 ; TCycles:+4
  186.                         'Print "GOT HERE!" ' For Testing Key Press FLAG Loop!
  187.                 ' JR Z,37698                                                            ' Jump Forwrad if not
  188.                         If fZERO=0 Then
  189.                                         ' IN A,(31)                                             ' Kempston Joystick PORT
  190.                                                 A=0 ; TCycles:+11               ' Assume FIRE is not being pressed, So we PASS back a 0!
  191.                                         ' BIT 4,A                                                       ' Was FIRE pressed?
  192.                                                 Local Ans:Byte
  193.                                                 Ans=(16 & A)                            ' Test Bit 4 ie, 2^4=16
  194.                                                 If Ans=0 Then fZERO=1 Else fZERO=0 ; TCycles:+8
  195.                                         ' RET NZ                                                        ' Return the ZERO FLAG if FIRE Pressed, Which it won't be
  196.                                                 If fZERO=1 Then
  197.                                                                 TCycles:+11 ; Return fZERO
  198.                                                         Else
  199.                                                                 TCycles:+5
  200.                                                 End If
  201.                         End If
  202.                         ' Print "GOT HERE!" ' For Testing Key Press FLAG Loop!
  203.                 ' LD BC,49140                                   ' Load BC ready to Read the Key Buffer for Keys_H_to_ENT
  204.                         BC=49150 ; TCycles:+10
  205.                         ' Print "GOT HERE!" ' For Testing Key Press FLAG Loop!
  206.                 ' IN A,(C)
  207.                         If KeyDown(Key_ENTER) Then
  208.                                         A=254 ; ENTER=1
  209.                                         'Print "A is "+A
  210.                                         'Print "Key_ENTER" ; Repeat Until KeyDown(Key_SPACE)
  211.                                 Else
  212.                                         A=255 ; ENTER=0
  213.                                         'Print "A is "+A
  214.                                         'Print "Key_ENTER" ; Repeat Until KeyDown(Key_SPACE)
  215.                         End If                                         
  216.                         TCycles:+11             ' if ENTER then A=254 else A=255
  217.                 ' AND 1
  218.                         A=(A & 1)
  219.                         If A=0 Then fZERO=1 Else fZERO=0
  220.                         TCycles:+7
  221.                 ' CP 1                          '  Has ENTER been pressed?
  222.                         If (A-1)=0 Then fZERO=1 Else fZERO=0 ; TCycles:+7
  223.                 '       RET
  224.                         TCycles:+10
  225. End Function
  226.  
  227. ' Update Keyboard Piano key and Value of A
  228. Function Call_37675:Byte()
  229.                 '       SUB 8
  230.                         A=A-8 ; TCycles:+7
  231.                         Local TempCARRY:Byte
  232.                 ' RRCA 
  233.                         TempCARRY=(A & 1) <> 0
  234.                         If TempCARRY=0 Then A=(A Shr 1) Else A=((A Shr 1) | 128)
  235.                         TCycles:+4
  236.                 ' RRCA 
  237.                         TempCARRY=(A & 1) <> 0
  238.                         If TempCARRY=0 Then A=(A Shr 1) Else A=((A Shr 1) | 128)
  239.                         TCycles:+4
  240.                 ' RRCA 
  241.                         TempCARRY:Byte=(A & 1) <> 0
  242.                         If TempCARRY=0 Then A=(A Shr 1) Else A=((A Shr 1) | 128)
  243.                         TCycles:+4     
  244.                 ' CPL
  245.                         A=(A ~ 255) ; TCycles:+4
  246.                 ' OR 224
  247.                         A=(A | 224) ; TCycles:+7
  248.                 ' LD L,A
  249.                         L=A ; TCycles:+4
  250.                 ' LD H,89
  251.                         H=89 ; TCycles:+7
  252.                 ' RET
  253.                         TCycles:+10 ; Return A
  254. End Function
  255.  
  256. Function OUT_CHUNKS(Port%, Value%)
  257.  
  258.         Const SAMPLE_RATE%=44100
  259.         Global NextSample:TAudioSample, PlayTime:Int
  260.         If Counter=0
  261.                 Print "Sample created"
  262.                 NextSample = CreateAudioSample(4410, SAMPLE_RATE, SF_MONO8 )
  263.                                                                         Print
  264.                                                                         Print "IY="+(RSet(IY, 3).Replace(" ", "0"))+" ... MusicData[IY]="+(RSet$(musicDATA[IY+2],3).Replace(" ","0"))+" ... A Register="+(RSet$(A,3).Replace(" ","0"))+" ... BIT4="+((A & 16) <> 0)
  265.                                                                         Print "OUT_CHUNKS CALLED!"
  266.         EndIf
  267.  
  268.         Select Value & 8
  269.                 Case 0
  270.                         NextSample.Samples[Counter]=50
  271.                 Case 8
  272.                         NextSample.Samples[Counter]=200
  273.         End Select
  274.         Counter=Counter + 1
  275.         If counter=4408
  276.                 NextSample.Samples[4408]=0    
  277.                 NextSample.Samples[4409]=0    
  278.                 Local Audio:TSound = LoadSound( NextSample )
  279.                 Repeat
  280.                   ' Delay 1
  281.                 Until PlayTime<MilliSecs()
  282.                 Print "play TSound"
  283.                 PlayTime=MilliSecs()+100
  284.                 PlaySound Audio
  285.                 Counter=0
  286.         EndIf
  287. End Function
  288.  
  289. Function Init()
  290.                 ' Reset Registers
  291.                 IY=0 ; A=0 ; B=0 ; C=0
  292.                 ' START SpecBlitz
  293.                 MainLoop()
  294. End Function
  295.  
  296. Function run()
  297.                 Call_37596()
  298.                 If fZERO=1 And Enter=0 Then
  299.                                 Print
  300.                                 Print "TUNE FINISHED"
  301.                                 Print "IY="+IY+" ... MusicData[IY]="+musicDATA[IY]
  302.                                 End
  303.                 End If
  304.                 'Print Key_Enter
  305.                 If ENTER=1 Then Print "ENTER was Pressed!" ; End
  306. EndFunction

If you read through my mini Emulator. There is a Delay routine that slows down BlitzMax 1.5 to the ZX Spectrum speed. Ive put in all the Tstate time Cycles of each Instruction. And just can't see away through the tree's  ??? On how to integrate this into your counter timer loop within OUT_CHUNKS. Anyone?

Conclusion
I really dont have a clue on how to use the TSound functions to create what i need hence the start of "Creating ZX SOUND" thread :-[

The code as is need's to be left intact! And only the "OUT_CHUNKS" routine using the Global TCycles somehow or the "Sound chip type" Routine to reference time within the Emulator. Using the Tcycles as a reference count. Being able todo this should allow other games or programs to use the "OUT_CHUNKS" function for sound as well. Rather than being unique to Manic Miner it's self.

Is there away the sound can be sped up or slowed down only using a GLOBAL DELAY? I see you used a DELAY instruction in there?

Side Tracking a bit BlitzMax 1.5 Really Dosen't explain how to use the TSOUND functions Clearly in Help >:( Any pointers on the Syntax to make the Penny drop would be much appriciated!

However, enjoy the Mini Zx Emulator and OUT_CHUNKS Playing the BLUE DANUBE from Manic Miner.

Thankyou to all who are helping and giving idea's.

Kind Regards Baggey
« Last Edit: February 20, 2021, 16:37:27 by Baggey »
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #64 on: February 20, 2021, 18:49:53 »
HOW TCYCLES IN OUT()?
I would think the OUT command in Z80 is no software function, which would consum time. It is the last step to hardware and only the call needs the 11 TCycles you already added.

MY DELAY
The circumstance that OUT is now a function is only "Windows"-related to simulate the hardware. You should calculte 0msec for it. The reason why I added a DELAY is also only windowsrelated. We have to care about that the next PlaySound exactly starts 100msec after the former PlaySound had started.
If your TCycles-timer ever will work perfect my DELAY will have no longer any effect. (but keep activated the code line for "security reasons".)


WOULD OUT_CHUNK WORK FOR EVERY Z80-CODE?
Yes! My OUT_CHUNK (please rename it as "OUT") is a possible way to simulate the hardware speaker with a BlitzMax TSound. You can "feed" it with any Value and it will always do what the Z80-speaker would have done. The only restrictions are...

1. The sound always starts with an latency of 100msec
2. Sounds shorter than 100msec are not played
3. The last 100msec of a sequence will be cutted away.

You can optimize this behavoir (2. and 3.), when you use a TimerEvent() based function (f.e. every 10msec) which periodically will have a look on the TAudioSample, whether it now should be played now. In this method the OUT()-function would only fill the TAudioSample but is not longer responisble for playing it.

TUNING THE SONG AND RISING SPEED
you can adjust the tuning of the music by changing Const SAMPLE_RATE towards lower values. F.e. 22.000 makes it sounding deeper. You can speed up the song by Changing the line B=255. f.e. B=125 plays it with double speed. If you need to do this inside the OUT, you could try to "forget" OUT calls. f.e.
Code: BlitzMax
  1. Function OUT(port%, value%)
  2.      Global SwapIt=1-SwapIt
  3.      If SwapIt=1 Return
  4.      ....
This would make the song playing 2 times faster

A HINT FOR TCYLCES
You are doing right in collecting the TCYCLES for simulating the processor speed of the Z80. you add them, and when they reached a crititical sum (f.e. 3500) you could pause the BlitzMax-Code for 1msec with a DELAY 1. This way you would not need a high resolution timer and the speed would be controlled 1.000 times a second.

Code: BlitzMax
  1. Function SlowDown()
  2.      Global Realtime:INT
  3.      If RealTime=0
  4.           RealTime=Millisecs()
  5.      Endif
  6.      If TCycles>3500
  7.           TCyles=TCyles-3500
  8.           RealTime=RealTime+1
  9.      Endif
  10.      While RealTime>Millisecs()
  11.           Delay 1
  12.      wend
  13. End Function
  14.  

This would interupt the simultator without slowing down the whole windows (and the other processes)
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Baggey

  • Full Member
  • ***
  • Posts: 182
Re: Creating ZX SOUND
« Reply #65 on: February 20, 2021, 21:00:52 »
Oh my god,

I feel like a "kid in a sweet shop" or in a "toy shop" not sure what translate's best but Kudos! Does that translate as well!

Daz ist gut!

I have four hours of play tomorrow until next weekend  :'(

For anyone who may be interseted this has been a long journey of passion! interest and tinasity if that's a word :-X

As i explained earlier in another thread writing an emulator from scratch takes a long time. If i ever get it working fully i promise to mention the people on the way "Kevin Picone" who helped me to get started. Midi Master and Scaremonger

A thread for Sound help!
https://www.underwaredesign.com/forums/index.php?topic=3946.0

My OLD Emulator which aint so OLD now! ;)
https://www.underwaredesign.com/forums/index.php?topic=3890.0

Again i think this deserves:-



Kind Regards Baggey
« Last Edit: February 20, 2021, 21:28:04 by Baggey »
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Baggey

  • Full Member
  • ***
  • Posts: 182
Re: Creating ZX SOUND
« Reply #66 on: March 27, 2021, 19:51:58 »
Im Interested in trying this with OpenAL but can't seem to get anything to work ive installed the .ExE filen for openAL.
But get this error anyone?

Code: [Select]
Building untitled1
Compiling:untitled1.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 3331 bytes.
Linking:untitled1.exe
Executing:untitled1.exe
No access to OpenAL capture Device !
Process complete

Kind Regards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Scaremonger

  • Full Member
  • ***
  • Posts: 233
    • ITSpeedway - Ramblings of a geek!
Re: Creating ZX SOUND
« Reply #67 on: March 27, 2021, 22:20:44 »
You should keep an eye on this link.

https://www.syntaxbomb.com/index.php/topic,8377.0.html

@Midimaster is creating a pretty awesome ringbuffer.


Offline Baggey

  • Full Member
  • ***
  • Posts: 182
Re: Creating ZX SOUND
« Reply #68 on: April 02, 2021, 17:31:51 »
Finally somemore time to tinker.

So, Thought id have ago at doing JetSet Willy Theme tune. In the Z80 Emulation format.
This runs in BlitzMax 1.5

Code: [Select]
' JetSet Willy Player Using BlitzMax 1.5

SuperStrict

Graphics 800,600

Global RunSpeed:Int=16 ' Using Blitzmax 1.5 Gives roughly a 50Hz Frame Delay!

' Registers 16 Bit
Global IY:Short, BC:Short, DE:Short, HL:Short, AF:Short

' Registers 8 Bit
Global A:Byte, B:Byte, C:Byte, D:Byte, E:Byte, H:Byte, L:Byte, F:Byte

' Registers 16 Bit
Global AF_:Short

' Alternate 8 Bit
Global A_:Byte, F_:Byte

' Control Registers
Global TCycles:Int
Global fZERO:Byte
Global fCARRY:Byte=0
Global ENTER:Byte=0
Global Counter:Int

' Temp Values
Global TempA:Short
Global Ans:Short
Global sum:Byte
Global Copy_of_bit7:Byte

' Music Data
Global MusicData:Int[] = [81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,76,60,51,76,60,51,76,57,45,76,57,45,81,64,45,81,60,51,81,60,54,91,64,54,102,81,60,81,60,51,81,60,51,40,60,40,40,54,45,81,54,45,81,54,45,40,54,40,40,60,51,81,60,51,38,60,79,45,76,60,45,40,64,51,81,64,51,45,64,54,32,64,54,61,121,61,255]
Print
Print "Length of MusicData = "+(Len MusicData)

Init()

Function MainLoop()
    Local starttime:Int = MilliSecs()
   
    While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)
       
Run()
        Local endtime:Int = MilliSecs()
        Local diff:Int = endtime - starttime
       
        Local pausedelay:Int = RunSPEED ' About 50Hz ie one TV frame

        If pausedelay > 0 Then
            Delay(pausedelay)
        Else
            pausedelay = 0
        End If
        starttime = endtime+pausedelay
    Wend
End Function

Function Call_38562:Byte()

While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)

' LD A,(HL) '  Get next Byte of Tune Data from 34299. In this case 0
A=MusicData[HL]  ; TCycles:+19

' CP 255 '  Has the tune finished?
sum=A-255
Print "A-255="+sum
If (A-255)=0 Then fZERO=1 Else fZERO=0 ; TCycles:+7

' RET Z         ' Return if ZERO flag is Set
If fZERO=1 Then
TCycles:+11 ; Return fZERO
Else
TCycles:+5
End If

' LD BC,100 ' Load BC with 100. B=0 (short note Counter). C=100 (Short Note Counter)
B=0 ; C=100 ; BC=(B Shl 8)+C ; TCycles:+10

' XOR A          ' XOR A  Equivalent to LOAD A with 0 ( Incerting a delay of 4 TCycles )
' Border Colour and Speaker state
A=(A ~ A) ; TCycles:+4

' LD E,(HL) ' Load E with byte of MusicData During the short note loop
E=MusicData[HL] ; DE=(D Shl 8)+E ; TCycles:+19

' LD D,E ' Load D with (Pitch delay counter)
D=E ; DE=(D Shl 8)+E ; TCycles:+4
'
Repeat ' C is OUTER LOOP Until C=0
' Part of the DJNZ
'
Repeat ' B is iner LOOP until B=0
'For Local DJNZ:Int=B To 0 Step -1 ' until B=0
'Print "B="+B

' OUT (254),A ' Produce a short note ( 0.003s ) Whose pitch is determined by E
OUT_254(254,A) ; TCycles:+11

' DEC D ' Decrease D by One
D:-1 ; DE=(D Shl 8)+E ; TCycles:+4
If D=0 Then fZERO=1 Else fZERO=0 ' If D=0 then set Zero flag

' JR NZ,38580
If fZERO=1 Then
' LD D,E
D = E ; DE=(D Shl 8)+E ; TCycles:+19
' XOR 24
A = (A ~ 24)
TCycles:+7 ' Condition not met
Else
TCycles:+12 ' Condition met
End If

' DJNZ
'DisJump=(twosum[PC1]+2) ; B:-1 ; BC=(B Shl 8)+C
If B<>0 Then 
TCycles:+13 ' Condition met
Else
TCycles:+8 ' Condition not met
End If
B:-1 ; BC=(B Shl 8)+C
'
Until (B=0) ' B=0

' EX AF,AF_ ' Exchange AF with AF_ Alternate registers
TempA=A ; A=TempA ; TCycles:+7

' LD A,C   ' Load A with C
A=C ; TCycles:+4

' CP 50 '  Is the short note counter down to 50 yet?
If (A-50)=0 Then fZERO=1 Else fZERO=0 ; TCycles:+7

' JR NZ,38590 ' Jump if not otherwise
If fZERO=1 Then
' RL E ' Double value in E
Copy_of_bit7=(E & 128) <> 0
Ans=(E Shl 1)
Ans = Ans & 255
'
'fSIGN=(Ans & 128) <> 0
fZERO = (Ans=0)
'fFIVE = (Ans & 32) <> 0
'fHALFCARRY = 0
'fTHREE = (Ans & 8) <> 0
'fPARITY=parity[Ans]
'fADDSUB = 0
fCARRY = Copy_of_bit7
'SetFLAGS()
E=Ans

' XOR 24
A = (A ~ 24)
TCycles:+7 ' Condition not met
Else
TCycles:+12 ' Condition met
End If

' EX AF,AF_ ' Exchange AF with AF_ Alternate registers
TempA=A ; A=TempA ; TCycles:+7


' DEC C ' Decrease C by one
C:-1 ; BC=(B Shl 8)+C ; TCycles:+4
If C=0 Then fZERO=1 Else fZERO=0 ' If E=0 then set Zero flag


' JR NZ ' Jump back until we've finished playing 50 notes
'
Until (C=0) ' C=0

' Check KEYBOARD and JOYSTICK
CALL_38601() ; TCycles:+17 ' Check wether Enter or Fire is being pressed Code ignored but timing needed!

' RET NZ          ' Return if ZERO flag is ReSet, if it is
If fZERO=0 Then
TCycles:+11 ; Return fZERO
Else
TCycles:+5
End If

' INC HL ' Increase HL by one
HL:+1 ; TCycles:+10

' JR 38562
TCycles:+12
Wend
End Function

' Check Wether ENTER Or FIRE is being pressed
Function Call_38601:Byte()
Print "Checking Keyboard"
' LD A,(34254)
A=0 ; TCycles:+13 ' Load A with Peek(34254). This Value will be 0 for this Purpose
' OR A ' Is Joystick Connected
A=(A | A)
'Print "A is "+A ; Repeat Until KeyDown(Key_SPACE)
If A=0 Then fZERO=1 Else fZERO=0 ; TCycles:+4
'Print "GOT HERE!" ' For Testing Key Press FLAG Loop!
' JR Z,38612 ' Jump Forwrad if not
If fZERO=0 Then
' IN A,(31) ' Kempston Joystick PORT
A=0 ; TCycles:+11 ' Assume FIRE is not being pressed, So we PASS back a 0!
' BIT 4,A ' Was FIRE pressed?
Local Ans:Byte
Ans=(16 & A) ' Test Bit 4 ie, 2^4=16
If Ans=0 Then fZERO=1 Else fZERO=0 ; TCycles:+8
' RET NZ ' Return the ZERO FLAG if FIRE Pressed, Which it won't be
If fZERO=1 Then
TCycles:+11 ; Return fZERO
Else
TCycles:+5
End If
End If
' Print "GOT HERE!" ' For Testing Key Press FLAG Loop!
' LD BC,45054 ' Load BC ready to Read the Key Buffer for Keys_H_to_ENT and 6-7-8-9-0
BC=45054 ; TCycles:+10
' Print "GOT HERE!" ' For Testing Key Press FLAG Loop!
' IN A,(C)
If KeyDown(Key_ENTER) Then
A=254 ; ENTER=1
'Print "A is "+A
'Print "Key_ENTER" ; Repeat Until KeyDown(Key_SPACE)
Else
A=255 ; ENTER=0
'Print "A is "+A
'Print "Key_ENTER" ; Repeat Until KeyDown(Key_SPACE)
End If
TCycles:+11 ' if ENTER then A=254 else A=255
' AND 1
A=(A & 1)
If A=0 Then fZERO=1 Else fZERO=0
TCycles:+7
' CP 1 '  Has ENTER been pressed?
If (A-1)=0 Then fZERO=1 Else fZERO=0 ; TCycles:+7
' RET
TCycles:+10
End Function

Function OUT_254(Port:Byte, Value:Byte)
' The three lines of code speed the song up ie Twice the speed!
'Global SwapIt:Byte
      'SwapIt=1-SwapIt
      'If SwapIt=1 Return
'
        Const SAMPLE_RATE:Short=44100
        Global NextSample:TAudioSample, PlayTime:Int
        If Counter=0
                Print "Sample created"
                NextSample = CreateAudioSample(4410, SAMPLE_RATE, SF_MONO8 )
Print
Print "HL="+(RSet(HL, 3).Replace(" ", "0"))+" ... MusicData[HL]="+(RSet$(musicDATA[HL],3).Replace(" ","0"))+" ... A Register="+(RSet$(A,3).Replace(" ","0"))+" ... BIT4="+((A & 16) <> 0)
Print "OUT_254 Called!"
        EndIf
 
        Select Value & 8
                Case 0
                        NextSample.Samples[Counter]=50
                Case 8
                        NextSample.Samples[Counter]=200
        End Select
        Counter=Counter + 1
        If counter=4408
                NextSample.Samples[4408]=0     
                'NextSample.Samples[4409]=0     
                Local Audio:TSound = LoadSound( NextSample )
                Repeat
                  ' Delay 1
                Until PlayTime<MilliSecs()
                Print "play TSound"
                PlayTime=MilliSecs()+100
                PlaySound Audio
                Counter=0
        EndIf
End Function

Function Init()
' Reset Registers
IY=0 ; A=0 ; B=0 ; C=0 ; D=0 ; E=0 ; F=0 ; TempA=0
' START SpecBlitz
MainLoop()
End Function

Function run()
Call_38562()
If fZERO=1 And Enter=0 Then
Print
Print "TUNE FINISHED"
Print "HL="+HL+" ... MusicData[HL]="+musicDATA[HL]
End
End If
'Print Key_Enter
If ENTER=1 Then Print "ENTER was Pressed!" ; End
EndFunction

Something wrong here, in the loops as i think each note is being played 3 times. Anyway you can here the Midnight Sonata in there. But something's not quite write?

Been staring at this most of the day and can't see my mistake.

Kind Rgards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #69 on: April 02, 2021, 17:57:20 »
Did you check the quality of the DATA[]-field with my StandAlone-Player from post#56?

How does it sound for you here?


And... Is it the correct number of values?

The size needs to be (3*x+1), so 100 would fit perfect, but there are 101 elements. The player crashs.

Do you have a link, how the music should sound? What is "midnight sonata"? Beethoven?



« Last Edit: April 02, 2021, 18:00:15 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Baggey

  • Full Member
  • ***
  • Posts: 182
Re: Creating ZX SOUND
« Reply #70 on: April 03, 2021, 09:29:16 »
POST HAS BEEN UPDATED

Will play somemore latter on. Ill check post 56. It's Beethoven i believe.

Ive now checked post #56 and it sounds nothing like it should.

The machine code routine that plays the tune in Manic Miner is slightly different to the machine code that plays the tune in Jet Set Willy. Out 254 would do the same thing to the speaker though!?

I think Manic Miner use's 3 bytes to make the tune and Jet Set willy uses 2 bytes (2*X+1). I think it's all to do with timing in the Loops themselves. Looking forward to experimenting with the Ring buffer wrapper. Hope it works with BlitzMax 1.5 and is easy to install.  :D

Latter on today ill check ive ripped the music data properly!

For now it should sound like this.

Kind Regards Baggey

« Last Edit: April 03, 2021, 09:48:43 by Baggey »
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Baggey

  • Full Member
  • ***
  • Posts: 182
Re: Creating ZX SOUND
« Reply #71 on: April 03, 2021, 10:45:26 »
JetSet Willy Tune Data actually in memory:-

So ripped data from Memory in my SpecBlitz Emulator so data is spot on!  ;D

There are 99 Bytes of Data. With the last byte being 255 terminator for the loop. If we count the Zero we have 100.

Will have a look at the Z80 Machine code emulation as i think ive got a loop wrong!

Code: [Select]
Building Spectrum emulator
Compiling:ZXio.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 11441 bytes.
Compiling:Z80.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 277502 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, 107819 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, 115762 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, 61637 bytes.
Linking:Spectrum emulator.exe
Executing:Spectrum emulator.exe
start ADDRESS
PEEK(34299)=81
PEEK(34300)=60
PEEK(34301)=51
PEEK(34302)=81
PEEK(34303)=60
PEEK(34304)=51
PEEK(34305)=81
PEEK(34306)=60
PEEK(34307)=51
PEEK(34308)=81
PEEK(34309)=60
PEEK(34310)=51
PEEK(34311)=81
PEEK(34312)=60
PEEK(34313)=51
PEEK(34314)=81
PEEK(34315)=60
PEEK(34316)=51
PEEK(34317)=81
PEEK(34318)=60
PEEK(34319)=51
PEEK(34320)=81
PEEK(34321)=60
PEEK(34322)=51
PEEK(34323)=76
PEEK(34324)=60
PEEK(34325)=51
PEEK(34326)=76
PEEK(34327)=60
PEEK(34328)=51
PEEK(34329)=76
PEEK(34330)=57
PEEK(34331)=45
PEEK(34332)=76
PEEK(34333)=57
PEEK(34334)=45
PEEK(34335)=81
PEEK(34336)=64
PEEK(34337)=45
PEEK(34338)=81
PEEK(34339)=60
PEEK(34340)=51
PEEK(34341)=81
PEEK(34342)=60
PEEK(34343)=54
PEEK(34344)=91
PEEK(34345)=64
PEEK(34346)=54
PEEK(34347)=102
PEEK(34348)=81
PEEK(34349)=60
PEEK(34350)=81
PEEK(34351)=60
PEEK(34352)=51
PEEK(34353)=81
PEEK(34354)=60
PEEK(34355)=51
PEEK(34356)=40
PEEK(34357)=60
PEEK(34358)=40
PEEK(34359)=40
PEEK(34360)=54
PEEK(34361)=45
PEEK(34362)=81
PEEK(34363)=54
PEEK(34364)=45
PEEK(34365)=81
PEEK(34366)=54
PEEK(34367)=45
PEEK(34368)=40
PEEK(34369)=54
PEEK(34370)=40
PEEK(34371)=40
PEEK(34372)=60
PEEK(34373)=51
PEEK(34374)=81
PEEK(34375)=60
PEEK(34376)=51
PEEK(34377)=38
PEEK(34378)=60
PEEK(34379)=45
PEEK(34380)=76
PEEK(34381)=60
PEEK(34382)=45
PEEK(34383)=40
PEEK(34384)=64
PEEK(34385)=51
PEEK(34386)=81
PEEK(34387)=64
PEEK(34388)=51
PEEK(34389)=45
PEEK(34390)=64
PEEK(34391)=54
PEEK(34392)=32
PEEK(34393)=64
PEEK(34394)=54
PEEK(34395)=61
PEEK(34396)=121
PEEK(34397)=61
PEEK(34398)=255
end ADDRESS

And as a Cut and Paste string :-

Code: [Select]
Building Spectrum emulator
Compiling:ZXio.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
3 passes, 11441 bytes.
Compiling:Z80.bmx
flat assembler  version 1.69.14  (1572863 kilobytes memory)
4 passes, 277089 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, 107819 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, 115762 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, 61637 bytes.
Linking:Spectrum emulator.exe
Executing:Spectrum emulator.exe
Start ADDRESS 34299
81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,76,60,51,76,60,51,76,57,45,76,57,45,81,64,45,81,60,51,81,60,54,91,64,54,102,81,60,81,60,51,81,60,51,40,60,40,40,54,45,81,54,45,81,54,45,40,54,40,40,60,51,81,60,51,38,60,45,76,60,45,40,64,51,81,64,51,45,64,54,32,64,54,61,121,61,255,
End ADDRESS 34398

Kind Regards Baggey

« Last Edit: April 03, 2021, 11:00:03 by Baggey »
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #72 on: April 03, 2021, 11:29:06 »
The audio sample compared with the datas I would guess that each byte is a new sound and the lenght of all is the same (fix).
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Baggey

  • Full Member
  • ***
  • Posts: 182
Re: Creating ZX SOUND
« Reply #73 on: April 03, 2021, 17:43:39 »
The audio sample compared with the datas I would guess that each byte is a new sound and the lenght of all is the same (fix).

Done it! the Out Function is fine. There where three errors in my Emulation of the Z80 code.

Sometimes it pays to have a break and walk away, and then come back to the code. As i found some silly errors quite quickly after a good walk and some fresh air  ^-^

id not emulated the RL E properly which just need doubling and the Exchange EX AF,AF_ was getting the same value and there was an extra XOR 24 ie on off of the speaker that wasn't needed  ;D in one off the loops.

I hope the wrapper will be able to play faster sounds than 100 mSecs as some sounds arnt being played quick enough i think. ie not heard.
Ive noticed with this tune a slight ringing? At the end of every note.

So here is executable code to play JetSet Willy. Midnight sonata by Beethoven :-

Code: [Select]
' JetSet Willy Player Using BlitzMax 1.5

SuperStrict

Graphics 800,600

Global RunSpeed:Int=16 ' Using Blitzmax 1.5 Gives roughly a 50Hz Frame Delay!

' Registers 16 Bit
Global BC:Short, DE:Short, HL:Short

' Registers 8 Bit
Global A:Byte, B:Byte, C:Byte, D:Byte, E:Byte, H:Byte, L:Byte

' Alternate 8 Bit
Global A_:Byte

' Control Registers
Global TCycles:Int
Global fZERO:Byte
Global ENTER:Byte=0
Global Counter:Int

' Temp Values
Global TempA:Byte
Global Ans:Short

' Music Data
Global MusicData:Int[] = [81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,76,60,51,76,60,51,76,57,45,76,57,45,81,64,45,81,60,51,81,60,54,91,64,54,102,81,60,81,60,51,81,60,51,40,60,40,40,54,45,81,54,45,81,54,45,40,54,40,40,60,51,81,60,51,38,60,45,76,60,45,40,64,51,81,64,51,45,64,54,32,64,54,61,121,61,255]
Print
Print "Length of MusicData = "+(Len MusicData)

Init()

Function MainLoop()
    Local starttime:Int = MilliSecs()
   
    While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)
       
Run()
        Local endtime:Int = MilliSecs()
        Local diff:Int = endtime - starttime
       
        Local pausedelay:Int = RunSPEED ' About 50Hz ie one TV frame

        If pausedelay > 0 Then
            Delay(pausedelay)
        Else
            pausedelay = 0
        End If
        starttime = endtime+pausedelay
    Wend
End Function

Function Call_38562:Byte()

While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)

' LD A,(HL) '  Get next Byte of Tune Data from 34299. In this case 0
A=MusicData[HL]  ; TCycles:+19

' CP 255 '  Has the tune finished?
If (A-255)=0 Then fZERO=1 Else fZERO=0 ; TCycles:+7

' RET Z         ' Return if ZERO flag is Set
If fZERO=1 Then
TCycles:+11 ; Return fZERO ' Exit Function End of Tune
Else
TCycles:+5
End If

' LD BC,100 ' Load BC with 100. B=0 (short note Counter). C=100 (Short Note Counter)
B=0 ; C=100 ; BC=(B Shl 8)+C ; TCycles:+10

' XOR A         ' XOR A  Equivalent to LOAD A with 0 ( Incerting a delay of 4 TCycles )
A=(A ~ A) ; TCycles:+4 ' Border Colour and Speaker state

' LD E,(HL) ' Load E with byte of MusicData During the short note loop
E=MusicData[HL] ; DE=(D Shl 8)+E ; TCycles:+19

' LD D,E ' Load D with (Pitch delay counter)
D=E ; DE=(D Shl 8)+E ; TCycles:+4
'
Repeat ' C is OUTER LOOP Until C=0
' Part of the DJNZ
'
Repeat ' B is iner LOOP until B=0
'Print "B="+B
'
' OUT (254),A ' Produce a short note ( 0.003s ) Whose pitch is determined by E
OUT_254(254,A) ; TCycles:+11

' DEC D ' Decrease D by One
D:-1 ; DE=(D Shl 8)+E ; TCycles:+4
If D=0 Then fZERO=1 Else fZERO=0 ' If D=0 then set Zero flag

' JR NZ,38580
If fZERO=1 Then
' LD D,E
D=E ; DE=(D Shl 8)+E ; TCycles:+19
' XOR 24
A=(A ~ 24)
TCycles:+7 ' Condition not met
Else
TCycles:+12 ' Condition met
End If

' DJNZ
If B<>0 Then 
TCycles:+13 ' Condition met
Else
TCycles:+8 ' Condition not met
End If
B:-1 ; BC=(B Shl 8)+C
'
Until (B=0) ' B=0

' EX AF,AF_ ' Exchange AF with AF_ Alternate registers
'Print "A Before exchange ="+A
TempA=A_ ; A_=A ; A=TempA ; TCycles:+7 ' For this emulation focus on A
'Print "A after exchange ="+A

' LD A,C   ' Load A with C
A=C ; TCycles:+4

' CP 50 '  Is the short note counter down to 50 yet?
If (A-50)=0 Then fZERO=1 Else fZERO=0 ; TCycles:+7

' JR NZ,38590 ' Jump if not otherwise
If fZERO=1 Then
' RL E ' Double value in E ' Rotating left is same as * by 2
Print "E="+E
E:+E ; DE=(D Shl 8)+E
Print "E Doubled ="+E
TCycles:+7 ' Condition not met
Else
TCycles:+12 ' Condition met
End If

' EX AF,AF_ ' Exchange AF with AF_ Alternate registers
TempA=A_ ; A_=A ; A=TempA ; TCycles:+7

' DEC C ' Decrease C by one
C:-1 ; BC=(B Shl 8)+C ; TCycles:+4
If C=0 Then fZERO=1 Else fZERO=0 ' If C=0 then set Zero flag

' JR NZ ' Jump back until we've finished playing 50 notes
'
Until (C=0) ' C=0

' Check KEYBOARD and JOYSTICK
CALL_38601() ; TCycles:+17 ' Check wether Enter or Fire is being pressed Code ignored but timing needed!

' RET NZ     ' Return if ZERO flag is ReSet, if it is
If fZERO=0 Then
TCycles:+11 ; Return fZERO ' Enter or fire was pressed
Else
TCycles:+5
End If

' INC HL ' Increase HL by one
HL:+1 ; TCycles:+10

' JR 38562
TCycles:+12
Wend
End Function

' Check Wether ENTER Or FIRE is being pressed
Function Call_38601:Byte()
Print "Checking Keyboard"
' LD A,(34254)
A=0 ; TCycles:+13 ' Load A with Peek(34254). This Value will be 0 for this Purpose

' OR A ' Is Joystick Connected
A=(A | A)
'Print "A is "+A ; Repeat Until KeyDown(Key_SPACE)
If A=0 Then fZERO=1 Else fZERO=0 ; TCycles:+4
'Print "GOT HERE!" ' For Testing Key Press FLAG Loop!

' JR Z,38612 ' Jump Forwrad if not
If fZERO=0 Then
' IN A,(31) ' Kempston Joystick PORT
A=0 ; TCycles:+11 ' Assume FIRE is not being pressed, So we PASS back a 0!
' BIT 4,A ' Was FIRE pressed?
Local Ans:Byte
Ans=(16 & A) ' Test Bit 4 ie, 2^4=16
If Ans=0 Then fZERO=1 Else fZERO=0 ; TCycles:+8
' RET NZ ' Return the ZERO FLAG if FIRE Pressed, Which it won't be
If fZERO=1 Then
TCycles:+11 ; Return fZERO
Else
TCycles:+5
End If
End If
' Print "GOT HERE!" ' For Testing Key Press FLAG Loop!

' LD BC,45054 ' Load BC ready to Read the Key Buffer for Keys_H_to_ENT and 6-7-8-9-0
BC=45054 ; TCycles:+10
' Print "GOT HERE!" ' For Testing Key Press FLAG Loop!

' IN A,(C)
If KeyDown(Key_ENTER) Then
A=254 ; ENTER=1
'Print "A is "+A
'Print "Key_ENTER" ; Repeat Until KeyDown(Key_SPACE)
Else
A=255 ; ENTER=0
'Print "A is "+A
'Print "Key_ENTER" ; Repeat Until KeyDown(Key_SPACE)
End If
TCycles:+11 ' if ENTER then A=254 else A=255

' AND 1
A=(A & 1)
If A=0 Then fZERO=1 Else fZERO=0
TCycles:+7

' CP 1 '  Has ENTER been pressed?
If (A-1)=0 Then fZERO=1 Else fZERO=0 ; TCycles:+7

' RET
TCycles:+10
End Function

Function OUT_254(Port:Byte, Value:Byte)
' The three lines of code speed the song up ie Twice the speed!
'Global SwapIt:Byte
    'SwapIt=1-SwapIt
    'If SwapIt=1 Return
'
        Const SAMPLE_RATE:Short=44100
        Global NextSample:TAudioSample, PlayTime:Int
        If Counter=0
                Print "Sample created"
                NextSample = CreateAudioSample(4410, SAMPLE_RATE, SF_MONO8 )
Print
Print "HL="+(RSet(HL, 3).Replace(" ", "0"))+" ... MusicData[HL]="+(RSet$(musicDATA[HL],3).Replace(" ","0"))+" ... A Register="+(RSet$(A,3).Replace(" ","0"))+" ... BIT4="+((A & 16) <> 0)
Print "OUT_254 Called!"
        EndIf
 
        Select Value & 8
                Case 0
                        NextSample.Samples[Counter]=50
                Case 8
                        NextSample.Samples[Counter]=200
        End Select
        Counter=Counter + 1
        If counter=4408
                NextSample.Samples[4408]=0     
                'NextSample.Samples[4409]=0     
                Local Audio:TSound = LoadSound( NextSample )
                Repeat
                  ' Delay 1
                Until PlayTime<MilliSecs()
                Print "play TSound"
                PlayTime=MilliSecs()+100
                PlaySound Audio
                Counter=0
        EndIf
End Function

Function Init()
' Reset Registers
A=0 ; B=0 ; C=0 ; D=0 ; E=0 ; A_=0 ; TempA=0
' START SpecBlitz
MainLoop()
End Function

Function run()
Call_38562()
If fZERO=1 And Enter=0 Then
Print
Print "TUNE FINISHED"
Print "HL="+HL+" ... MusicData[HL]="+musicDATA[HL]
End
End If
'Print Key_Enter
If ENTER=1 Then Print "ENTER was Pressed!" ; End
EndFunction

Kind Regards Baggey
« Last Edit: April 03, 2021, 17:45:44 by Baggey »
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #74 on: April 03, 2021, 18:15:20 »
you can simply speed up if you reduce the value of C here:
Code: [Select]
' LD BC,100 ' Load BC with 100. B=0 (short note Counter). C=100 (Short Note Counter)
B=0 ; C=30 ; BC=(B Shl 8)+C ; TCycles:+10

I changed C=100 to C=30

Try also 10 or 5 or 3 to see, how special effects can be made....
did you already succeed in motor sound, etc...?

by the way...

here is the update OnlyPlayer-Version for testing 1Byte-Song-Data:
Code: BlitzMax
  1.     SuperStrict
  2.      
  3.     Graphics 800,600
  4.    
  5. Global Data:Int[] = [81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,81,60,51,76,60,51,76,60,51,76,57,45,76,57,45,81,64,45,81,60,51,81,60,54,91,64,54,102,81,60,81,60,51,81,60,51,40,60,40,40,54,45,81,54,45,81,54,45,40,54,40,40,60,51,81,60,51,38,60,45,76,60,45,40,64,51,81,64,51,45,64,54,32,64,54,61,121,61,255]
  6.  
  7.     Global NextTime%, ReadPos%
  8.      
  9.      
  10.     Repeat
  11.             Wait
  12.             If Data[ReadPos]=255 End
  13.     Until AppTerminate()
  14.      
  15.      
  16.     Function Wait()
  17.             If NextTime<MilliSecs()
  18.                     Print ReadPos + " " + data[ReadPos] + " " + data[ReadPos+1] + " " + data[ReadPos+2]
  19.                                         Local duration%=100
  20.                     NextTime=MilliSecs()+Duration
  21.                    MakeSound Duration, Data[ReadPos], 0
  22.      
  23.                     ReadPos=ReadPos+1
  24.             EndIf  
  25.     End Function
  26.      
  27.      
  28.     Function MakeSound(Duration%,PulseA%, PulseB%)
  29.             Const SAMPLERATE% = 22000'Hz
  30.             Local SampleLen%, Half%
  31.             Local SampleA:TAudioSample, SampleB:TAudioSample, AudioA:TSound, AudioB:TSound
  32.            
  33.             SampleLen = SAMPLERATE*Duration/1000
  34.             If PulseA>0    
  35.                             SampleA = CreateAudioSample( SampleLen, SAMPLERATE, SF_MONO8 )
  36.                             Half = PulseA/2
  37.                             For Local n:Int=0 Until SampleLen-1
  38.                                     If (n Mod PulseA) < half
  39.                                             SampleA.samples[n] = 200
  40.                                     Else
  41.                                             SampleA.samples[n] = 55
  42.                                     End If
  43.                             Next
  44.                             AudioA = LoadSound( SampleA )
  45.             EndIf
  46.             If PulseB>0            
  47.                             SampleB = CreateAudioSample( SampleLen, SAMPLERATE, SF_MONO8 )        
  48.                             Half = PulseB/2
  49.                             For Local n:Int=0 Until SampleLen-1
  50.                                     If (n Mod PulseB)<half
  51.                                             SampleB.samples[n] = 200
  52.                                     Else
  53.                                             SampleB.samples[n] = 55
  54.                                     End If
  55.                             Next          
  56.                             AudioB = LoadSound( SampleB )
  57.             EndIf
  58.             If PulseA>0    
  59.                     PlaySound AudioA
  60.             EndIf
  61.             If PulseB>0
  62.                     PlaySound AudioB
  63.             EndIf
  64.     End Function
  65.  
« Last Edit: April 03, 2021, 18:26:29 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal