Shop OBEX P1 Docs P2 Docs Learn Events
Karplus-Strong on the Propeller? - Page 2 — Parallax Forums

Karplus-Strong on the Propeller?

2456

Comments

  • jeff-ojeff-o Posts: 181
    edited 2011-05-02 07:35
    lonesock wrote: »
    They can all be the same sample rate, however, if you were using one cog per virtual guitar string (for example), you could keep the buffer size constant, but change the sample rate for each string to get the needed range. The lowest frequency you can play is ~ sample_rate / buffer_size. You could tune the sample rate so that each string starts at E (~82.4 Hz), A, D, G, B, E (~330 Hz). Just an idea to minimize buffer space.

    Jonathan

    I'll keep that in mind, if memory space becomes an issue. I'm not yet sure how much space the rest of the program will occupy.
  • jeff-ojeff-o Posts: 181
    edited 2011-05-02 22:23
    At last, I managed to get multiple instances running. It wasn't your code, the problem was setting up 12blocks to play nice.

    I notice some noise in the output while the notes are playing. Sampling rate doesn't seem to have an effect. Would this be something that could be corrected in software or should I add a hardware filter?
  • Ahle2Ahle2 Posts: 1,179
    edited 2011-05-03 01:59
    The higher cog number used the more noise you will hear.
    This is because the duty signal has to go through more gates and accumulates "errors".
    These errors will be heard as noise.

    It took me a while to figure this out.
    In some applications "sound generator xxx" sounded noisy; In some others, the same sound generator sounded noise free.
    I found out while moving initializing codes around, and by change moved a sound generator from a high cog to a low one.

    /Ahle2
  • jeff-ojeff-o Posts: 181
    edited 2011-05-03 04:50
    That's good to know. There doesn't seem to be much change between running on one or on four cogs, though it would appear that my (serial) LCD screen is affecting things. I can hear "blips" in the noise when the LCD text changes. This is on my custom Propeller board. On the basic demo board, set up with nothing but a headphone jack, the noise is far less noticeable. So, perhaps I'll have to figure out how to isolate the LCD screen...
  • jeff-ojeff-o Posts: 181
    edited 2011-05-03 04:54
    Also, noise is present regardless of whether I launch Karplu-Strong, so it must be the LCD only.
  • jeff-ojeff-o Posts: 181
    edited 2011-05-03 23:23
    More progress! I identified a component that was causing some of the noise (basically feeding it back into the propeller somehow). So, when I leave everything intact (LCD connected, etc) but don't launch KS, there is zero noise. If I initialize KS but never play a note, there is a bit of a buzz that is always present. When playing a note, there is noticeable noise, though I am investigating whether my summing amp is to blame. I've got capacitors in series to kill any DC, but I may have to remove them and do the DC filtering after the op amp.

    I suppose much of this is hardware issues but it helps to write it out. Once I get my issues worked out I can box it all up in a neat case (already complete) and concentrate more on the software.

    Jonathan, any idea on whether it'll be possible to pitch-bend or change the sustain in real time?
  • lonesocklonesock Posts: 917
    edited 2011-05-04 08:31
    Cool! See if the buzz when you start only 1 KS object just happens to be at the sample rate...I'm using a waitcnt, which drops the cog into a low power mode until the timer value is hit, then powers it back up again...that power on/off cycle may well be the cause of your buzz. If so, I can rewrite the object to never go into the power-down state, though we'll lose some timing-determinism.

    Sure, I can change the delay length and sustain value on the fly, just let me know what kind of interface you'd like. Note that it will be much tougher to slide the delay value around (especially if you want it log-scaled instead of linear) rather than just jump to a new delay value.

    Jonathan
  • jeff-ojeff-o Posts: 181
    edited 2011-05-04 09:27
    lonesock wrote: »
    Cool! See if the buzz when you start only 1 KS object just happens to be at the sample rate...I'm using a waitcnt, which drops the cog into a low power mode until the timer value is hit, then powers it back up again...that power on/off cycle may well be the cause of your buzz. If so, I can rewrite the object to never go into the power-down state, though we'll lose some timing-determinism.

    Sure, I can change the delay length and sustain value on the fly, just let me know what kind of interface you'd like. Note that it will be much tougher to slide the delay value around (especially if you want it log-scaled instead of linear) rather than just jump to a new delay value.

    Jonathan

    I'll give it a try, if shorting out the capacitors has no effect. I've got a few of them in my audio path, so I can certainly afford to lose some. Don't rewrite anything just yet, until I know for sure the hardware isn't to blame!

    I was thinking about how to go about changing the delay and sustain on the fly. There could be a variable that is continually monitored, rather than passed as a fixed value to the loop. In the case of the delay length, the period value would be continually updated by a different part of the program, presumably based on user input. The MIDI note value couldn't be used because is is basically a fixed period length. Alternatively, there could be a fixed stim value that is used to trigger the pluck, and a second "pitch bend" value that is being monitored for changes. Deviation from a central value, either above or below, would change the delay length. I don't think log scaling would be required, as long as whatever is controlling that pitch bend value is properly scaled. In this alternative, the MIDI note value could be used. Come to think of it, that's how it's done in the official MIDI standard, isn't it? Note + pitch bend? I recall reading something about that, I should dig it up again.

    I suppose sustain would work in a similar way, though over a much smaller scale (0-8, rather than +/- a few thousand for the delay)
  • jeff-ojeff-o Posts: 181
    edited 2011-05-04 19:06
    So, I tried it out using just a single channel, using the original code and not the one that launches multiple objects. The noise is virtually zero, though still audible if you turn up the volume high enough.

    I then tried the multi-object version, launching one, two, three and four strings. Launching just one produced about the same amount of noise than the single channel code. Each additional channel added to the noise, so that at four channels it was very audible.

    I guess that pretty much proves it; at least some of the noise is coming from software. The fact that it increases when more channels are added suggests that noise is being produced on each channel, and adding together at the summing amp. Indeed, when all four channels are on the noise is not only louder but has a pattern as well, that repeats each time my four-note loop makes a pass.

    So yeah, your plan to disable the low power mode sounds good at this point. It's not ideal for battery powered projects, but I think that eliminating noise is more important.
  • lonesocklonesock Posts: 917
    edited 2011-05-05 07:43
    Well, one possibility is have each KS object simply write the output value to a hub location (say a single word). The your master cog would be responsible for sampling that word for all N channels, summing, clamping, etc. then using the counters to actually output the values to a pin. I could also get this to work where a single cog can process more than 1 channel, you would just keep N buffers (each would include a bit of state information), and pass them one at a time into a new routine, e.g. "value += next_sample( buffer_pointer )".

    In line with what Ahle2 noted, this would allow you to use only a single cog to do the actual pre-mixed audio output, and that cog could be chosen to match the pin-put for minimum noise.

    Jonathan
  • jeff-ojeff-o Posts: 181
    edited 2011-05-05 09:17
    lonesock wrote: »
    Well, one possibility is have each KS object simply write the output value to a hub location (say a single word). The your master cog would be responsible for sampling that word for all N channels, summing, clamping, etc. then using the counters to actually output the values to a pin. I could also get this to work where a single cog can process more than 1 channel, you would just keep N buffers (each would include a bit of state information), and pass them one at a time into a new routine, e.g. "value += next_sample( buffer_pointer )".

    In line with what Ahle2 noted, this would allow you to use only a single cog to do the actual pre-mixed audio output, and that cog could be chosen to match the pin-put for minimum noise.

    Jonathan

    Whatever works is fine with me. Minimizing noise is key, and reducing the number of cogs used is always welcome!

    I'm currently chasing another issue; I've got an ADC accepting user input for the string frequencies that seems to be messing up the KS objects. There's more testing to be done before I can say exactly what is doing what, but the effect is that when I'm using the ADC driver, the volume of the KS output is extremely attenuated. When I disable the ADC object, they're fine.

    There are three "parties" involved, the KS objects, the ADC and a digital potentiometer controlling the output volume of the mixed signals. The ADC and digipot share data and clock lines, but (of course) have different CS pins. I've got a few tests planned for tonight to try to narrow down what's going on. I was approaching 2am when all this came to light so I wasn't able to do more then...
  • jeff-ojeff-o Posts: 181
    edited 2011-05-05 20:46
    More interesting stuff! I tried a few experiments, here's what I found.

    The problem is definitely the ADC object I'm using (ADC_INPUT_DRIVER, for MCP3x0x series ADCs). I'm using this one because it separates the DIN and DOUT pins. Now here's the interesting part: regardless of whether I have the Prop generate a KS strum or just a 1 second tone, it's attenuated when the ADC code is running! Even a basic program that does nothing but read the ADC and produce a tone exhibits this behavior. Time to find a different ADC object...
  • jeff-ojeff-o Posts: 181
    edited 2011-05-05 21:50
    jeff-o wrote: »
    More interesting stuff! I tried a few experiments, here's what I found.

    The problem is definitely the ADC object I'm using (ADC_INPUT_DRIVER, for MCP3x0x series ADCs). I'm using this one because it separates the DIN and DOUT pins. Now here's the interesting part: regardless of whether I have the Prop generate a KS strum or just a 1 second tone, it's attenuated when the ADC code is running! Even a basic program that does nothing but read the ADC and produce a tone exhibits this behavior. Time to find a different ADC object...

    SOLVED!!! There is a single 'NOP' command in the ADC Input Driver object that is commented out. The comments state, "slow down input (slowest part of ADC) add this NOP if not reading information at 80MHz (or faster)" I did so, and now the sound volume is back, and ADC is working.

    One.Little.NOP.
  • jeff-ojeff-o Posts: 181
    edited 2011-05-06 04:40
    So I traded in a few more hours of sleep last night, but I'm delighted to say that I now have a working proof-of-concept design! Four KS objects running, with user input via the ADC controlling pitch. Depending on where the user's finger lies on the sensor, a specific note is selected and then "plucked."

    Now I can add in fancy stuff like different tunings, and other user-selected options. Once Jonathan rigs up the code to change the delay and sustain in real time, I can add in those features as well (hopefully, without breaking the ADC again...)

    I'd probably be giddy right now, but my lack of sleep is keeping that subdued. ;) Totally worth it though. Maybe I'll let myself sleep tonight.
  • lonesocklonesock Posts: 917
    edited 2011-05-06 08:28
    That's great news! I'll try to rig up the next version on break today.

    Jonathan
  • lonesocklonesock Posts: 917
    edited 2011-05-09 09:10
    Sorry, for the delay, I started an update with way too many changes, then I realized you just needed a few extra features [8^)

    So, here's the next rev. You can set the frequency (in Hz, or via the MIDI note) and the sustain independently. Just call pluck to add energy to the string when you want.

    Jonathan
  • jeff-ojeff-o Posts: 181
    edited 2011-05-09 09:52
    lonesock wrote: »
    Sorry, for the delay, I started an update with way too many changes, then I realized you just needed a few extra features [8^)

    So, here's the next rev. You can set the frequency (in Hz, or via the MIDI note) and the sustain independently. Just call pluck to add energy to the string when you want.

    Jonathan

    Ah, I see what you've done. At first I was wondering why you split up the pluck, note and sustain parameters but now it makes perfect sense. So you can have the note and sustain continually updating, no matter whether the string is plucked or not. Then when it IS plucked, the KS loop is all ready to go with the most recent data. Yeah, that'll work quite well for me, now that I think about it!

    So, I assume that if I want to "mute" the string early, I just shoot the sustain down to 1 or 0 and it takes care of the rest.

    I'll report back tonight once I mod my program to use the new code!
  • lonesocklonesock Posts: 917
    edited 2011-05-09 09:57
    Yep, or pluck with a volume of 1. By the way, the volume stuff is really rough...let me know if you need more fine-grained control.

    Jonathan
  • jeff-ojeff-o Posts: 181
    edited 2011-05-09 10:41
    lonesock wrote: »
    Yep, or pluck with a volume of 1. By the way, the volume stuff is really rough...let me know if you need more fine-grained control.

    Jonathan

    Sure thing. Right now I've got an off-board digital potentiometer controlling overall volume (256 steps, I think) but if it could all be done in software then that would be another $5 I could trim off the build price. I'll add in the real-time volume controls to my code tonight and see if "scrubbing" the volume control sensor causes weird effects with just 32 steps.
  • lonesocklonesock Posts: 917
    edited 2011-05-09 11:26
    Well, I'm all about adding functionality to the software to reduce hardware costs! [8^)

    So, from what I understand of your project you can cut costs by:

    * mixing all the samples internally, with only a single mono output (or maybe stereo with some added effects?)
    * adding in at least a 256-level volume control per string, and maybe a master level?

    Anything else? Might as well add it to the wishlist. I'm guessing you'd like, but don't necessarily need:

    * more than 4 strings
    * multiple strings per cog (fewer cogs)
    * less RAM requirements (I may still be able to get the doubler working)

    Let me know. Your project sounds like a lot of fun, can you post a video or pics of the project in its current state?

    thanks,
    Jonathan
  • ctwardellctwardell Posts: 1,716
    edited 2011-05-09 11:58
    I like this thread...

    - no hand wringing about the arduino
    - no windows/.net bashing
    - no "if we only had proper support for 'C'"
    - no "if we only had a standard"
    - no "how do we add a MegaGigaZillaByte of RAM"
    - no "chip that shall not be named..."

    Just someone with an idea, someone who really kicks tail with algorithms, and a prop chip.

    More of these would be a really nice trend.

    C.W.
  • jeff-ojeff-o Posts: 181
    edited 2011-05-09 12:04
    lonesock wrote: »
    Well, I'm all about adding functionality to the software to reduce hardware costs! [8^)

    So, from what I understand of your project you can cut costs by:

    * mixing all the samples internally, with only a single mono output (or maybe stereo with some added effects?)
    * adding in at least a 256-level volume control per string, and maybe a master level?

    Anything else? Might as well add it to the wishlist. I'm guessing you'd like, but don't necessarily need:

    * more than 4 strings
    * multiple strings per cog (fewer cogs)
    * less RAM requirements (I may still be able to get the doubler working)

    Let me know. Your project sounds like a lot of fun, can you post a video or pics of the project in its current state?

    thanks,
    Jonathan

    - I've got lots of extra pins to work with, so internal mixing isn't high on my list. I still need an op-amp to buffer and filter the output, so it's no big deal to change it into a mixer by adding X number of resistors.
    - A 256 level volume control would equal what an external digital volume control is capable of; and would eliminate the device, some supporting resistors and caps, and free up a pin on the propeller. That would be a plus, for sure.
    - more than 4 strings would be great. I'm not planning on making a concert harp (46 strings) but I think it ought to be capable of 24 strings. Dunno if that's possible though.
    - less RAM requirements is always a good thing!

    I'm building a laser guitar. I'll see if I can post a few photos for you. It's really pretty freakin' awesome. Eventually I plan on posting full instructions at Instructables.com, though in the back of my mind I'm wondering if it's almost too good to give away for free...... A conundrum for sure, especially since I'd want you to make some profit from it as well. I'll have to see what kind of reactions I'm getting from people, especially musicians. People loved my last guitar, and it sounded terrible. They'll go bananas over this one!
  • jeff-ojeff-o Posts: 181
    edited 2011-05-09 12:06
    ctwardell wrote: »
    I like this thread...

    - no hand wringing about the arduino
    - no windows/.net bashing
    - no "if we only had proper support for 'C'"
    - no "if we only had a standard"
    - no "how do we add a MegaGigaZillaByte of RAM"
    - no "chip that shall not be named..."

    Just someone with an idea, someone who really kicks tail with algorithms, and a prop chip.

    More of these would be a really nice trend.

    C.W.

    Thanks! Just wait till you see the result... ;)
  • jeff-ojeff-o Posts: 181
    edited 2011-05-10 04:54
    Here are the results of last night's tinkering. I converted the new KS code to work inside 12blocks without much issue. I then swapped out the old "blocks" and stuck in the new ones. I also added the mute capability. After that I was chasing that same attenuation issue I was having before. Fortunately, this morning I managed to get it working reliably. The noise is still present though, and I have another theory. I noticed that when I activate the string mute, the sound of the noise changes. What I'm wondering is whether there is some sort of feedback, either internal in software or externally through my summing op amp, causing one or more strings to continue "ringing" even though they should have died away. Indeed, the sound of the noise is different depending on the amount of sustain the strings were given when plucked. There is more noise when plucked with sustain = 9 than when sustain = 3. And, I can make some of the noise go away if I hit the mute after the strings have stopped ringing (there is still a high frequency component in there that never goes away, however)

    Oh! And the noise is there even before I pluck the first note.

    You mentioned earlier about the pins going to sleep between plucks - perhaps it's worth removing that function to see how it affects things. Unless there's something else you can spot that's causing this!

    Thanks again for all your hard work, Jonathan. I really appreciate it.
  • prof_brainoprof_braino Posts: 4,313
    edited 2011-05-10 07:09
    You guys are doing a lot of great development. I haven't thoroughly examined the code yet, may I ask my default question?

    Do the comments in the code reflect the discussion in the thread?

    The reason I ask is that there might be a bunch of kids looking at this function (school robot club), but it will likely be after you've moved on to a bigger and better project.
    The thread makes perfect sense, does the code also capture your discussion and discoveries? The extra polish of detailed comments is most appreciated.

    In any case good work!
  • jeff-ojeff-o Posts: 181
    edited 2011-05-10 07:29
    You guys are doing a lot of great development. I haven't thoroughly examined the code yet, may I ask my default question?

    Do the comments in the code reflect the discussion in the thread?

    The reason I ask is that there might be a bunch of kids looking at this function (school robot club), but it will likely be after you've moved on to a bigger and better project.
    The thread makes perfect sense, does the code also capture your discussion and discoveries? The extra polish of detailed comments is most appreciated.

    In any case good work!

    Both the main code and the demo project are quite well commented and should be easy for most people to follow. I'll make sure that my finished project has lots of comments, too!
  • LawsonLawson Posts: 870
    edited 2011-05-10 07:33
    Are you using the duty mode of the counters for output? If so, that mode has significant signal content up to 40MHz. (assuming an 80MHz clock) I doubt many op-amps are happy with such high frequency signals at the summing junction. I'd add an RC low-pass filter right next to each output pin to cut out the really high frequency stuff before it reaches the summing op-amp. Alternatively the output pwm could be switched to using NCO mode setup as a pulse generator. (AN001 page 7 has a nice bit of code that demonstrates this) This would produce a signal with a more clearly defined spectrum. Duty-mode counters also have a fair amount of "pattern" noise, continually adding a small pseudo-random number to the output samples will spread this noise out so it's less noticeable. (picked this up, from one of the many propeller audio threads, and should apply to any pwm DAC)

    Lawson
  • jeff-ojeff-o Posts: 181
    edited 2011-05-10 08:16
    Lawson wrote: »
    Are you using the duty mode of the counters for output? If so, that mode has significant signal content up to 40MHz. (assuming an 80MHz clock) I doubt many op-amps are happy with such high frequency signals at the summing junction. I'd add an RC low-pass filter right next to each output pin to cut out the really high frequency stuff before it reaches the summing op-amp. Alternatively the output pwm could be switched to using NCO mode setup as a pulse generator. (AN001 page 7 has a nice bit of code that demonstrates this) This would produce a signal with a more clearly defined spectrum. Duty-mode counters also have a fair amount of "pattern" noise, continually adding a small pseudo-random number to the output samples will spread this noise out so it's less noticeable. (picked this up, from one of the many propeller audio threads, and should apply to any pwm DAC)

    Lawson

    I've got a LPF with a cutoff frequency of about 13kHz after the summing junction, and DC blocking capacitors before. The LPF seems to be doing its job to kill anything above 13kHz, but it's the noise in the audible range that I'm having trouble with. I can play around with the LPF and lower the cutoff, but I can't drop it too low or it will kill off harmonics as well. I chose 13kHz because it's just above the maximum MIDI note 127. However, I've found that the KS algorithm breaks down long before that. Indeed, the highest fundamental note on a guitar is 1175Hz, so I could set my LPF to maybe 2350 Hz to capture the first harmonic (if harmonics are even being produced) and that should take care of a lot of the noise without touching the code.
  • lonesocklonesock Posts: 917
    edited 2011-05-10 16:52
    Thanks for the kind words, everybody!

    Here's an incremental update: this version has a linear volume control, so now when you call pluck, pass in a number between 1 and 255. I got the period doubler to work better, as in it always got the fundamental frequency right, but it still sounded much worse (it actually sounded a bit like plucking the harmonic of a string; i.e. lightly touching the string at a node, but not actually pressing hard enough for the string to touch the fret, so it might be a cool effect). So, again the period doubler is off the table.

    With the current code, and an output sample rate of 44.1kHz, and a prop clock speed of 80MHz, we have enough processing power to do about 4 strings per cog. If you want to do analog mixing, you can only do 2 strings per cog, since we only have 2 counters per cog. Still, you could do a 6-string with 3 cogs, which is nice.

    Regarding the noise, I'm a bit suspicious of the summing circuit, though I'm no electronics whiz. I would at least try an inline resistor per pin to the summing junction, and a cap from there to gnd, and a DC blocking cap from there to your (assuming bipolar) op-amp input. I only have 2 cogs running for my demoboard output, but I'm not hearing any noise there.

    Lawson, the issue with NCO mode is that the output pin(s) are always high 50% of the time, so it doesn't lend itself well to being a DAC. You are absolutely correct about adding in the PRNG value to the output to help the DUTY mode...it masks the noise heard when the FRQA values are right near the center. I believe the thread you mentioned was between JonnyMac and Chip. If we switch to summing all the values in software, then output via a single cog & pin, that would be the ideal setup, IMO.

    Jonathan
  • LawsonLawson Posts: 870
    edited 2011-05-10 19:31
    @lonesock follow my link and skip to page 7. The code is using NCO mode to generate a variable duty cycle at a fixed frequency. CTRx is setup in NCO mode, FRQx is set to 1, and PHSx is cleared. This results in a low output until bit 31 of PHSx goes high, about ~30sec. To generate a pulse PHSx is set to the negative number of clock cycles the pulse should last, this sets bit 31 and sets the output pin. Meanwhile the counter is still incrementing PHSx. After the pulse duration in clock cycles PHSx overflows and bit 31 clears returning the output pin to low.

    This is a super useful trick. It allows for the creation of single clock cycle pulses from SPIN, and since its only one instruction it's extremely useful for high speed serial communication. Ofc. it's also nice for generating pwm signals.

    Lawson
Sign In or Register to comment.