violin synth works, sort of... note response too slow, different approach needed?

moorea21

Senior Member
Finally got this code to work... and it's very laggy indeed when notes are called that require the processor to run slowly and/ or pwm DIV to equal a low value:-

Code:
'violin synth with 4 strings, too slow

'G string C.4 pin6
'D string B.3 pin15
'A string C.0 pin10
'E string B.2 pin16
'pwm1 = C.2 pin5
'adc = C.7 pin3

'           G1        G#1       A2        Bb2
EEPROM   0,(m4,64,78, m4,64,75, m4,64,70, m4,64,66)

'            B2         C2         C#2        D2
EEPROM   12,(m4,16,252, m4,16,238, m4,16,224, m4,16,212)

'           Eb2        E2         F2         F#2
EEPROM  24,(m4,16,200, m4,16,189, m4,16,178, m4,16,168)	

'           G2         G#2        A3         Bb3
EEPROM  36,(m4,16,158, m4,16,150, m4,16,141, m4,16,133)

'           B3        C3        C#3        D3
EEPROM  48,(m2,4,252, m2,4,238, m4,16,112, m2,4,212)

'           Eb3       E3        F3        F#3	
EEPROM  60,(m2,4,200, m4,16,94, m2,4,178, m2,4,168)

'           G3        G#3       A4        Bb4
EEPROM  72,(m2,4,159, m2,4,150, m2,4,141, m4,16,66)

'           B4        C4        C#4       D4
EEPROM  84,(m4,4,252, m4,4,238, m4,4,224, m4,4,212)

'            Eb4       E4        F4        F#4
EEPROM   96,(m4,4,200, m4,4,189, m4,4,178, m4,4,168)

'             G4        G#4       A5        Bb5
EEPROM   108,(m4,4,158, m4,4,150, m4,4,141, m4,4,133)

'            B5        C5        C#5       D5
EEPROM  120,(m8,4,252, m8,4,238, m4,4,112, m8,4,212)

'            Eb5       E5       F5        F#5
EEPROM  132,(m8,4,200, m4,4,94, m8,4,178, m8,4,168)

'            G5        G#5       A6       Bb6
EEPROM  144,(m8,4,159, m8,4,150, m4,4,70, m4,4,66)


main:
	b2 = 0
	
	high C.4	; starting with G string notes
	low B.2
	b2 = 1
	gosub readAdcVal

	low C.4	; D string notes
	high B.3
	b2 = 2
	gosub readAdcVal

	low B.3	; A string notes
	high C.0

	b2 = 3
	gosub readAdcVal

	low C.0	; E string notes
	high B.2
	b2 = 4
	gosub readAdcVal
goto main


readAdcVal:	
	readadc c.7,b0	'b0 is the adc value
	If b0 > 4 Then
		b1 = 12
		do
			If b0 < b1 then lookUpData
       		if b1 > 244 then exit
			b1 = b1 + 8
		loop
	Else
		'pwmout pwmdiv4,C.2,0,0
		return
	End If

lookUpData:
	b0 = 0
	select case b2
	case 1
	b1 = b1- 12: b1 = b1 * 3 / 8: goto playNote
	case 2
	b1 = b1 - 12: b1 = b1 * 3 / 8: b1 = b1 + 21: goto playNote
	case 3
	b1 = b1 - 12: b1 = b1 * 3 / 8: b1 = b1 + 42: goto playNote
	case 4
	b1 = b1 - 12: b1 = b1 * 3 / 8: b1 = b1 + 63: goto playNote
	else
	end select

playNote:
	read b1, b10, b11, b12	'read data from the next 3 bytes of eeprom, starting from address that has same value as b1
	b13 = b12 * 2
	select case b11
	  case  4 : setfreq b10: pwmout pwmdiv4,  C.2, b12, b13
	  case 16 : setfreq b10: pwmout pwmdiv16, C.2, b12, b13
	  case 64 : setfreq b10: pwmout pwmdiv64, C.2, b12, b13
	end select
return
I will have to produce the notes in a different way; with the processor speed set as high as possible to minimise lag due to processing time. I could use picaxe 'tune', although from memory that has a range of 18 notes only; I'd need some sort of frequency divider/multiplier circuit to extend that to the 4 1/2 octaves (52 notes) I need for this project. I have no idea how that could be acheived in a small package, using the picaxe's non square wave sound output.

Does anyone have any other/ better suggestions for how to produce 4 1/2 octaves of accurately (ish) tuned sound out of a picaxe? An mp3 of each note? Midi? Out of my depth a bit at this point.
 

Buzby

Senior Member
Yes, there is a trade-off between needing high clock speed for fast response, but needing a lower clock when generating notes.

I'd be tempted to use two chips, ( well, they are "as cheap as chips" ), with one dedicated to tone generation and the other to scanning and selection.

The tone gen PICAXE could even run three PWMs at once for a nice rich sound, while the scanning chip just sends simple note commands via serout/serin.

This solution also makes it easier to test each part individually.

Worth a punt ?.

Cheers,

Buzby
 

techElder

Well-known member
Sorry if I'm way off the reservation, but it seems like you are sequentially looking at each string whether there is a note active or not. That would be a lot of time spent looking at nothing (unless you are a very fast player.)

What I can't find is a setfreq freq command for the whole program. Are you running this program at the default frequency? (I can see the frequency adjustments to get the PWM notes right.)
 

AllyCat

Senior Member
Hi,

it's very laggy indeed .
An ON b2 GOTO should be significantly faster than a SELECT b2 ... CASE and shouldn't need much modification: Note that the first ON ... GOTO label is for b2 = zero, but the fastest workaround may be to simply start with a "dummy" (unused) label. Also, b1 = b1 - 12 ** 24576 would be a little faster than b1 = b1- 12 : b1 = b1 * 3 / 8 (note 3/8 = 24576 / 65536).

You already seem to have avoided using PWMDUTY which is much slower than PWMOUT. Often, the "speed" of these constructs can be easily checked by seeing how many (extra) bytes of program code are created by the Program Editor.

However, I suspect the "laggy" part is :
Code:
		do
			If b0 < b1 then lookUpData
			if b1 > 244 then exit
			b1 = b1 + 8
		loop
You should be able to replace that with a simple mathematical expression assigning b1 directly from b0 * 8 , (and a couple of IFs).

Cheers, Alan.
 

moorea21

Senior Member
Thanks both, I think premelec's suggestion sounds neat, but complicated for someone with my experience, unless its been done before and I can just more or less copy it. I like the idea of dedicating 1 chip to sound production though, although I've not used 2 picaxes talking to each other before. I've looked through completed projects here, not found anything that suggests a way. Can anyone suggest a good thread to look at?

I found this, in www.picaxe.com/FAQs/Interfacing/

Code:
        let dirsb = $FF
main:   serin C.0, N2400, b1
        let pinsb = b1
        goto main
Which is programmed into a receiving chip.

Does the line 'let pinsb = b1' mean 'let all the pins starting with B on the chip at address $FF (this one) be controlled by the value of variable b1, as received on pin C.0'?

Apparently the outputting chip will control it with a line like;
serout C.7,N2400,(b2) 'pinsb' is the value (0 to 255) desired on the second chip

But I don't understand what form that might take, and how the second chip is supposed to decide from this which of its 8 outputs pins to send high or low...
 

moorea21

Senior Member
Tex: Yes, there is no setfreq for the program, it just runs at whatever speed the pwm last ran at.
I couldnt think of another way to do this apart from as you put it 'sequentially looking at each string whether there is a note active or not.' Is there another approach?

Alan: 'You should be able to replace that with a simple mathematical expression assigning b1 directly from b0 * 8 , (and a couple of IFs).'...
I have no idea what that would look like; how would that be done?

I'd assumed the looking up values inthe eeprom would be the bottleneck.
 

premelec

Senior Member
There must be some chips which generate tones or take a bit of data and turn it into a wave considering all the generation of sound devices we have. it might be good to contact manufacturers and ask... I found some complicated ones searching "audio codec" at TI.com usually with an amplifier in it as well as the DAC etc... Or look for an addressable audio device which has stored the wave files. Sorry it's not my field - find someone who knows... ;-0

If you could find a cheap kid's electronic keyboard you could arrange using a PICAXE to switch what fingers used to... I just looked on Ebay and there's a 1 octave unit for $2 and many units for less than $14 for kids... I may have lost track of your project entirely so I'll be playing a bunch of syncopated silences now... ;-0
 
Last edited:

lbenson

Senior Member
What physically are C.2 and C.7 connected to?

Is the chip a 20M2 or a 20X2 (I note the comment that B.2 is pin 16)?

lookupdata can be simplified to this:
Code:
lookUpData:
	b0 = b2 - 1 * 21 ' result of 0, 21, 42, or 63 for 1,2,3,4
	b1 = b1- 12 * 3 / 8 + b0
	b0 = 0
  ' fall through to playNote
(I don't know if you need to test for b2 > 0 and < 5.)
 
Last edited:

lbenson

Senior Member
If reading from eeprom is the cause of the lag, perhaps shifting that data to upper ram and reading with "peek" will help.

This attempts to do that. When running in simulation I can see that the eeprom data has been moved to the corresponding ram address starting at 100 (so occupying 100-255). In simulation, it appears to me that the right values are being placed into b10, b11, and b12 (using peek). All runs with "setfreq 32" except for the pwm statements.
Code:
' 20violinsynth violin synth with 4 strings, too slow
#picaxe 20M2

'G string C.4 pin6
'D string B.3 pin15
'A string C.0 pin10
'E string B.2 pin16
'pwm1 = C.2 pin5
'adc = C.7 pin3

'           G1        G#1       A2        Bb2
EEPROM   0,(m4,64,78, m4,64,75, m4,64,70, m4,64,66)

'            B2         C2         C#2        D2
EEPROM   12,(m4,16,252, m4,16,238, m4,16,224, m4,16,212)

'           Eb2        E2         F2         F#2
EEPROM  24,(m4,16,200, m4,16,189, m4,16,178, m4,16,168)	

'           G2         G#2        A3         Bb3
EEPROM  36,(m4,16,158, m4,16,150, m4,16,141, m4,16,133)

'           B3        C3        C#3        D3
EEPROM  48,(m2,4,252, m2,4,238, m4,16,112, m2,4,212)

'           Eb3       E3        F3        F#3	
EEPROM  60,(m2,4,200, m4,16,94, m2,4,178, m2,4,168)

'           G3        G#3       A4        Bb4
EEPROM  72,(m2,4,159, m2,4,150, m2,4,141, m4,16,66)

'           B4        C4        C#4       D4
EEPROM  84,(m4,4,252, m4,4,238, m4,4,224, m4,4,212)'

'            Eb4       E4        F4        F#4
EEPROM   96,(m4,4,200, m4,4,189, m4,4,178, m4,4,168)

'             G4        G#4       A5        Bb5
EEPROM   108,(m4,4,158, m4,4,150, m4,4,141, m4,4,133)

'            B5        C5        C#5       D5
EEPROM  120,(m8,4,252, m8,4,238, m4,4,112, m8,4,212)

'            Eb5       E5       F5        F#5
EEPROM  132,(m8,4,200, m4,4,94, m8,4,178, m8,4,168)

'            G5        G#5       A6       Bb6
EEPROM  144,(m8,4,159, m8,4,150, m4,4,70, m4,4,66)

  setfreq m32
  pause 2000 ' allow frequency change to settle
  b2 = 0     ' start of eeprom
  bptr = 100 ' start of upper ram for note data
  for b1 = 1 to 13
    ' move 12 bytes of data to upper ram--13 times
    read b2,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc
    b2 = b2 + 12
  next b1
 
main:
	b2 = 0
	
	high C.4	; starting with G string notes
	low B.2
	b2 = 1
	gosub readAdcVal

	low C.4	; D string notes
	high B.3
	b2 = 2
	gosub readAdcVal

	low B.3	; A string notes
	high C.0

	b2 = 3
	gosub readAdcVal

	low C.0	; E string notes
	high B.2
	b2 = 4
	gosub readAdcVal
goto main

readAdcVal:	
	readadc c.7,b0	'b0 is the adc value
	If b0 > 4 Then
		b1 = 12
		do
			If b0 < b1 then lookUpData
       		if b1 > 244 then exit
			b1 = b1 + 8
		loop
	Else
		'pwmout pwmdiv4,C.2,0,0
		return
	End If

lookUpData:
  b0 = b2 - 1 * 21 ' result of 0, 21, 42, or 63
  b1 = b1- 12 * 3 / 8 + b0
	b0 = 0
  ' fall through to playNote

playNote:
'	read b1, b10, b11, b12	'read data from the next 3 bytes of eeprom, starting from address that has same value as b1
  b1 = b1 + 100    ' adjust for location in ram starting at 100
	peek b1, b10, b11, b12	'read data from the next 3 bytes of ram, starting from address that has same value as b1
	b13 = b12 * 2
	select case b11
	  case  4 : setfreq b10: pwmout pwmdiv4,  C.2, b12, b13
	  case 16 : setfreq b10: pwmout pwmdiv16, C.2, b12, b13
	  case 64 : setfreq b10: pwmout pwmdiv64, C.2, b12, b13
  end select
  setfreq m32
return
I don't know exactly what is happening with the inputs and the outputs, so can't test further. Sometimes a brief pause seems to be required after setfreq to allow time for the speed change to settle--only testing would show whether you need it or not.
 

moorea21

Senior Member
It's a 20m2, pin C.2 is a piezo buzzer, the adc pin C.7 , see attached. The 31 'frets' are the junctions between resistors; the values are arranged to give very close to 8 adc units between each one.log adc input.jpg
 

AllyCat

Senior Member
Hi,

Code:
do
		If b0 < b1 then lookUpData
		if b1 > 244 then exit
		b1 = b1 + 8
		loop
You should be able to replace that with a simple mathematical expression assigning b1 directly from b0 * 8 ,...

I have no idea what that would look like; how would that be done?
That loop may execute up to about 30 times with 4 instructions per pass, each taking around 1 ms, so could be adding > 100 ms delay. However, it appears that "all" it is doing as calculating b1 such that it is a value of between 1 and 8 greater than b0, in steps of 8 above 12. The "offset" of 12 (not a multiple of 8) makes it a little "messy" but I think this pure mathematical expression will do the same thing (untested):

b1 = b0 - 4 / 8 * 8 + 12

Because PICaxe does this with integers from left to right, the division and then multiplication by 8 does do something useful.

Or using a logical operator is a little faster, for example I think the following also should work:

b1 = b0 - 4 ANDNOT 7 + 12

"ANDNOT 7" is a little unusual but its function might be more obvious than ANDing the equivalent "magic number" 248, $F8 or %11111000 .

Also, I believe the "exit" and "lookUpData" both jump to the same place, so the "if b1 > 244 then exit" might be replaced by a MAX 252 at the end of the expression(s) for b1 above.

Cheers, Alan.
 

moorea21

Senior Member
Thanks Alan, that looks like genius level stuff from here! All works too.

I still think the main problem is the lag caused by the program needing to run slowly for certain notes; the response is fine for notes with 'setfreq m4' and 'Div16'; its only awful when its 'setfreq m2' and 'Div4' as far as I can tell, hence thinking about junking the whole 'pwm makes notes' thing if possible.

I'm leaning more towards the idea of premelecs off board 'sound chip' really, as I think I'd have the same response problems from using a 2nd picaxe chip running at whatever setfreq and pwmdiv settings it is told to in order to 'pwm' the note.

Looking for an arduino project for something like this, I looked on google, found this odd little thing:- https://www.jameco.com/Jameco/Products/ProdDS/2110991.pdf

Can be bought here, apparently:
http://www.active-robots.com/soundpal.html
For £15 UK... a bit steep for a machine that goes beep
 
Last edited:

hippy

Technical Support
Staff member
I still think the main problem is the lag caused by the program needing to run slowly for certain notes; the response is fine for notes with 'setfreq m4' and 'Div16'; its only awful when its 'setfreq m2' and 'Div4' as far as I can tell, hence thinking about junking the whole 'pwm makes notes' thing if possible.
That is probably where the problem rests, reducing operating frequency is reducing responsiveness.

What you might need is a three PICAXE solution so you can have one determining which string was held against which fret, passing that information or a note value to a second via serial, which in turn produces parallel output data for a third which generates the actual tones.

Changing frequency in the tone generating module will change baud rates so trying to do serial input with that would get complicated.

You could use one PICAXE as you are currently have, and generate MIDI output as you suggested.

Code:
; Read string and fret
HSerSetup ...
Do
  Gosub DetermineString
  ; b0 = string 0-3
  ; b1 = fret   0-31
  b2 = b0 * 32 | b1
  HSerOut 0,(b2)
Loop
Code:
; Convert serial to parallel
HSerSetup ...
Do
  w0 = -1
  Do
    HSerIn w0
  Loop Until b1 = 0
  PinsB = b0
Loop
Code:
; Generate tone
Do
  b2 = PinsC
  If b2 <> b3 Then
    b3 = b2
    b0 = b2 / 32 ; string 0-3
    b1 = b2 & 31 ; fret   0-31
    Gosub SetPwmOut
  End If
Loop
You could probably replace that second PICAXE with a shift register and use SPI.

Actually there might be a way to push SPI direct from the first to the third but that would require some prototyping.
 
Last edited:

moorea21

Senior Member
Thanks hippy,
I'm starting to have sacreligious thoughts, like 'this would probably be really easy with an arduino...'
Seriously though, something that already has lots of sound libraries written for it, and costs about the same as a picaxe chip may be the sensible way to go, if there isnt some chip out there that does the 'soundpal's job at 1/10th the price. That one seems a little steep for something that probably has no history of being used with picaxe chips anyway, and hence will probably need more work than arduino-ing it. Although having never written anything in 'C', I wouldnt know what I was getting into there either.
 

hippy

Technical Support
Staff member
I'm starting to have sacreligious thoughts...
The main problem is that you cannot easily choose the range of frequencies you want to produce with a PICAXE due to its hardware design, and overcoming that introduces sluggishness. You would probably encounter the same issues with any other native micro but you could bit-bang audio frequency PWM rather than rely on hardware PWM.

You may be able to do that using a PICAXE-X2 and SETTIMER or by fiddling with SFR and timer settings. I haven't really investigated that so maybe not.

There may be means to improve the responsiveness in your checking for which string/fret is pressed even at lower SETFREQ M2 speeds. It seems to me that you are doing a lot of unnecessary processing in detecting what note to play. Perhaps post your full code, details of what ADC values each string/fret combination produces.

Also post a full circuit diagram. It's not easy to see why you are doing certain things with your output pins, why the ADC values seem to be different depending on which string you are accessing.
 
Last edited:

moorea21

Senior Member
That is all the code! Below are the adc values for each fret, and the note values/fret numbers for the G string. The D, A, and E strings are a '5th' up from the Gstring, hence adding 21, 42, and 63 to the basic 'find the right eeprom address' code in lookUpData.

Code:
'b0 = the 31 adc values (from 7 to 250, although the higher values waver a bit)
'           G1        G#1       A2        Bb2
EEPROM   0,(m4,64,78, m4,64,75, m4,64,70, m4,64,66)			'Gstring frets 1-4 		b0/b1 = 7/12     15/20    23/28    31/36

'            B2         C2         C#2        D2
EEPROM   12,(m4,16,252, m4,16,238, m4,16,224, m4,16,212)		'Gstring frets 5-8 		b0/b1 = 39/44    47/52    55/60    63/68

'           Eb2        E2         F2         F#2
EEPROM  24,(m4,16,200, m4,16,189, m4,16,178, m4,16,168)			'Gstring frets 9-12 		b0/b1 = 71/76    80/84    88/92    96/100

'           G2         G#2        A3         Bb3
EEPROM  36,(m4,16,158, m4,16,150, m4,16,141, m4,16,133)			'Gstring frets 13-16		b0/b1 = 104/108  112/116  120/124  128/132

'           B3        C3        C#3        D3
EEPROM  48,(m2,4,252, m2,4,238, m4,16,112, m2,4,212)			'Gstring frets 17-20		b0/b1 = 136/140  145/148  152/156  160/164

'           Eb3       E3        F3        F#3	
EEPROM  60,(m2,4,200, m4,16,94, m2,4,178, m2,4,168)			'Gstring frets 21-24		b0/b1 = 169/172  176/180  184/188  193/196

'           G3        G#3       A4        Bb4
EEPROM  72,(m2,4,159, m2,4,150, m2,4,141, m4,16,66)			'Gstring frets 25-28		b0/b1 = 201/204  209/213  218/221/0  225/229

'           B4        C4        C#4       D4
EEPROM  84,(m4,4,252, m4,4,238, m4,4,224, m4,4,212)			'Gstring frets 29-31		b0/b1 = 234/237  243/245  250/253

'            Eb4       E4        F4        F#4
EEPROM   96,(m4,4,200, m4,4,189, m4,4,178, m4,4,168)

'             G4        G#4       A5        Bb5
EEPROM   108,(m4,4,158, m4,4,150, m4,4,141, m4,4,133)

'            B5        C5        C#5       D5
EEPROM  120,(m8,4,252, m8,4,238, m4,4,112, m8,4,212)

'            Eb5       E5       F5        F#5
EEPROM  132,(m8,4,200, m4,4,94, m8,4,178, m8,4,168)

'            G5        G#5       A6       Bb6
EEPROM  144,(m8,4,159, m8,4,150, m4,4,70, m4,4,66)
What is a 'native micro' btw?
 

moorea21

Senior Member
Ah, I see, that makes sense.
It seems arduinos can output accurate pwm tones in the range I want to use, so I've ordered one to play around with. If it turns out not to do what I want, I'll be back here like a shot!
 

hippy

Technical Support
Staff member
I would try this ...

Code:
'violin synth with 4 strings
#Picaxe 20m2

'G string C.4 pin6
'D string B.3 pin15
'A string C.0 pin10
'E string B.2 pin16
'pwm1 = C.2 pin5
'adc = C.7 pin3

'           G1        G#1       A2        Bb2
EEPROM   0,(m4,64,78, m4,64,75, m4,64,70, m4,64,66)

'            B2         C2         C#2        D2
EEPROM   12,(m4,16,252, m4,16,238, m4,16,224, m4,16,212)

'           Eb2        E2         F2         F#2
EEPROM  24,(m4,16,200, m4,16,189, m4,16,178, m4,16,168)	

'           G2         G#2        A3         Bb3
EEPROM  36,(m4,16,158, m4,16,150, m4,16,141, m4,16,133)

'           B3        C3        C#3        D3
EEPROM  48,(m2,4,252, m2,4,238, m4,16,112, m2,4,212)

'           Eb3       E3        F3        F#3	
EEPROM  60,(m2,4,200, m4,16,94, m2,4,178, m2,4,168)

'           G3        G#3       A4        Bb4
EEPROM  72,(m2,4,159, m2,4,150, m2,4,141, m4,16,66)

'           B4        C4        C#4       D4
EEPROM  84,(m4,4,252, m4,4,238, m4,4,224, m4,4,212)

'            Eb4       E4        F4        F#4
EEPROM   96,(m4,4,200, m4,4,189, m4,4,178, m4,4,168)

'             G4        G#4       A5        Bb5
EEPROM   108,(m4,4,158, m4,4,150, m4,4,141, m4,4,133)

'            B5        C5        C#5       D5
EEPROM  120,(m8,4,252, m8,4,238, m4,4,112, m8,4,212)

'            Eb5       E5       F5        F#5
EEPROM  132,(m8,4,200, m4,4,94, m8,4,178, m8,4,168)

'            G5        G#5       A6       Bb6
EEPROM  144,(m8,4,159, m8,4,150, m4,4,70, m4,4,66)

#Macro PLAYNOTE(add)
  b0 = b0 Min 12 - 12 * 3 / 8 + add ?
  If b0 <> b21 Then
    b21 = b0
    read b0, b10 : b0 = b0 + 1
    read b0, b11 : b0 = b0 + 1
    read b0, b12
    b13 = b12 * 2
    select case b11
	case  4 : setfreq b10 : pwmout pwmdiv4,  C.2, b12, b13
	case 16 : setfreq b10 : pwmout pwmdiv16, C.2, b12, b13
	else    : setfreq b10 : pwmout pwmdiv64, C.2, b12, b13
    end select
  End If
#EndMacro

b21 = 1

main:
	high C.4	; starting with G string notes
	low B.2
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(0)
        Goto Main
      End If

	low C.4	; D string notes
	high B.3
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(21)
        Goto Main
      End If

	low B.3	; A string notes
	high C.0
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(42)
        Goto Main
      End If

	low C.0	; E string notes
	high B.2
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(63)
        Goto Main
      End If

  if b21 <> 1 then
    b21 = 1
    PwmOut C.2, OFF
  end if
  goto main
It's untested but should work. You may have to adjust the first line of the PLAYNOTE macro. That will give a syntax error until you take out the question mark, but should still be usable to test if the responsiveness has improved.
 

techElder

Well-known member
I would have added "setfreq m32" or such around the whole thing.

Is there a reason to run at the default frequency since the OP mentioned "it's very laggy indeed" ?
 

Buzby

Senior Member
With me not being musically minded, all those F# and Bb things mean nothing to me.

Can you explain what the min and max needed frequencies are in Hz ?

Cheers,

Buzby
 

hippy

Technical Support
Staff member
I would have added "setfreq m32" or such around the whole thing.
The problem with that is that the PWMOUT requires a particular SETFREQ and PWMDIV to put out the right tone. Use SETFREQ and the tone changes from what's wanted.
 

moorea21

Senior Member
Blimey hippy, mine took me 2 months to write, you wrote that how fast??
Not run it on the picaxe yet, but simulating it I get the right values by planting a line like 'b0 = 5' etc in the appropriate place.
Probably many questions to ask (mostly 'how did you come up with this??) but see below for the easier to answer ones...

Code:
#Macro PLAYNOTE(add)
  b0 = b0 Min 12 - 12 * 3 / 8 + add		'what on earth is this?
  If b0 <> b21 Then				'what is b21? what does it do? where does itget its value from initially? is it just a 'previous b0' marker?
    b21 = b0
    read b0, b10 : b0 = b0 + 1		'is this faster than 'read b0, b10, b11, b12'?
    read b0, b11 : b0 = b0 + 1
    read b0, b12
    b13 = b12 * 2
    select case b11
	case  4 : setfreq b10 : pwmout pwmdiv4,  C.2, b12, b13
	case 16 : setfreq b10 : pwmout pwmdiv16, C.2, b12, b13
	else    : setfreq b10 : pwmout pwmdiv64, C.2, b12, b13
    end select
  End If
#EndMacro

b21 = 1

main:
	high C.4	; starting with G string notes
	low B.2
	readadc c.7,b0
	'b0 = 5
	If b0 > 4 Then
        PLAYNOTE(0)
        Goto Main
      End If

	low C.4	; D string notes
	high B.3
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(21)
        Goto Main
      End If

	low B.3	; A string notes
	high C.0
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(42)
        Goto Main
      End If

	low C.0	; E string notes
	high B.2
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(63)
        Goto Main
      End If

  if b21 <> 1 then		'An 'impossible value' on purpose? If b21 was the previous b0, & adc never receives a value of 1 from resistor network
    b21 = 1
    PwmOut C.2, OFF
  end if
  goto main
Buzby: The frequency range is 196Hz to 3729Hz, from G1 to Bb6. https://pages.mtu.edu/~suits/notefreqs.html has all note values; their 'G3' is my 'G1' etc
 

moorea21

Senior Member
Am I right in thinking the picaxe pwm is a square wave? In which case I could use 1 octave of picaxes 'tune' notes, and double the frequency similarly to how premelec suggested using a VCO fed from a picaxe DAC pin?
Those 'tune' notes have far more accurate frequencies than I can 'hack' out of a pwm anyway.
 

hippy

Technical Support
Staff member
b0 = b0 Min 12 - 12 * 3 / 8 + add 'what on earth is this?
That's your ADC to EEPROM address calculation written in a different, more efficient, way.

If b0 <> b21 Then 'what is b21? what does it do? where does itget its value from initially? is it just a 'previous b0' marker?
b21 tracks the last b0 EPROM address so it doesn't update the PWMOUT if it's already what is needed. It's initial value is set just before 'main:', to 1 which means PWMOUT OFF, no sound.

read b0, b10 : b0 = b0 + 1 'is this faster than 'read b0, b10, b11, b12'?
read b0, b11 : b0 = b0 + 1
read b0, b12
Yes. At the end of a multi-byte READ the b0 would be adjusted back to what it was at the start and this saves having to do that.

if b21 <> 1 then 'An 'impossible value' on purpose? If b21 was the previous b0, & adc never receives a value of 1 from resistor network
b21 = 1
PwmOut C.2, OFF
Yes, an impossible value so it doesn't clash with any possible value. And again, it doesn't turn PWM off if it's already off. Saves more time.
 

moorea21

Senior Member
Thats all excellent.

I think one of the things I'm least proficient in is the maths side of this programming thing, would you mind talking me through how the adc-eeprom address calc works? You never know, it might kickstart some some dormant mathematical part of my brain. Possibly...

And also, if its possible to answer, how did you go about reworking the code? It might be a good opportunity for me to 'up my game' if I could see what sort of mental processes you went through to get to a far cleaner bit of code. And, as I said before, I'm amazed by how fast you did that.
 

hippy

Technical Support
Staff member
I think one of the things I'm least proficient in is the maths side of this programming thing, would you mind talking me through how the adc-eeprom address calc works?
I have no idea. I just used what others had suggested should be used.

That's why I earlier asked what ADC values were being read by the READADC commands so I could figure out how they would map to EEPROM address ;-)

And also, if its possible to answer, how did you go about reworking the code? It might be a good opportunity for me to 'up my game' if I could see what sort of mental processes you went through to get to a far cleaner bit of code. And, as I said before, I'm amazed by how fast you did that.
Years of doing it is the short answer. I'll see if I can think of something better to write to explain my thinking and method.
 

techElder

Well-known member
The problem with that is that the PWMOUT requires a particular SETFREQ and PWMDIV to put out the right tone. Use SETFREQ and the tone changes from what's wanted.
I sure did think I saw frequency parameters in the EEPROM and "setfreq" before the PWM commands.
 

AllyCat

Senior Member
Hi,

Am I right in thinking the picaxe pwm is a square wave? In which case I could use 1 octave of picaxes 'tune' notes, and double the frequency similarly to how premelec suggested using a VCO fed from a picaxe DAC pin?

Those 'tune' notes have far more accurate frequencies than I can 'hack' out of a pwm anyway.
Yes, the PWM output is a "square" wave* (or more generally a pulse waveform), but I don't understand how you would use the TUNE command, nor the DAC which has only 32 equal steps (so filtered PWM is often a better method to generate a "DAC" output anyway). The TUNE command might have a somewhat better accuracy (at lower frequencies) than PWM, but it must specify (predict) the length of the tone in advance. Also, I believe the PICAxe can do nothing else (not execute the program) whilst a note is "sounding", which doesn't seem to fit with your User-Interface concept.

I'm not convinced that the processor speed is the root issue, but I don't see that you ever need to run below m8. Currently your lowest note G1 is defined by m4,64,78 , but it could be m8,64.156 (the Wizard suggests 158). Then the next notes (from your EPROM data) would be 150, 140 and 132. After that you could continue to 126, etc, or double the clock to m16 and use 252, etc.. Eventually you will need to drop to DIV16 and maybe DIV4, but never need a period count lower than 128 (or 64 if not switching the clock frequency). Thus the minimum frequency step is either 0.8% or 1.6%, whilst a semitone is +6.0% . So you should be able to get the pitch to within better than 1/15 semitone, or the worst interval to double that value.

* A square wave is very rich in Odd harmonics, whilst a violin is generally considered to generate a "sawtooth" waveform, which is rich in Odd and Even harmonics. A low pass filter (R + C) can convert a square wave into a triangle wave (at lower amplitude) which might be a reasonable compromise, or also change the PWM output towards a pulse waveform to get closer to a true sawtooth. However, for four octaves (16:1 frequency shift) you would need to change/switch the "corner frequency" of the filter, which would need a little more external hardware (including a simple amplifier).

Cheers, Alan.
 

moorea21

Senior Member
I'd use the 'tune' command to output a note from a pin that connects to a VCO, and use the DAC to tell the VCO to multiply the frequency by 0.5, 1, 2, or 4 etc to output to the piezo. That way I could have as many octaves as I want. I was hoping that the tune commands length could be set at 50 (posssibly the minimum note length needed) and repeated in a loop as long as that note is still 'held' by the player.

I wrote the eeprom data about 2 years ago, so I can't actually remember why I didnt do as you suggest. I may have used setfreq m2 etc because the notes it produces are closer to the correct frequency, not sure. I'm going to try hippy's code and setfreq and DIV values based on your recommendations with the hardware, will post the results

I'd probably be happy just to get a good response and reasonable notes out of it at the minute, but a sawtooth waveform might be good at some point in the future.
 

AllyCat

Senior Member
Hi,

I'd use the 'tune' command to output a note from a pin that connects to a VCO,....

I was hoping that the tune commands length could be set at 50 (possibly the minimum note length needed) and repeated in a loop as long as that note is still 'held' by the player.
I still don't see how the (square wave) TUNE output would control a (d.c.) Voltage-Controlled Oscillator. But for me, the real "strength" of the PICaxe is that you can build a really simple and portable device that runs on just a few AA cells.

IMHO your "hoping" is misguided. The inter-note program code would need to be really fast and I suspect the "glitch" (which may involve a phase-jump) is likely to sound horrible.

BTW I'd forgotten about the CALIBFREQ command. Unfortunately it's not well documented; the PICaxe Manual references are so out-of-date as to be misleading (IMHO). but even the relevant PIC data sheet gives very little information. I believe that it (now) allows you to trim the frequency in steps of around 0.1 to 0.2%, with a maximum of +/- 31 steps:

You would need to measure the specific chip that you're using and add a value in the lookup table (probably in place of the m4, m8, etc.), but it gives the capability to tune your notes to within a few "cents" (a new term to me) if the present +/- 1% is not good enough. Of course then it will be important to determine if you're tuning to the Just (Harmonic) or Well- (Equal-) Tempered scale. ;)

Cheers, Alan.
 

moorea21

Senior Member
'I still don't see how the (square wave) TUNE output would control a (d.c.) Voltage-Controlled Oscillator'
It wouldnt. The DAC would control the VCO. The tune signal ( basically a pwm) would come out of it's pin, into the VCO, out of the VCO, and into a piezo. The DAC would alter the frequency output of the VCO by telling it to double/halve the frequency of the 'tune' note coming from the tune pwm pin. Except of course if theres another way involving calibfreq...

setfreq b10 : calibfreq -2 : pwmout pwmdiv64, C.2, 156, 312

Would that be the sort of arrangement needed?

hippy:-
There is something amiss with your code: I'm not sure having looked at it that I can tell how it works well enough to debug it, but basically the first note sounds great, but after that any new note just sounds like 'tick tick tick' from the piezo; as if the program is not giving it enough time to sound the new note? It sometimes clears after a variable number of 'ticks', allowing only the note that was played first to be replayed, as any new notes (using any of the 'strings' for input') produce the same ticking sound.

I tried running it as a simulation, with extra code to hard code a couple of values for b0, which I've placed below:
Code:
'violin synth with 4 strings
#Picaxe 20m2

'G string C.4 pin6
'D string B.3 pin15
'A string C.0 pin10
'E string B.2 pin16
'pwm1 = C.2 pin5
'adc = C.7 pin3

'196Hz to 3729Hz


'           G1        G#1       A2        Bb2
EEPROM  0,(m8,64,156, m8,64,150, m8,64,140, m8,64,132)

'            B2         C2         C#2        D2
EEPROM  12,(m16,64,252, m16,64,238, m16,64,224, m16,64,212)

'           Eb2        E2         F2         F#2	
EEPROM 24,(m16,64,200, m16,64,189, m16,64,178, m16,64,168)

'           G2         G#2        A3         Bb3
EEPROM 36,(m16,64,158, m16,64,150, m16,64,141, m16,64,133)

'           B3        C3        C#3        D3
EEPROM 48,(m8,16,252, m8,16,238, m8,16,224, m8,16,212)	

'           Eb3       E3        F3        F#3	
EEPROM 60,(m8,16,200, m16,64,94, m8,16,178, m8,16,168)	

'           G3        G#3       A4        Bb4
EEPROM 72,(m8,16,159, m8,16,150, m8,16,141, m16,64,66)

'           B4        C4        C#4       D4
EEPROM 84,(m16,16,252, m16,16,238, m16,16,224, m16,16,212)	

'            Eb4       E4        F4        F#4
EEPROM  96,(m16,16,200, m16,16,189, m16,16,178, m16,16,168)

'             G4        G#4       A5        Bb5
EEPROM  108,(m16,16,158, m16,16,150, m16,16,141, m16,16,133)

'            B5        C5        C#5       D5
EEPROM  120,(m8,4,252, m8,4,238, m16,16,112, m8,4,212)

'            Eb5       E5       F5        F#5
EEPROM  132,(m8,4,200, m16,16,94, m8,4,178, m8,4,168)

'            G5        G#5       A6       Bb6
EEPROM  144,(m8,4,159, m8,4,150, m16,16,70, m16,16,66)

w0 =0
w5 = 0
w6 = 0
w10= 0
w13 = 0

#Macro PLAYNOTE(add)
  b0 = b0 Min 12 - 12 * 3 / 8 + add
  If b0 <> b21 Then
    b21 = b0
    read b0, b10 : b0 = b0 + 1
    read b0, b11 : b0 = b0 + 1
    read b0, b12
    b13 = b12 * 2
    select case b11
	case  4 : setfreq b10 : pwmout pwmdiv4,  C.2, b12, b13	'b0 = 5(init) 2(after PLAYNOTE)	b10 = 114	b11 = 64	b12 = 156	b13 =56	b21 = 0
	case 16 : setfreq b10 : pwmout pwmdiv16, C.2, b12, b13	'b0 = 201(init) 72(after PLAYNOTE)	b10 = 16	b11 = 168	b12 = 114	b13 =228	b21 = 70
	else    : setfreq b10 : pwmout pwmdiv64, C.2, b12, b13
    end select
  End If
#EndMacro

b21 = 1
b0 = 5
main:
	high C.4	; starting with G string notes
	low B.2
	'readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(0)
	  b0 = 201	'address 72's data
        Goto Main
      End If

	low C.4	; D string notes
	high B.3
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(21)
        Goto Main
      End If

	low B.3	; A string notes
	high C.0
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(42)
        Goto Main
      End If

	low C.0	; E string notes
	high B.2
	readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(63)
        Goto Main
      End If

  if b21 <> 1 then
    b21 = 1
    PwmOut C.2, OFF
  end if
  goto main
There may well be some problem with how I'm adding the values for b0, although I can't see any. The values I got for all the other variables are commented in.

As far as I can see, b10 = 114 when setfreq will = m8, and 122 when it will = m16.

But when simulated b0 = 201 (played as the second note after b0 =5 plays/ works correctly) goes into this:-

select case b11
case 16 : setfreq b10 : pwmout pwmdiv16, C.2, b12, b13

which should be eeprom 72 (m8,16,159) ie 114, 16 159

b10 = 114(m8)
b11 = 16
b12 = 159
b13 =318

giving

select case 16
case 16 : setfreq 16 : pwmout pwmdiv16, C.2, 159, 318

instead it gets:-

b10 = 16
b11 = 168
b12 = 114(m8)
b13 =228

giving

select case b11
case 168 : setfreq 16 : pwmout pwmdiv16, C.2, 114, 228

Now, if b11 had equalled 159, I'd have said that :-

b10 had taken data intended for b11
b11 ' ' b12
b12 ' ' b10

but of course b11 = 168 not 159 as it would if that was true.
This may well be twaddle of course! Quite confused now.

But anyway, I'm stumped.

But in the meantime:

The faster processor speeds and corresponding DIV and period data (thanks Alan) cut and pasted into my original program result in correct notes delivered without any noticeable lag! So that now works (hurray!), although I'd prefer to use hippy's code if I could follow it clearly enough.
 
Last edited:

hippy

Technical Support
Staff member
hippy:-
There is something amiss with your code: I'm not sure having looked at it that I can tell how it works well enough to debug it, but basically the first note sounds great, but after that any new note just sounds like 'tick tick tick' from the piezo; as if the program is not giving it enough time to sound the new note?
It could be the ADC to note selection calculation which is wrong. If ADC is changing then it may be repeatedly and rapidly issuing PWMOUT commands causing the ticking.

Though looking at it the *3 should probably be after the /8 -

b0 = b0 Min 12 - 12 / 8 * 3 + add

I tried running it as a simulation, with extra code to hard code a couple of values for b0, which I've placed below:

Code:
	high C.4	; starting with G string notes
	low B.2
	'readadc c.7,b0
	If b0 > 4 Then
        PLAYNOTE(0)
	  b0 = 201	'address 72's data
        Goto Main
      End If
There may well be some problem with how I'm adding the values for b0, although I can't see any.
You have to set b0 to a forced value before b0 is tested by "IF b0 > 4" and before b0 is used by PLAYNOTE, and if you want to force a particular note then you'll have to force that within PLAYNOTE.

Or you could split PLAYNOTE into FINDNOTE and PLAYNOTE, do something like ...

Code:
#Macro FINDNOTE(add)
  b0 = b0 Min 12 - 12 / 8 * 3 + add
#EndMacro

#Macro PLAYNOTE()
  If b0 <> b21 Then
    :
  End If
#EndMacro

	high C.4	; starting with G string notes
	low B.2
	'readadc c.7,b0
        [b]b0 = 5 ; Force string[/b]
	If b0 > 4 Then
          FINDNOTE(0)
	  b0 = [b]144 ; Force G5[/b]
          PLAYNOTE()
        Goto Main
      End If
 

AllyCat

Senior Member
Hi,

The DAC would alter the frequency output of the VCO by telling it to double/halve the frequency of the 'tune' note coming from the tune pwm pin.

setfreq b10 : calibfreq -2 : pwmout pwmdiv64, C.2, 156, 312
TUNE or PWM ? If the "DAC" is actually just a programmable binary frequency multiplier/divider, then I don't see that it achieves any more than the correct selection of m8 or m16 and pwmdiv64, pwmdiv16 and pwmdiv4 parameters in the pwmout command.

Yes, basically that format, but I'd use CALIBFREQ b10 (taken from the lookup table). You probably don't need to select m8 or m16 now, or if you do it only needs one "flag" bit (and calibfreq uses only 6 bits). Note that m8, etc are just numbers, as can be shown by running SERTXD(#m8," ",#m16) in the simulator.

Also, I wouldn't store the b10 value directly in the EPROM, but the desired offset in (say) cents (hundredths of a semitone) from the calculated versus available pwm frequency. Then you can calibrate each individual PICaxe chip (e.g. in setfreq steps per semitone) and send the compensated value to the setfreq command.

Cheers, Alan.
 
Last edited:

moorea21

Senior Member
Thanks Alan.

This is the current state of play with my code, as opposed to hippy's:
Code:
'violin synth with 4 strings sharing 1 adc,compact code using eeprom 18 2 18
'G string C.4 pin6
'D string B.3 pin15
'A string C.0 pin10
'E string B.2 pin16
'pwm = C.5 pin5
'adc = C.7 pin3

'           G1        G#1       A2        Bb2
EEPROM  0,(m8,64,156, m8,64,150, m8,64,140, m8,64,132)

'            B2         C2         C#2        D2
EEPROM  12,(m16,64,252, m16,64,238, m16,64,224, m16,64,212)

'           Eb2        E2         F2         F#2	
EEPROM 24,(m16,64,200, m16,64,189, m16,64,178, m16,64,168)

'           G2         G#2        A3         Bb3
EEPROM 36,(m16,64,158, m16,64,150, m16,64,141, m16,64,133)

'           B3        C3        C#3        D3
EEPROM 48,(m8,16,252, m8,16,238, m8,16,224, m8,16,212)	

'           Eb3       E3        F3        F#3	
EEPROM 60,(m8,16,200, m16,64,94, m8,16,178, m8,16,168)	

'           G3        G#3       A4        Bb4
EEPROM 72,(m8,16,159, m8,16,150, m8,16,141, m16,64,66)

'           B4        C4        C#4       D4
EEPROM 84,(m16,16,252, m16,16,238, m16,16,224, m16,16,212)	

'            Eb4       E4        F4        F#4
EEPROM  96,(m16,16,200, m16,16,189, m16,16,178, m16,16,168)

'             G4        G#4       A5        Bb5
EEPROM  108,(m16,16,158, m16,16,150, m16,16,141, m16,16,133)

'            B5        C5        C#5       D5
EEPROM  120,(m8,4,252, m8,4,238, m16,16,112, m8,4,212)

'            Eb5       E5       F5        F#5
EEPROM  132,(m8,4,200, m16,16,94, m8,4,178, m8,4,168)

'            G5        G#5       A6       Bb6
EEPROM  144,(m8,4,159, m8,4,150, m16,16,70, m16,16,66)


main:
	b2 = 0	'reset the 'which string is high?' marker
		
	high C.4	; starting with G string notes
	low B.2
	b2 = 1
	gosub readAdcVal

	low C.4	; D string notes
	high B.3
	b2 = 2
	gosub readAdcVal
	
	low B.3	; A string notes
	high C.0
	b2 = 3
	gosub readAdcVal

	low C.0	; E string notes
	high B.2
	b2 = 4
	gosub readAdcVal
goto main


readAdcVal:	
	readadc c.7,b0
	If b0 > 4 Then
		b1 = 12
		do
			If b0 < b1 then exit
       		if b1 > 244 then exit           
			b1 = b1 + 8
		loop
	Else
		return
	End If
	
	select case b2
	case 1
	b1 = b1- 12: b1 = b1 * 3 / 8
	case 2
	b1 = b1 - 12: b1 = b1 * 3 / 8: b1 = b1 + 21
	case 3
	b1 = b1 - 12: b1 = b1 * 3 / 8: b1 = b1 + 42
	case 4
	b1 = b1 - 12: b1 = b1 * 3 / 8: b1 = b1 + 63
	else
	end select

	read b1, b10, b11, b12
	b13 = b12 * 2
	select case b11
	  case  4 : setfreq b10: pwmout pwmdiv4,  C.2, b12, b13
	  case 16 : setfreq b10: pwmout pwmdiv16, C.2, b12, b13
	  case 64 : setfreq b10: pwmout pwmdiv64, C.2, b12, b13
end select
return
Unfortunately this does everything except switch the note off when a string stops contacting a fret, which is a large part of what its supposed to do...

I have tried everything I can, and I can't seem to find any way to do this. Getting quite tired now, don't want to shelve this for another 2 years over this issuse, but somewhat losing the will to carry on here.

Given that this code run perfectly well in all other respects and doesnt actually need optimising per se, can anyone suggets a way of doing this? Thanks
 

hippy

Technical Support
Staff member
Given that this code run perfectly well in all other respects and doesnt actually need optimising per se, can anyone suggets a way of doing this? Thanks
All it seems you need is a flag which is cleared at the start of the loop, set if a PWMOUT has been executed within the loop, and, if it is not set at the end of the loop, turn the PWMOUT off.
 

moorea21

Senior Member
Okay!

After rewiring it to get rid of some randomness, I put this code into it:-
Code:
b15 = 0

main:
	b2 = 0	'reset the 'which string is high?' marker
	b15 = 0
	
	high C.4	; starting with G string notes
	low B.2
	b2 = 1
	gosub readAdcVal

	low C.4	; D string notes
	high B.3
	b2 = 2
	gosub readAdcVal
	
	low B.3	; A string notes
	high C.0
	b2 = 3
	gosub readAdcVal

	low C.0	; E string notes
	high B.2
	b2 = 4
	gosub readAdcVal
	
	If b15 = 0 Then
	PwmOut C.2, OFF
	End If
goto main

readAdcVal:	
	readadc c.7,b0	'b0 is the adc value
	'Let b0 = 3	'0, 7, 108.etc...
	If b0 > 4 Then
		b1 = 12
		do
			If b0 < b1 then exit
       		if b1 > 244 then exit           ' exit before overflow (if 252 is highest valid value)
			b1 = b1 + 8
		loop
	Else
		return
	End If
	
	select case b2
	case 1
	b1 = b1- 12: b1 = b1 * 3 / 8
	case 2
	b1 = b1 - 12: b1 = b1 * 3 / 8: b1 = b1 + 21
	case 3
	b1 = b1 - 12: b1 = b1 * 3 / 8: b1 = b1 + 42
	case 4
	b1 = b1 - 12: b1 = b1 * 3 / 8: b1 = b1 + 63
	else
	end select

	read b1, b10, b11, b12
	b13 = b12 * 2
	b15 = 1
	select case b11
	  'case  1 : setfreq b10: pwmout           b.4, b12, b13
	  case  4 : setfreq b10: pwmout pwmdiv4,  C.2, b12, b13
	  case 16 : setfreq b10: pwmout pwmdiv16, C.2, b12, b13
	  case 64 : setfreq b10: pwmout pwmdiv64, C.2, b12, b13
return
	end select
And it all works! Can't believe how long it took.
Many thanks to everyone, you've all been amazing, as usual. This would have crashed and burned permanently if not for this forum.
When the hardware is built, and the other 'side' of its functionality programmed, I'll put it all in the 'finished' section. Someone else can maybe build something like it if they want to...
 

lbenson

Senior Member
Glad it's working.

If you still find laggyness, the "select case b2" can be simplified, though perhaps without much savings in execution time. The only difference in the calculation of b1 for b2=1,2,3,4, is how much you add, which as it turns out is a multiple of 21--21 multiplied by one less than b2.

Since you need to preserve the value of b2, you can use a scratch variable to calculate how much you will need to add after the other (otherwise identical) calculation is made--for instance, b27. So this should be able to replace the whole "select case b2":
Code:
b27 = b2 - 1 * 21 ' gives values of 0,21,42,63 for b2 values of 1,2,3,4
b1 = b1 - 12 * 3 / 8 + b27
You might of course find the structure that you have easier to understand. You could preserve that code as a comment by enclosing it with "#rem" and "#endrem" statements.
 
Top