Shop OBEX P1 Docs P2 Docs Learn Events
TinySynth ! — Parallax Forums

TinySynth !

Ahle2Ahle2 Posts: 1,178
edited 2012-06-03 14:27 in Propeller 1
Here is a minimalistic synth written in just 35 rows of assembler.
TinySynth - Archive [Date 2012.04.27 Time 22.24].zip

It features:
* 1 Oscillator
* Pulse width modulation
* Very rough low pass filter
* Portamento/slide
* Logaritmic attack and decay
* 500 kHz sample rate for "aliasing free" sound generation
* A massive 164 bytes of code + data for the actual synth (not counting spin code)
* No multiplications or lookup tables (yes you heard me!!)

It was really not meant to be released or even usable as a synth. I just thought that it might be of interest to someone else.
This is just me experimenting with "new" techniques that might be implemented into Retronitus later.

/Johannes
«1

Comments

  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2012-04-27 13:43
    That's awesome! Anyone should be able to shoe horn that into any game creation!

    How on earth did you manage such great 8bit synth sound from so little memory?!?

    OBC
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-04-27 15:49
    This looks fun. Well done!
  • Cluso99Cluso99 Posts: 18,069
    edited 2012-04-27 16:16
    WOW! Truely awesome work Johannes.
    The prop still has such a lot of capabilities still being uncovered by such imaginative people :)
  • pik33pik33 Posts: 2,350
    edited 2012-04-27 22:03
    User Name wrote: »
    Johannes,

    Thanks! Have you given any thought to making a full-blown retro synth like Linus Akesson's Chipophone? Would the techniques you used in Retronitus be adaptable to such a use?

    Of course they are. Chipophone is an Atmega based synth, which accepts midi type input and produces square waves, triangle waves or noise. I think Linus having all his knowledge and Propeller code base can replace Atmega with Propeller in some minutes.

    This "minimalistic synth" is awesome production.
  • tonyp12tonyp12 Posts: 1,950
    edited 2012-04-27 22:27
    I'm playing it back on my new 1watt Quickstart Speaker Board
    Video:https://skydrive.live.com/redir.aspx?cid=6d7787b33d967b1a&resid=6D7787B33D967B1A!289&parid=6D7787B33D967B1A!141
    (soon available for $14.95, comes with 128k spi flash)
    Johannes, pm mej din adress s
  • roglohrogloh Posts: 5,158
    edited 2012-04-27 23:05
    This rules. An amazing set of features for such a minimalist implementation and it sounds pretty decent to me. Still trying to figure out how it all works. I've done some PWM and DDS audio stuff before on the AVR but not the prop.

    You are the king of audio around here! :thumb:
  • Ahle2Ahle2 Posts: 1,178
    edited 2012-04-28 01:35
    @rogloh
    Here is the pseudo code for the sound generation part
                  [B]actualFrequency [/B]-= ([B]actualFrequency [/B]- [B]wantedFrequency[/B]) >> [B]frequencyDeltaShiftAmount           [/B]
                  [B]actualVolume [/B]-= ([B]actualVolume [/B]- [B]wantedVolume[/B]) >> [B]volumeDeltaShiftAmount     [/B]
    
                  if [B]modulationAmount [/B]< 512
                     [B]pulseWidth [/B]:= [B]modulationAmount [/B]<<  23  '  Shifts the 9 bit value to the MSBs of the 32 bit pulse width register
                  else
                     [B]pulseWidth [/B]+= [B]modulationAmount 
    [/B] 
                  [B]phaseAccumulator [/B]+= [B]actualFrequency [/B]
    
                  if [B]phaseAcumulator [/B]> absoluteValueof([B]pulseWidth)
                    wantedOutput := [/B][B]actualVolume [/B]     
                  else
    [B]                wantedOutput := -[/B][B]actualVolume [/B]   
    
                  [B]actualOutput [/B]+= ([B]actualOutput [/B]- [B]wantedOutput [/B]) >> [B]outputDeltaShiftAmount 
    
                  [/B]outputToSpeaker(dcOffset + [B]actualOutput[/B])
    
    Some notes:
    * The logaritmic behaviour comes from "the divided delta feedback approach"
    * It's only possible to handle power of two rise/fall times with shifting
  • Ahle2Ahle2 Posts: 1,178
    edited 2012-04-28 01:51
    @Cluso99, Dr_Acula, Oldbitcollector (Jeff), rogloh, pik33

    Thanks for the kind words, but to be honest this isn't revolutionary in any sence of the word!
    SIDcog and Retronitus are the real achievements of course.
  • Ahle2Ahle2 Posts: 1,178
    edited 2012-04-28 01:52
    @tonyp12
    Lol... SVEN!!!
  • average joeaverage joe Posts: 795
    edited 2012-04-28 04:02
    Once again, great work! I'm DL tonight and will have to play around with it next week hopefully. I've been thinking about a minimalist tone generator with a LFO controlled Filter Cutoff sweep. This looks like a promising starting point for my idea! Keep it up SoundMaster!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-05-05 00:31
    Thanks for this synth. I was looking for something quick and simple to test audio on the touchscreen board.

    It works brilliantly. Volume is fine with headphones (?32 ohm) and a 1k/0.01uF lowpass filter and no amplifier needed for headphones.

    Touch the music icon and your synth plays - see photo.

    Now to combine this with a picture of a keyboard so you can play the synth...
    640 x 480 - 66K
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-05-05 01:15
    You can now play the keyboard. Touchscreen is a little small for a finger but it works with a stylus or pen. Knobs are not working but with the software and the slider knobs and textboxes already working it should be possible to get them to move and change the sound of the notes.

    I see your notes are midi so they could come in from a real keyboard too.

    This synth is really fun!
    PUB PlaySynthesizer | xval,yval
        synth.start(21, 23)  
        Clearscreen(%00000000_00000000)                     ' so something happens immediately
        ClearRamDiskExcludeDesktop 
        LoadBMPtoRamdisk(string("synth.bmp"))
        DrawBMP(string("synth.bmp"),0,0)
        SelectSPIGroup                                        ' talk to the spi touchscreen
        repeat
          yval := TouchYPercent                               ' decode yval 0-100%               
          xval := TouchXPercent                               ' decode xval 0-100%
          if (xval <> 255)  and (yval <> 255)                    ' valid keypress
             xval := (xval*24)/10                             ' 0-240, use portrait mode but it doesn't matter  
             yval := (yval*32)/10
             if xval>220 and yval >300
               synth.stop                                     ' close down the synthesizer
               return
             if xval < 53                                      ' white keys     
              case yval                                     
                 0..29 : synth.pluck(20, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)                              
                 0..59 : synth.pluck(22, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)
                 60..89: synth.pluck(24, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)
                 90..119 :synth.pluck(25, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)
                 120..148: synth.pluck(27, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 149..177: synth.pluck(29, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 178..207: synth.pluck(31, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 208..238: synth.pluck(32, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 239..266: synth.pluck(34, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 267..297: synth.pluck(36, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 298..320: synth.pluck(37, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)
             if (xval > 54) and (xval < 118)                     ' black keys a little bit wider so can play them
               case yval
                 10..44: synth.pluck(21, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 47..77: synth.pluck(23, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)
                 102..131: synth.pluck(26, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 133..162: synth.pluck(28, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 166..193: synth.pluck(30, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 221..250: synth.pluck(33, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)  
                 255..284: synth.pluck(35, tb1_decRate, tb1_susLevel, tb1_pwinit, tb1_pwmRate, 1)     
            pause1ms(250)' to "debounce" 
    
    640 x 480 - 49K
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-05-09 06:19
    Hi Ahle2,
    It was really not meant to be released or even usable as a synth. I just thought that it might be of interest to someone else.

    I tweaked your synth a bit. Now it can be played with a stylus. I found some pictures of proper retro slider pots and switches from an online synth and then added a bit of woodgrain round the outside. Slider pots, switches and red LEDs. Has to look retro! Then just plug all the bitmaps together with your code and you can play it.

    Spin synth code ended up about the same size as your demo code - remove the automatic tune playing code and replace with code that reads the slider pots and the keyboard and plays the notes.

    Hmm - small problem. My wife is a music teacher and she says it needs a polyphonic keyboard so she can play "chopsticks" with two styluses! Maybe need to add Rayman's capacitive screen? Or a MIDI keyboard. The MIDI should be easy - just add an optocoupler on the RS232 port.

    Thanks for this code. Maybe we can add more features from some of your more sophisticated synths?
    PUB PlaySynthesizer | xval,yval,portsw,note ' see next routine for automatic tunes
        synth.start(21, 23)                                 ' start the synth
        portsw := false                                     ' portamento off
        PWinit := 255                                        ' synth settings all 0-255
        pwmRate := 12
        decRate := 200                                      
        susLevel := 255
        filter := 16
        portamento := 6  
        Clearscreen(%00000000_00000000)                     ' 
        ClearRamDiskExcludeDesktop 
        LoadBMPtoRamdisk(string("synth2.bmp"))
        LoadBMPtoRamdisk(string("synoff.bmp"))              ' switch off
        LoadBMPtoRamdisk(string("synon.bmp"))               ' switch on with led
        LoadBMPtoRamdisk(string("synknb.bmp"))              ' slider knob
        LoadBMPtoRamdisk(string("synslbk.bmp"))             ' slider background
        DrawBMP(string("synth2.bmp"),0,0)
        DrawBMP(string("synoff.bmp"),148,241)
        DrawBMP(string("synon.bmp"),148,283)
        RefreshSliders                                      ' draw all the sliders                              
        SelectSPIGroup                                        ' talk to the spi touchscreen
        repeat
          yval := TouchYPercent                               ' decode yval 0-100%               
          xval := TouchXPercent                               ' decode xval 0-100%
          if (xval <> 255)  and (yval <> 255)                    ' valid keypress
             xval := (xval*24)/10                             ' 0-240, use portrait mode but it doesn't matter  
             yval := (yval*32)/10
             if xval>143 and xval <199 and yval >280 and yval <320
               synth.stop                                     ' close down the synthesizer
               return
             if xval >143 and xval <199 and yval > 240 and yval < 275 ' portamento switch
               SelectMemGroup
               !portsw                                        ' change status of portamento
               if portsw
                     DrawBMP(string("synon.bmp"),148,241)                          ' on or off
                     synth.noteOn(30, 19, 60, 12, 3, 6)                             ' turn on a note, next notes slide to this 
               else      
                     DrawBMP(string("synoff.bmp"),148,241)
                     synth.noteOff(18)                                              ' turn off any portamento
               pause1ms(250) ' debounce delay      
               SelectSPIGroup
             if xval >132 and xval <202 and yval >9 and yval <244                   ' one of the 5 sliders
               case yval
                 9..9+34:   DecRate := ReadSlider(xval,1)                             ' move the slider and scale value
                 53..53+34: SusLevel := ReadSlider(xval,2)     
                 88..88+34: PWinit := ReadSlider(xval,3)
                 124..124+34:PWMRate := ReadSlider(xval,4)
                 162..162+34:Filter := ReadSlider(xval,5)
                 210..210+34:Portamento := ReadSlider(xval,6)
             if xval < 53                                                           ' white keys     
              case yval                                     
                 0..23 : note := 20                              
                 24..56 : note := 22
                 57..89: note := 24
                 90..119 : note := 25
                 120..148: note := 27  
                 149..177: note := 29  
                 178..207: note := 31  
                 208..238: note := 32  
                 239..266: note := 34  
                 267..297: note := 36  
                 298..320: note := 37
             if (xval > 54) and (xval < 118)                     ' black keys a little bit wider so can play them
               case yval
                 10..44: note := 21  
                 47..77: note := 23
                 102..131: note := 26  
                 133..162: note := 28  
                 166..193: note := 30  
                 221..250: note := 33  
                 255..284: note := 35
             if note <> 0                                                           ' play note
               if portsw                                              ' portamento switch
                  synth.slideToNote(note, portamento/16) 
               else
                 synth.pluck(note, decRate/12, susLevel/4, pwinit, pwmRate/6, filter/32)        ' do any scaling here, all values 0-255
               note := 0
    
    PUB SynthSlider(x,y,value)                              ' value is 0-255. assumes bitmaps in ram
        DrawBMP(string("synslbk.bmp"),x,y)
        DrawBMP(string("synknb.bmp"),x+((value*100)/450),y+5)
    
    PUB RefreshSliders | n
        repeat n from 1 to 6
          RedrawOneSlider(n)
    
    PUB ReadSlider(y,n)
        y := y - 132                                            ' origin of the slider is 132
        y := y*256                                              ' slider is 69 pixels so convert to 256   
        y := y / 69
        if y < 0                                                ' check bounds
          y := 0
        if y > 255
          y := 255
        SelectMemGroup
        RedrawOneSlider(n)                                  ' redraw this slider
        SelectSPIGroup     
        result := y                    
    
    PUB RedrawOneSlider(n)                                  ' n = 1,2,3,4,5,6
       case n
         1:SynthSlider(132,9,decRate)                           ' decay     
         2:SynthSlider(132,53,susLevel)                         ' sustain
         3:SynthSlider(132,88,pwinit)                           ' pwminit
         4:SynthSlider(132,124,pwmRate)                         ' pwmrate
         5:SynthSlider(132,162,filter)                          ' filter (0-12 so multiply by 21)
         6:SynthSlider(132,210,portamento)                       ' portamento 0 - 16
         
    
    640 x 480 - 58K
  • mindrobotsmindrobots Posts: 6,506
    edited 2012-05-09 07:07
    Dr A. and Johannes, this is brilliant. Once again the humble Propeller is stretched in ways people can only imagine!

    "Propeller land, where the impossible comes to die"

    The company I work for has a current campaign "Rethink Possible" - this forum does a much better job at that than my company does!!

    {Of course, under my breath, I'm cursing you guys since this is YET ONE MORE project I need to try.}
  • average joeaverage joe Posts: 795
    edited 2012-05-09 07:14
    Serial Midi from the computer is easy. Hardware midi is fairly simple too. And I was just thinking about poly and my solution! 2 screens! Shouldn't be hard.
    Very nice work as always, I can't wait to get this running!
  • pik33pik33 Posts: 2,350
    edited 2012-05-09 09:07

    Dr A. and Johannes, this is brilliant. Once again the humble Propeller is stretched in ways people can only imagine!

    "Propeller land, where the impossible comes to die"

    This is THE chip which returned programming joy back to me. Back from these '80s. There is a place for creativity with a Proprller, for something like "this is hard/impossible, so I have to do it". Like these old home computers from 80's when the manual says - four color are possible to display on the screen - then they displayed a colorful photos with it using weird timing and other programmer's black magic. Or simple atari Pokey chip from which someone maked a software synthesizer.

    Today I made my first VGA driver with Propeller - it was a real fun to see this good old Atari ST font back on my screen. This - "It works" - and that I can control every aspect of my display. Pixel clock. Line number. Sync time. All I can play with, experiment with and see, if it works and how it works. This is real fun. Not these protected library code I have to call on PC. A hundred line of code to initialize a library. Then no way to tell it, how I want to display my content. I can only call "swap buffer". This of course works, but there is no fun with it. If it takes a 10 miliseconds to swap these buffers, you cannot do anything. Nothing. You can only go, buy a faster cpu. A faster gpu. Buy, Replace. Wait for a new hardware...
  • Ahle2Ahle2 Posts: 1,178
    edited 2012-05-09 11:50
    @Dr_Acula
    One word: Awesome!
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2012-05-09 13:24
    Wow..

    That is amazing work @Dr_A!

    OBC
  • potatoheadpotatohead Posts: 10,253
    edited 2012-05-09 20:30
    Totally. A few "impossibles" knocked down in this one. Great stuff!!

    @Pik33 Yep. Me too. Just this last weekend, I've been working on TV drivers and color again. Always fun. Eric Ball has put up some underutilized signal code that I'm just now getting to jam on a little. Love the control, because it allows a person to explore what really is possible, instead of what is supplied or stated. Great fun!
  • Ahle2Ahle2 Posts: 1,178
    edited 2012-05-19 06:22
    I've uploaded a new version of TinySynth; It has now increased to a whooping 37 rows of assembly.
    Changes from the first version includes noise generation and asymmetric filter.
    The noise generator is an "or-modulation" of the phase accumulator and the modulation value. This makes it possible to generate everything from bells to white noise.
    The asymetric filter makes it possible to generate saw waves and all kinds of hybrid waveforms. (half triangle, half saw for instance)
    (Still no multiplications or lookup tables used)

    /Johannes
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-05-19 17:33
    Cool. I wonder if we could get a "bomb drop plus explosion" out of this? eg a falling tone and then some white noise fading away - just like in all the old arcade games.

    I'll need to add some more slider pots. I'm working on a simple sequencer with a nice music font. If we wrote this in music, the bomb drop could be a slide trombone (portamento), and the explosion could be percussion.

    Have you got a link to the latest code by any chance? This is going to be fun!
  • HShankoHShanko Posts: 402
    edited 2012-05-19 18:19
    This might be off topic, but is there any information how to use TinySynth, SIDcog or Retronitus with a MIDI keyboard (with or without velocity-control)? I note there are some USB MIDI keyboards under $100 which seem they might be of use. Not sure if the 'old' MIDI is better/easier to work with than USB. Right off I'd think the USB MIDI is more HID than what a PropBOE requires; which I have.

    I didn't find Retronitus in the OBEX; where might it be accessed?

    I'm rather new to this music area, so may not realize how much that's usable this way.

    Goal is a rather simple keyboard and a way to synthesize sounds. I've listened to a number of videos and enjoy many of the sounds created. Including the one by Akesson (sp?) which I thought impressive.
  • Ahle2Ahle2 Posts: 1,178
    edited 2012-05-20 01:01
    @Dr_Acula
    Have a look at the first post for the new version!
    "bomb drop plus explosion" should be possible to do with a few timed calls to TinySynth methods.

    Should I do polyfonic version of TinySynth? I will, o
    f course, have to rename it since it will not be tiny anymore!

    @HShanko
    Retronitus is the ultimate solution for anyone interested in making old school music and sound FX for games.
    The sad thing is that I kind of lost momentum because the sequencer/sound editor I'm developing just bugs me out. (fun intended)
    It's not in the OBEX, because it's not done yet! :(

    I have personally not been thinkering with MIDI on the prop yet, but I do have a lot of MIDI equipped synths in my "home studio".
    One of my Propeller projects will include a MIDI port, so it's just a matter of time before I will be able to answer your questions.
    Until then talk to "Ariba", "
    AntoineDoinel" or "average joe" and I'm sure you will get all your answers.
    (One thing I do know is that "real" MIDI is a lot easier to handle than MIDI over USB)
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-05-20 02:57
    Hey, brilliant work.

    How could you do polyphony? Do you have to work out the decay values of several notes and add them together?
  • average joeaverage joe Posts: 795
    edited 2012-05-20 05:27
    @HShanko, there are a few different ways to do Midi with the prop. The method that would be best for you depends on what you are trying to interface with. If you intend on using a PC to play the synth, serial midi is the best bet. If you plan on using a keyboard, hardware midi in is probably best. I will assume you're looking for hardware midi. I would download this object in the obex: http://obex.parallax.com/objects/229/ There is a HTML file with a midi-in circuit that works great!

    Gotta run for now but let me know if you need more help.
  • HShankoHShanko Posts: 402
    edited 2012-05-20 13:32
    @ Ahle2 and average joe, Thank you for the comments.

    I downloaded from OBEX the MidiIn. On BST I couldn't compile; got error. Found that, for some reason, BST wouldn't display the first line, which was where the error was. Found it by looking at it with another app and saw 4 strange characters on the first line. Also, BST cursor entry is one line below where the actual edit point is. Temporarily working around that.

    Once repaired the first line it compiles. Now without a MIDI interface (yet) or cables, I can go no further.

    I searched the source for what pin MIDI should connect to the Prop; I'll have to look for that info further. I was hoping to hear something on the PropBOE. Nada.

    I realized I do have a MIDI device; a Korg M1; yes, old; even listed on Wikipedia !!! I'll have to see if that is working. The M1 top surface has been used to store some papers and boxes for some years now. Out of sight and out of mind.

    Looking at the M1 manuals I note it does have MIDI; 3 DIN for THRU, OUT and IN. Old MIDI, not USB MIDI. Time to order parts for that MIDI to single-ended circuit and working with MidiIn
  • average joeaverage joe Posts: 795
    edited 2012-05-20 13:36
    So you able to find the schematic for the midi in? The Korg should work fine! Midi-In pin can be whatever you like.
  • HShankoHShanko Posts: 402
    edited 2012-05-20 14:30
    @ average joe,

    Yes. The MidiIn zip file has three files; MidiIn.spin, MIDI in for Propeller.htm, and midi.jpg. The last two show the circuit.

    I just realized that MidiIn does not do any synthesis. That is up to other code it appears; like for the PropB3 'Hammond' organ code.. I couldn't find the code to PropB3.
  • average joeaverage joe Posts: 795
    edited 2012-05-20 14:37
    Check here for a good start : Check post #26!
  • HShankoHShanko Posts: 402
    edited 2012-05-20 16:00
    Thanks again, average joe.

    I've printed out both the MidiIn driver and TinySynth. That lets me be anywhere and check out the sources. I don't care to read on-screen; not with bifocals or small type, or bad color text, etc.

    It's a bummer that BST doesn't allow printing; at least I don't see it.

    My iMac with Parallels/Windows is really slow; minutes to make some keyboard or mouse selections. HD just grinds away and doesn't let me have my turn at inputting. So have to use other ways of printing, thus losing formatting as PropTool provides.
Sign In or Register to comment.