Ooops
October 25, 2021, 10:28:34

Author Topic: Creating ZX SOUND  (Read 7037 times)

Offline Baggey

  • Full Member
  • ***
  • Posts: 196
Re: Creating ZX SOUND
« Reply #75 on: April 03, 2021, 19:10:01 »
did you already succeed in motor sound, etc...?

Not quite sure what you mean by "motor sound, etc...?"

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.    Resistance Is Futile! The code will be assimulated!

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 364
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #76 on: April 04, 2021, 00:13:22 »
oh i wanted to know, whether the emulator also is able to play the ingame sounds like crashs or thing you can hear at the end of the youtube moonlight sonata example you sended some posts ago? (in reply #70)
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Baggey

  • Full Member
  • ***
  • Posts: 196
Re: Creating ZX SOUND
« Reply #77 on: April 04, 2021, 08:47:17 »
oh i wanted to know, whether the emulator also is able to play the ingame sounds like crashs or thing you can hear at the end of the youtube moonlight sonata example you sended some posts ago? (in reply #70)

Not all? It sometimes clicks and bashes or it plays the in game music but not all of it. It's like playing some of the notes or the sounds are to quick! Im trying to put together a mini avi file of a couple of games but don't know how to post it yet ??? Im also still ironing out some underlying bugs. The more functionality im getting the bugs are getting harder to find.

Im probably a good 98% there. And id also like to add more features to it as well.

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.    Resistance Is Futile! The code will be assimulated!

Offline Baggey

  • Full Member
  • ***
  • Posts: 196
Re: Creating ZX SOUND
« Reply #78 on: July 09, 2021, 19:35:50 »
So, This isn't an update or anything!

Im still working on this.

Im almost 98% there with the emulator. im not good with trying to get an upload with how it's working at the moment.

Ive Found that there are undocumented NOP opCodes that no person seems to of mentioned anywhere? Only those who have sucessful Emulator's will know what im on about!

i am dry running and coming up against these's at the mo! BATMAN, COMMANDO, and Remember CHEQURED FLAG! seem to rely on these.

Sound with OUT CHUNKS no matter how i alter either works or dosent or seems to repeat it's self?

Have a week off and will be playing with various routine's!

Hopefully im almost at the point off all CODE working  8)

I would like to be able to upload an AVI for this thread to show in game sounds and emulation running if thou's in charge would let me!  :D

Oh also finding that at certain times there are write's to ROM which need to be ignored! As when an Interrupt happens that uses ROM code could be Currupted! So adding a feature to not write to any PART of memory below 16384! or just ignore the WRITE!

Kind Regards Baggey
« Last Edit: July 09, 2021, 19:59:24 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.    Resistance Is Futile! The code will be assimulated!

Offline Baggey

  • Full Member
  • ***
  • Posts: 196
Re: Creating ZX SOUND
« Reply #79 on: July 12, 2021, 09:40:12 »
So been Reading this Thread, I Havent read all of it yet.  :-X

https://www.syntaxbomb.com/worklogs/done!-a-new-audio-out-approach-in-blitzmax-freeaudio-ringbuffer/ from Reply #28 I think.

Quote
Silence at the end?
this is the dis-advantage of ring buffers. they will play the last datas of the buffer over and over again. So you have to fill the buffer in a last round with 0 to prevent this.

Now 16bit Stereo and Manipulation
Here is the next step. Now you can stream SF_STEREO16 and you can manipulate the samples by using a RAMStream. This is an executable example. use the TestC.ogg from attachment. (the poor quality you can hear is in the ogg file, not because of the algo). you can move the mouse to pan between LEFT and RIGHT.

What are the possible Chunk-times?

Any chunk time must be a possible divider of the 1000msec. f.e. 20 or 25 or 40, but not 30!
But the result must again be a possible divider of the samples frequency.

In our example the chunk-time for refreshing must stay at 20msec, because only this value is without crackling. This is caused by the fact that the prime number in 22050Hz is 441 and this a 1/50 of 22050Hz. ---> 1/50 from 100msec= 20msec.
Also a possible chunk-times is 40msec = 882samples

This means with 44100Hz you could also use 10msec, 20msec and 40msec

This means with 48000Hz or 24000Hz you are more flexible: 1, 2, 4, 5, 8, 10, 20, 25 and 40msec

My Latest Code Version of OUT_254 Implementing Midi's idea for OUT_CHUNKS

Code: BlitzMax
  1. Function OUT_254(Port:Byte, Value:Byte)
  2.                                
  3.         If (Port & 1) = 0 Then ZXio.Border = (Value & 7) 'Border Colour
  4.                                
  5.                 ' The three lines of code speed the song up ie Twice the speed!
  6.                 'Global SwapIt:Byte
  7.                 'SwapIt=1-SwapIt
  8.                 'If SwapIt=1 Return
  9.                 '
  10.                 'If Port=254 Then
  11.  
  12.         Const SAMPLE_RATE:Int= 24000 ' 22050 ' 96000
  13.         Global NextSample:TAudioSample, PlayTime:Int
  14.  
  15.  
  16.         If MusicCounter=0
  17.                 'Print "Sample created"
  18.                 NextSample = CreateAudioSample(883, SAMPLE_RATE, SF_mono8 )
  19.                 'Print
  20.                 '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)
  21.                 'Print "OUT_CHUNKS CALLED!"
  22.         End If
  23.  
  24.         ' This is checking EAR/MIC Bit 
  25.         'Note if bit 8 is on or off this is normal volume
  26.         ' if bit 16 id on or off is like the Loud button        
  27.         'Value=(Value & 24)
  28.         '       NextSample.Samples[MusicCounter]=0
  29.         '       If Value = 16 Then NextSample.Samples[MusicCounter]=200
  30.         '       If Value = 8 Then NextSample.Samples[MusicCounter]=128
  31.         'If Value = 24 Then NextSample.Samples[MusicCounter]=250
  32.         'If Port=254 Then
  33.         'If (Value & 16) Then
  34.          '   NextSample.Samples[MusicCounter] = 250
  35.         'Else
  36.          '   NextSample.Samples[MusicCounter]=0
  37.         'End If
  38.  
  39.         Local out_value:Byte=(Value & 24)
  40.         Print "Play Sound!   Out value "+out_value
  41.        
  42.         Select out_value
  43.                 ' Note the other Case values are todo with the EAR/MIC socket, Which is Effecting the Sound produced!?                         
  44.                 Case 0
  45.                         NextSample.Samples[MusicCounter]=50
  46.                 Case 8
  47.                         NextSample.Samples[MusicCounter]=200
  48.                 Case 16
  49.                         NextSample.Samples[MusicCounter]=200
  50.                 Case 24
  51.                         NextSample.Samples[MusicCounter]=200
  52.  
  53.                 'Default
  54.                 '                               NextSample.Samples[MusicCounter]=0
  55.  
  56.         End Select
  57.  
  58.         MusicCounter:+1
  59.  
  60.         If MusicCounter=880 Then ' Why 800 or 880 Sound Samples to Sound Better?
  61.                 NextSample.Samples[881]=0          
  62.                 Local Audio:TSound = LoadSound( NextSample )
  63.                 Repeat
  64.                    'Delay 1
  65.                 Until PlayTime<MilliSecs()
  66.                 'Print "play TSound"
  67.                 PlayTime=MilliSecs()+5 ' Played with this Helps with Fast Sounds and long Sounds? Between 1 and 100 or starts to cause Emulator Lag
  68.                 PlaySound Audio
  69.                 'Stopsound audio
  70.                 MusicCounter=0
  71.                                                                
  72.         End If
  73.         'End If
  74. End Function
  75.  

In the Above CODE 22050 sounds good but changing to 24000 gives higher pitched sounds to be in the Sample. Changing to 44100 sounds Tinney? The 22050 sounds BASSIER if that's a word.

In the Case Select statements the Mic and Ear together amplify the Sound slightly by changing Volume minimally not sure how to achieve this effect.
it sort of pluse's or Amplify's the Sound heard.

If i Play Manic Miner with the Above code the in game music is sort off okay but the Title Music is Ringing/Echoing or Reapeating Awfully.

Now,
 
If i change the Music Counter to 4408. Title Music sort off works but then i lose the quick in game Sounds.

So Im going to Look into the RingBuffer idea as im Using BlitzMax 1.5 Vanilla.

The Emulator "SpecBlitz or BlitzSpec" is Almost Working!  8). There is a game called Movie by Imagine. FUSE runs it with Flashing RED screen and so does Spectulator. Mine Runs it Almost!

Im Currently Stopping ROM write's as this currupts Imterrupt routine's Acessing the Rom code. Also there are a Lot of Special NOP opcode's not Documented.
They appear as two byte codes but don't work in that Manner. ::)

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.    Resistance Is Futile! The code will be assimulated!

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 364
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #80 on: July 12, 2021, 12:05:58 »
with the new ringbuffer approach  (starting reading from post #59 please!!!) you would use exact the same (or little slower) sample rate the old vintage computers had. The sample rate should fit to the timing rate of OUT_254()-calls.

A too high sample rate would result in pauses (or crackles), a too low sample rate would cause a little latency, when long songs are played. So better try at first slower sample rates.


The Ringbuffer-Approach needs a threaded BlitzMax build.


The commands coming in at your OUT_254() function need to be translated putten in an array, then send to the RingBuffer.Send AudioArray() function.


I will write a executable example here, which transfers the old CHUNK_OUT approach to the new RingBuffer-approach. Give me 2 days...

See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Derron

  • Hero Member
  • *****
  • Posts: 3674
Re: Creating ZX SOUND
« Reply #81 on: July 12, 2021, 12:15:28 »
The Ringbuffer-Approach needs a threaded BlitzMax build.

the threaded stuff could be done in C (eg callbacks into blitzmax from threads) and so work in "non threaded" builds (vanilla) too.


bye
Ron

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 364
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #82 on: July 14, 2021, 07:55:02 »
Ok, I adapted my FreeAudio-Ringbuffer to BlitzMax 1.50 and added some security functions, which cares about audio timing and prevent the buffer overrun.

This is how you would call the new approach:
Code: BlitzMax
  1. Import "RingBufferClassVanilla.bmx"                         ' import the BlitzMax Ringbuffer
  2. RingBufferClass.SetDriver("FreeAudio DirectSound")          ' needs to be this driver on Windows
  3. Global RingBuffer:RingBufferClass = New RingBufferClass     ' define the ringbuffer
  4. RingBuffer.CreateNew(40000, SF_MONO8)                       '  40000Hz and Mono 8bit
  5. Local WatchThread:TThread=CreateThread(WatchLoop, "")       ' define a multi-thread function
  6.  
  7. ' this is the function that cares about the ringbuffer DO NOT change anything
  8. Function  WatchLoop:Object(data:Object)
  9.         Repeat
  10.                 Delay 3
  11.                 RingBuffer.Watch
  12.         Forever
  13. End Function
  14.  
  15.  
  16. 'This is how you would send one single sample to the ringbuffer:
  17. RingBuffer.SendONE 200    ' sends a value of 200 to the buffer
  18.  




This is how your OUT_254 function would look now:

Code: BlitzMax
  1. SuperStrict
  2. Import "RingBufferClassVanilla.bmx"
  3.  
  4. Graphics 800,600
  5.  
  6. RingBufferClass.SetDriver("FreeAudio DirectSound")
  7. Global RingBuffer:RingBufferClass = New RingBufferClass
  8. RingBuffer.CreateNew(40000, SF_MONO8)
  9. Local WatchThread:TThread=CreateThread(WatchLoop, "")
  10.  
  11. Global A:Byte, B:Byte=1, C:Byte, D:Byte=0, E:Byte=0
  12. Global IY:Int
  13.  
  14. ' Music Data
  15. Global data%[] = [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]
  16. Print( "DATA LENGTH: "+Len(data) )
  17.  
  18. CALL_37596()
  19.  
  20. Repeat
  21.         Delay 5
  22. Until AppTerminate()
  23. End
  24.  
  25.  
  26. Function  WatchLoop:Object(data:Object)
  27.         Repeat
  28.                 Delay 3
  29.                 RingBuffer.Watch
  30.         Forever
  31. End Function
  32.  
  33.  
  34.  
  35. Function CALL_37596:Int()
  36.         While True
  37.                 A = data[IY]
  38.                 If A=255
  39.                         Return True
  40.                 End If
  41.                 C = A  
  42.                 B = 0
  43.                 A = 0
  44.                 D = data[IY+1]
  45.                 E = data[IY+2]
  46.                 Repeat              
  47.                         Repeat                    
  48.                                 OUT_RING_ONE(254,A)    ' <-------------- NEW APPROACH
  49.                                 D :- 1
  50.                                 If D=0
  51.                                         D = data[IY+1]
  52.                                         A = A~24  
  53.                                 End If
  54.                                 E :- 1
  55.                                 If E=0
  56.                                         E = data[IY+2]                        
  57.                                 End If
  58.                                 B :- 1                         
  59.                         Until B=0
  60.                         C :- 1                 
  61.                 Until C=0
  62.                 IY :+ 3
  63.                 Print "Step in DATA=" +  iy
  64.         Wend
  65. End Function
  66.  
  67.  
  68.  
  69.  
  70. Function OUT_RING_ONE(Port%, Value%)
  71.         Select Value & 8
  72.                 Case 0
  73.                         RingBuffer.SendONE 50
  74.                 Case 8
  75.                         RingBuffer.SendONE 200
  76.         End Select
  77. End Function
  78.  


And this is the new RingBufferClass for BlitzMax 1.50:


Code: BlitzMax
  1. Type RingBufferClass
  2.         ' RingBufferClassVanilla.bmx
  3.         ' A permant running Audio-Output using FreeAudio
  4.         ' --------------------------------------------------
  5.         ' Author: Midimaster at www.midimaster.com
  6.         ' V1.1 2021-07-14
  7.         ' see examples of use at https://www.syntaxbomb.com/index.php/topic,8377.0.html
  8.         ' -------------------------------------------------------------------
  9.         ' minimal example:
  10.         '   RingBufferClass.SetDriver("FreeAudio....")
  11.         '   Global RingBuffer:RingBufferClass = New RingBufferClass
  12.         '   RingBuffer.CreateNew(HERTZ, FORMAT)
  13.         '   SendSize:INT  = RingBuffer.IntervalSamples()
  14.         '   SendTime:INT = RingBuffer.IntervalTime()
  15.         '   Local WatchThread:TThread=CreateThread(WatchLoop, "")
  16.         '   Global WriteTime = MilliSecs()
  17.         '   Function SendSamples
  18.         '               If WriteTime>MilliSecs() Return
  19.         '               WriteTime =WriteTime + SendTime
  20.         '               Local AudioArray:Int[SendSize]
  21.         '               For Local i:Int=0 To SendSize-1
  22.         '                       AudioArray[i] = any value....
  23.         '               Next
  24.         '               RingBuffer.Send AudioArray
  25.         '   End Function
  26.  
  27.         Global MyDriver$
  28.         Global BufferMutex:TMutex=CreateMutex()
  29.  
  30.         Field CHANNELS:Int, CHUNK_SIZE:Int, BUFFER_SAMPLES:Int, BUFFER_SIZE:Int
  31.         Field FORMAT:Int, CHUNK_TIME:Int, HERTZ:Int, BITS:Int
  32.         Field WritePointer:Int, ReadPointer:Int, RingPointer:Int
  33.         Field WriteTime:Int, WatchTime:Int, InFormat%
  34.  
  35.         Field RingBuffer:TAudioSample, InBuffer:TAudioSample, Sound:TSound
  36.         Field RingStream:TStream, InBufferStream:TStream
  37.        
  38.  
  39.  
  40.         Function SetDriver(Driver$)
  41.         ' PUBLIC: Use this to...
  42.                 ' select one of the audio drivers. It needs to be FreeAudio
  43.                 ' on Windows needs to be FreeAudio DirectSound
  44.                 If MyDriver<>"" Return
  45.                 If Driver.contains("FreeAudio")=False
  46.                         Notify "wrong AudioDriver"
  47.                         End
  48.                 EndIf
  49.                 MyDriver = Driver
  50.                 SetAudioDriver(MyDriver)
  51.         End Function
  52.        
  53.  
  54.        
  55.         Method CreateNew(Hertz%, UserFormat%=SF_STEREO16LE , Latency%=8)
  56.         ' PUBLIC: Use this to define the Ringbuffer...
  57.                         ' HERTZ should be a multiple of 1000 for CHUNK_TIME=10, 20, 40 or 50
  58.                         ' HERTZ can also be 44100 when CHUNK_TIME=20 or 40
  59.                         '
  60.                         ' UserFormat can be SF_MONO8 or SF_STEREO8 or SF_MONO16LE or SF_STEREO16LE
  61.                         '
  62.                         ' LATENCY can be from 1 to 32
  63.                         ' 2=extrem small, 4=normal size,  8-32..=secure sizes
  64.                         '      
  65.                         If MyDriver=""
  66.                                         Notify "No AudioDriver selected"
  67.                                         End
  68.                         EndIf
  69.                         Self.HERTZ=Hertz
  70.                         Self.FORMAT=SF_STEREO16LE
  71.                         Self.InFormat=UserFormat
  72.                         DefineBuffer Latency
  73.                         ClearBuffer
  74.                         WatchTime=MilliSecs()
  75.                         PlaySound Sound
  76.         End Method
  77.  
  78.  
  79.  
  80.         Method IntervalSamples:Int()
  81.         ' PUBLIC: Use this to...
  82.                 ' inform how many samples you should send each call    
  83.                 If  (InFormat=SF_MONO8) Or (InFormat=SF_MONO16LE)
  84.                         Return CHUNK_SIZE/4
  85.                 Else
  86.                         Return CHUNK_SIZE/2
  87.                 EndIf
  88.         End Method
  89.  
  90.  
  91.        
  92.         Method IntervalTime:Int()
  93.         ' PUBLIC: Use this to...
  94.                 ' inform how long you should wait between calls (in msecs)     
  95.                 Return CHUNK_TIME
  96.         End Method      
  97.  
  98.  
  99.  
  100.        
  101.         Method SendOne(Value:Int)
  102.         ' PUBLIC: Use this to...
  103.                 ' send one single sample value to the ringbuffer
  104.                 Global ShortCollector:Short[IntervalSamples()*2]
  105.             Global CollektorCounter:Int
  106.  
  107.                 Select InFormat
  108.                         Case SF_MONO8                                    
  109.                                 Value=(Value-128)*128
  110.                                 ShortCollector[CollektorCounter]=Value
  111.                                 ShortCollector[CollektorCounter+1]=Value
  112.                                 CollektorCounter=CollektorCounter+2
  113.                         Case SF_STEREO8                                          
  114.                                 Value=(Value-128)*128
  115.                                 ShortCollector[CollektorCounter]=Value
  116.                                 CollektorCounter=CollektorCounter+1
  117.                         Case SF_MONO16LE
  118.                                 ShortCollector[CollektorCounter]=Value
  119.                                 ShortCollector[CollektorCounter+1]=Value
  120.                                 CollektorCounter=CollektorCounter+2
  121.                         Case SF_STEREO16LE
  122.                                 ShortCollector[CollektorCounter]=Value
  123.                                 CollektorCounter=CollektorCounter+1
  124.                 End Select
  125.                 If CollektorCounter=IntervalSamples()*2
  126.                         CheckBufferOverRun
  127.                         Transfer ShortCollector
  128.                 CollektorCounter=0
  129.                 EndIf
  130.         End Method
  131.  
  132.  
  133.  
  134.  
  135.          Method Send(AudioArray:Int[])
  136.         ' PUBLIC:  Use this to...
  137.                 ' send a couple of samples value to the ringbuffer
  138.                 Local ShortArray:Short[]
  139.                 Local i%, v%
  140.                 Select InFormat
  141.                         Case SF_MONO8                                    
  142.                                         ShortArray= New Short[AudioArray.Length*2]
  143.                                         For i=0 To AudioArray.Length-1
  144.                                                 v=AudioArray[i]
  145.                                                 V=V-128
  146.                                                 ShortArray[2*i]=v*128
  147.                                                 ShortArray[2*i+1]=v*128
  148.                                         Next
  149.                         Case SF_STEREO8
  150.                                         ShortArray= New Short[AudioArray.Length]
  151.                                         For i=0 To AudioArray.Length-1
  152.                                                 v=AudioArray[i]
  153.                                                 V=V-128
  154.                                                 ShortArray[i]=v*128
  155.                                         Next
  156.                         Case SF_MONO16LE
  157.                                         ShortArray= New Short[AudioArray.Length*2]
  158.                                         For i=0 To AudioArray.Length-1
  159.                                                 ShortArray[2*i]=AudioArray[i]
  160.                                                 ShortArray[2*i+1]=AudioArray[i]
  161.                                         Next
  162.                         Case SF_STEREO16LE
  163.                                          ShortArray= New Short[AudioArray.Length]
  164.                                         For i=0 To AudioArray.Length-1
  165.                                                 ShortArray[i]=AudioArray[i]
  166.                                         Next
  167.                 End Select
  168.                 CheckBufferOverRun
  169.                 Transfer ShortArray
  170.         End Method     
  171.  
  172.  
  173. '
  174. '   E N D   O F   T H E   P U B L I C   F U N C T I O N S
  175. '
  176. ' ***************************************************************************
  177. '
  178. '    I N T E R N A L   F U N C T I O N S :
  179.  
  180.         Method Watch()
  181.         ' private: called by the thread WatchLoop(), cares about transfering ringbuffer content to the audio device
  182.                 If WatchTime<MilliSecs()
  183.                         WatchTime = WatchTime + CHUNK_TIME
  184.                         SendOneChunk
  185.                 EndIf
  186.         End Method
  187.                
  188.          Method DefineBuffer(Latency%)
  189.          ' private
  190.                         CHUNK_TIME=20
  191.                         BITS=16
  192.                         CHANNELS=2
  193.                         CHUNK_SIZE     = HERTZ * CHUNK_TIME * CHANNELS * BITS/8/1000
  194.                         BUFFER_SAMPLES = 4*Latency * HERTZ * CHUNK_TIME             /1000
  195.                         BUFFER_SIZE    = BUFFER_SAMPLES * BITS/8 *CHANNELS
  196.                        
  197.                         RingBuffer     = CreateAudioSample(BUFFER_SAMPLES, HERTZ, FORMAT)
  198.                         InBuffer       = CreateAudioSample(BUFFER_SAMPLES, HERTZ, FORMAT)
  199.                         Local fa_sound:Int  =  fa_CreateSound( BUFFER_SAMPLES-1, BITS, CHANNELS, HERTZ, RingBuffer.Samples, $80000000 )
  200.                         Sound          = TFreeAudioSound.CreateWithSound( fa_sound, Null)
  201.                         RingStream     = CreateRamStream(RingBuffer.Samples , Buffer_size,True,True)
  202.                         InBufferStream = CreateRamStream(InBuffer.Samples,Buffer_size,True,True)
  203.                         RingPointer    =  BUFFER_SIZE/4
  204.         End Method
  205.  
  206.          Method SendOneChunk()
  207.          ' private
  208.                 LockMutex BufferMutex
  209.                         Local ReadPointerMod% = ReadPointer Mod BUFFER_SIZE
  210.                         InBufferStream.Seek ReadPointerMod
  211.                         RingStream.Seek RingPointer
  212.                         Local i%
  213.                         If ReadPointer + CHUNK_SIZE > WritePointer
  214.                                 Local Maxi%=WritePointer-ReadPointer
  215.                                 For i = 0 To Maxi-1
  216.                                                 RingStream.WriteByte InBufferStream.ReadByte()
  217.                                 Next
  218.                                 For i = Maxi To CHUNK_SIZE-1
  219.                                                 RingStream.WriteByte 0
  220.                                 Next
  221.                                 ReadPointer=ReadPointer + Maxi
  222.                         Else
  223.                                 For i = 0 To CHUNK_SIZE-1
  224.                                         RingStream.WriteByte InBufferStream.ReadByte()
  225.                                 Next
  226.                                 ReadPointer=ReadPointer + CHUNK_SIZE
  227.                         EndIf
  228.                         RingPointer=(RingPointer + CHUNK_SIZE) Mod (BUFFER_SIZE)
  229.                 UnlockMutex BufferMutex
  230.         End Method
  231.  
  232.          Method ClearBuffer()
  233.          ' private
  234.                         For Local i:Int =0 To BUFFER_SIZE
  235.                                 RingBuffer.Samples[i]=0
  236.                         Next   
  237.         End Method
  238.        
  239.          Method Transfer(ShortArray:Short[])
  240.          ' private
  241.                 LockMutex BufferMutex
  242.                 Local WritePointerMod% = WritePointer Mod BUFFER_SIZE
  243.                 InBufferStream.Seek WritePointerMod
  244.            Local i%
  245.                 If ShortArray.Length*2 + (WritePointerMod) <= BUFFER_SIZE
  246.                         For i=0 To ShortArray.Length-1
  247.                                 InBufferStream.WriteShort ShortArray[i]
  248.                         Next                   
  249.                 Else
  250.                
  251.                         Local Maxi% = BUFFER_SIZE-WritePointerMod
  252.                        
  253.                         For i=0 To Maxi/2-1
  254.                                 InBufferStream.WriteShort ShortArray[i]
  255.                         Next
  256.                         InBufferStream.Seek 0
  257.                         For i=Maxi/2 To ShortArray.Length-1
  258.                                 InBufferStream.WriteShort ShortArray[i]
  259.                         Next
  260.                 EndIf
  261.                 WritePointer=WritePointer + ShortArray.Length*2
  262.  
  263.                 UnlockMutex BufferMutex
  264.         End Method
  265.        
  266.         Method CheckBufferOverRun()
  267.         ' private  cares about buffer overruns and report if you send to fast
  268.                 Local grade%
  269.                         Local diff%=WritePointer-Readpointer
  270.                         diff=diff*100/BUFFER_SIZE
  271.                        
  272.                         If diff>80
  273.                                 Print "RINGBUFFER: Prevent Buffer Overrun! Wait for " + IntervalTime() + "msec"
  274.  
  275.                                 Delay IntervalTime()
  276.                                 'Print WritePointer + " " + Readpointer + " " + (WritePointer-Readpointer) + " " + BUFFER_SIZE
  277.                                 'Print diff + "%"
  278.                         EndIf
  279.         End Method
  280. End Type
  281.  
  282.  
« Last Edit: July 14, 2021, 08:04:19 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: 196
Re: Creating ZX SOUND
« Reply #83 on: July 14, 2021, 09:06:17 »
Oh my! Using Blitzmax 1.5

Ive Saved the RingBufferClass to  "C:\Blitzmax\tmp\RingBufferClassVanilla.bmx"

Ive Locked as a build file "C:\Blitzmax\tmp\Music.bmx"

When i Compile i get theses errors from the Output window?

Building Music.tmp
Compiling:RingBufferClassVanilla.bmx
Compile Error: Identifier 'TMutex' not found
[C:/BlitzMax/tmp/RingBufferClassVanilla.bmx;29;13]
Build Error: failed to compile C:/BlitzMax/tmp/RingBufferClassVanilla.bmx
Process complete

Do i need some other files? as Identifier Tmutex isnt there.  :-[

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.    Resistance Is Futile! The code will be assimulated!

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 364
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #84 on: July 14, 2021, 09:20:04 »
you have to build as "Threaded Build", because the Ringbuffer uses MultiThreading
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Baggey

  • Full Member
  • ***
  • Posts: 196
Re: Creating ZX SOUND
« Reply #85 on: July 14, 2021, 09:32:16 »
you have to build as "Threaded Build", because the Ringbuffer uses MultiThreading

Okay havent got a Clue what Multithreading is  :o

Ive Selected 'Program/Buid Options and Checked the "Threaded Build" also still have "Quick Build" Checked as usual.

It Works!  8)

So need to Play with this now as the Music Loop being played hasn't got all the Z80 Emulation behind it. It Should Sound "Screechy"
Thankyou so much for this new Approach. I shall in due course adapt to the Emulator and see how this effects music and in game bullets and jumps etc...  ;D

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.    Resistance Is Futile! The code will be assimulated!

Offline Baggey

  • Full Member
  • ***
  • Posts: 196
Re: Creating ZX SOUND
« Reply #86 on: July 14, 2021, 20:30:45 »
Thanks to MidiMaster,

I have been playing around, with a new way of Playing ZX Sound's using "Mini Z80 MachineCode Emulator" to send Bytes to MidiMasters "RingBuffer".

Note, The Z80 Mock up code is the full Machine Code Routine in Manic Miner. So we can hear the Cllasic Screechy! 'Manic Miner' Sound as origonaly Played/heard. 8)

This is Executable Code in Blitzmax 1.5 A.K.A "Vanilla" You will need to use the drop down Menu  Program-Build Options- and Check/Tick "Threaded Build" to compile.

The "RingBufferClassVanilla.bmx" file needs to be in the same Folder as this Code.

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

So Hopefully This is a Much quicker way of playing Sounds So in Game Jigs, Explosions, Bullets, and really Quick sound Effects will work now!
Not just for Manic Miner but for all Beeper Sound produced by the Piezo Speaker in the ZX Spectrum. Hopefully this will progress to the AY Chip It's self. ::)

Next Step to Integrate this Mock up Code into "SpecBlitz" now! Finggers crossed.  :D

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.    Resistance Is Futile! The code will be assimulated!

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 364
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #87 on: July 15, 2021, 05:24:30 »
You need not to stay at the 40.000Hz and can also adjust them in the definition...
Code: [Select]
  RingBuffer.CreateNew(40000, SF_MONO8)... to reach the original pitch of the vintage computers.

I also see, that you alreay added a counter TCycles for simulating the processor speed. If this is adjusted correct and works also the RingBuffer should work without using its "Emergency Break"
Code: BlitzMax
  1.         Method CheckBufferOverRun()
  2.         ' private  cares about buffer overruns and report if you send to fast
  3.                 Local grade%
  4.                         Local diff%=WritePointer-Readpointer
  5.                         diff=diff*100/BUFFER_SIZE
  6.                        
  7.                         If diff>80
  8.                                 Print "RINGBUFFER: Prevent Buffer Overrun! Wait for " + IntervalTime() + "msec"
  9.  
  10.                                 Delay IntervalTime()
  11.                                 'Print WritePointer + " " + Readpointer + " " + (WritePointer-Readpointer) + " " + BUFFER_SIZE
  12.                                 'Print diff + "%"
  13.                         EndIf
  14.         End Method
  15.  

Please have a look on the DEBUG window. Whenever you see the notice..
Code: [Select]
RINGBUFFER: Prevent Buffer Overrun! Wait for 20 msec...you are feeding the Ringbuffer to fast.

See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Baggey

  • Full Member
  • ***
  • Posts: 196
Re: Creating ZX SOUND
« Reply #88 on: July 16, 2021, 17:59:37 »
I am getting more sucess with the RingBuffer. I have theme tunes working. ie Game Music and in Game Jigs.

However, Not all explosions or bullets are heard and some in game music seems like some of the byte's being sent get lost or missed somehow?

I see you've mentioned writing to the buffer to fast can happen. I supouse this could be why in game bullets and explosions aren't being recognized.

I am getting this message sometimes. RINGBUFFER: Prevent "Buffer Overrun! Wait for 20 msec"

Ive already tried ajusting the 40Khz down to 22,100Hz but then emulation runs very slow and laggy.

Im going to try and not use the CheckBufferOverRun() Function and see what effect that has.

Ive created a you tube channel last night  :o  and waiting for confirmation to use it i think  ::)

I want to try and post a video to demonstrate what's happening with the Sound when i run full throttle and at FullScreen 3.5Mhz speed.

Im also still trying to iron out some rather stuborn bugs as well!

Hopefully soon  :o

Kind Regards Baggey
« Last Edit: July 16, 2021, 18:07:33 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.    Resistance Is Futile! The code will be assimulated!

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 364
    • Midimaster Music Education Software
Re: Creating ZX SOUND
« Reply #89 on: July 16, 2021, 18:39:22 »
The datas were written in the 70th exactly for the speed of the vintage hardware. If you now run your emulator on a extrem fast computer you have to care about the simulation of the vintage processor speed.

A first sign for you, that your emulator runs to fast is now my message "Prevent Buffer Overrun! Wait for 20 msec"

When you see this you can be sure, that your simulator runs to fast compared with the vintage original hardware. In a perfect tuned simulator you would not see this message.



Reducing the kHz now only has the effect, that the songs sound lower. First try to find the best fitting kHz (39kHz or 38kHz or ...) and then have a look on the overrun messages.

With the new overrun protection no sounds can be lost. Because the simulator would prefer waiting  for 20msec instead of loosing datas. Switching of the overrun protection serves nothing: Sounds will get lost, because the problem of the to high simulator speed is still there.

Could you show the code where you reduce the processor speed to the vintage speed. Or is this secret? I'm sure you forgot some TCycles-commands. What about this NOPs?

3.5Mhz would mean 3500 TCyles per millisecs. but are you sure that all 3500000 TCycles had really been avaiable for the vintage computers in those days too? Maybe the system needed 10%-30% of the performance also when a game was running?






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