Music Synthesizer object with General MIDI sound set

13

Comments

  • laserjoneslaserjones Posts: 14
    edited 2009-09-22 - 09:05:32
    Thanks a lot, Andy!
    Ariba said...
    The whole cog has a single loop which runs exactly with a rate of 32 kHz.
    How do you achieve this exact frequency (and the 12 time slices) if you don't use the hardware counters? Or are you using the same 32 kHz clock for the DACs?

    Is the triangle generation considerably faster than using the sine table?

    So you generate two triangle oscillators per voice, and then you modify the frequency register of one oscillator by the amplitude value of the other?
  • AribaAriba Posts: 2,212
    edited 2009-09-22 - 17:51:20
    > How do you achieve this exact frequency (and the 12 time slices) if you don't use the hardware counters? Or are you using the same 32 kHz clock for the DACs?

    I use the system counter CNT. The exact periode is done with the WAITCNT tm,rate at label loopend.
    The time slices are just code parts in the loop, that are written one after the other, and every part needs a defined amount of time.
    All the parts together must be a little bit shorter than the available ticks for the periode. The waitcnt then waits for the exact time.
    The rate-ticks are calculated in the Spin start methode from the clkfreq, and passed to the assembly code.

    > Is the triangle generation considerably faster than using the sine table?

    Oh yes. Generating the absolute value is a single PASM instruction (or a half in case of ADDABS [noparse]:)[/noparse]. The ROM sine table stores only 1/4 of the sine and you need shifting and swapping to get a full sine cycle. You need minimal 8 instruction and one is a hub access (RDWORD) which can take a lot of time.

    > So you generate two triangle oscillators per voice, and then you modify the frequency register of one oscillator by the amplitude value of the other?

    Not exactly, this would be FM. The modulation is a mix of the OSC2-Triangle with the OSC1-Triangle and the DAC output multiplied with the modulation intensity (PM and FB). This modulation is added to the phase of OSC1 before I do the Saw to Triangle translation. That's why I call it Phase Modulation synthesis. The exact modulation path is shown in the diagram at begin of the source code.
    (Yamaha's FM synthesis is in fact also a phase modulation synthesis, they just call it FM. You hear the difference at low modulation frequencies. With FM you would get a heavy vibrato, with PM just a phasing. Modulation with audio frequencies gives the same result with both types).

    Andy
  • laserjoneslaserjones Posts: 14
    edited 2009-10-21 - 10:15:41
    Hello again,

    I've been thinking again about your design, and I wonder how you achieve the required frequency resolution with a master clock of only 32 kHz. For example, the highest note on a piano is 4.186 kHz. If we divide your 32 kHz by this, the result is 7.64 clock ticks per wave cycle. But since the phase register can only flip over at a whole-numbered clock cycle, we would need to round this to a divider of 8, which would result in 4.0 kHz instead of 4.186 kHz, which is an error of almost 5 %. Even worse: The second-highest piano note (3.951 kHz) would require a divider of 8.1 - if we round that, we have 8 again, i.e. the same frequency as for the other note. So this error is obviously too large, because the frequency difference between two adjacent notes is only about 6 %.

    So I assume your program cannot play notes with such high frequencies? What is the highest note you can produce with sufficient accuracy?


    EDIT: It just dawned on me that the DDS principle is probably even smarter than I thought. Am I right to assume that in case of the highest note mentioned above, the wave period would automatically vary between 7 and 8 master clock ticks with a ratio that would result in an average frequency of 4.186 kHz? But even if this is the case, these variations (jitter) are probably audible at high frequencies, right?

    Kind regards,
    Joerg

    Post Edited (laserjones) : 10/21/2009 10:59:58 AM GMT
  • AribaAriba Posts: 2,212
    edited 2009-10-23 - 03:18:12
    Hello Joerg

    Yes, DDS is smart enough to compensate the error in the next periode, so it's no problem to play a note with 4.186 kHz.
    The frequency resolution is in fact: 32kHz / 2^32 = 0.0000074 Hz for every oscillator. I.e. you can detune the 2 oscillators by 0.1 Hz and get a phasing with 10 seconds periode.
    You should see it more like a 4.186 kHz Signal sampled with 32 kHz, the ratio between the 32kHz and the signal frequency is not important, the 32 kHz defines only the rate of the single computed samples.
    If you have a 4.1 kHz sine then then all is OK, if you want a 4.1 kHz square wave then you will hear a lot of jitter (aliasing). So the highest frequency is theoretical 16 kHz, but in reality maybe 6..8 kHz for a sine wave (= 4..5 samples per periode).

    Andy
  • laserjoneslaserjones Posts: 14
    edited 2009-10-23 - 08:53:37
    Ah, OK - when I think about it, it's clear that the jitter is not such a big problem with sine or triangle waves, because the amplitude is low at the border between two periods, where the inaccuracies occur. Thanks a lot for the good explanation!

    So if I want to make a synthesizer that also does square waves, I'd need a higher master frequency, which should be no problem if I use less oscillators per cog than your program. I'll let you know when I have something to show.

    EDIT: Wait a minute - something must be wrong with your formula (32 kHz / 2^32 = 0.0000074 Hz), because increasing the master clock frequency should result in a better resolution, i.e. a smaller value, but in your formula the value increases with the clock frequency ...??

    Joerg

    Post Edited (laserjones) : 10/23/2009 9:01:33 AM GMT
  • AribaAriba Posts: 2,212
    edited 2009-10-23 - 17:04:45
    laserjones said...
    Wait a minute - something must be wrong with your formula (32 kHz / 2^32 = 0.0000074 Hz), because increasing the master clock frequency should result in a better resolution, i.e. a smaller value, but in your formula the value increases with the clock frequency ...??

    No, the formula is correct. With frequency resolution, I mean how exactly can you define a desired frequency with the freq-register. Or in other words the effect of changing one LSB of the frequency value added to the phase register. With higher sampling frequency this resolution is lower (the LSB steps are higher), but still ways better than what you need for a synthesizer (thanks to the 32 Bit processor). To calculate the needed resolution:
    Say the lowest note plays with 30 Hz, and we need 0.1% accuracy, then 30 / 1000 = 0.03Hz resolution is good enough. These would need a DDS with 20 Bits.

    > So if I want to make a synthesizer that also does square waves, ...

    No synthesizer or other music instrument (synthetic or accoustic) needs square waves with 4 kHz. This is just not hearable by our ears. A 4 kHz square wave has strong harmonics at 12, 20, 28 ... kHz and frequencies over 16..20 kHz can not be hear.
    A subtractive synthesizer with square wave oscillators has a Low pass filter that filters out this high frequency to get accomodate sounds. A additive synthesizer like FM or my PM needs sine (or triangle) oscillators otherwise the harmonics, produced by the modulation, can't be controlled good enough. The original FM synthesis has Key-scaling of the modulation intensity for every oscillator, so that the modulation at higher frequencies goes to zero.

    But with a higher sample frequency (48, or 64, 96 kHz) you will get a better sound quality anyway. Mainly because of the reduced aliasing. But then you need perhaps also better DACs and a strong 20 kHz Low pass filter at the output and a more detailed synthesis algorithm.

    Andy
  • laserjoneslaserjones Posts: 14
    edited 2009-10-23 - 19:28:16
    Ariba said...
    With higher sampling frequency this resolution is lower
    It took me a while to understand that you are right, but you definitely are. smile.gif
    Ariba said...
    No synthesizer or other music instrument (synthetic or accoustic) needs square waves with 4 kHz. This is just not hearable by our ears.
    OK, good point. But it would not be elegant having to switch the oscillator from square to triangle above a certain frequency. Well, another possibility would be a simple fixed low-pass filter that removes the higher harmonics. The question is whether that would remove the jitter to the same degree as if it had been a triangle right from the start. But since I'll go for a higher sample frequency, I probably won't run into the problem anyway.

    Thanks again for the interesting discussion!
    Joerg
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,090
    edited 2009-12-03 - 13:31:55
    @Andy,

    I've got a little game in the Hydra forum which incorporated your midi player for
    background music. (Christmas Catch) The midi file is actually only 8k in size.

    Can you give an example of how to use your gm_synth, loading the midi file
    as a DAT inclusion? Is this possible?

    Thanks again for a great sound driver!

    OBC

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    New to the Propeller?

    Visit the: The Propeller Pages @ Warranty Void.
    <br>
  • BaggersBaggers Posts: 2,965
    edited 2009-12-03 - 14:02:27
    OBC, without looking at the code, I'd say change the the FSRW stuff in gm_synth, to a new func, and have POPEN, PREAD functions,

    POPEN would set a pointer to the beginning of the dat inclusion, and PREAD would read the byte from where the pointer is and increase the pointer [noparse]:D[/noparse]

    You may need filesize possibly seek but maybe not, so don't quote me on that, as I've not looked at the source for gm_synth in a long while.

    that's how I'd do it without modding the gm_synth too much, that way, you can just have a comment on the object you want.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    http://www.propgfx.co.uk/forum/·home of the PropGFX Lite

    ·
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,090
    edited 2009-12-03 - 14:10:33
    Thanks Baggers..

    Those two commands are significant gaps in my spin knowledge.
    I'll be in the spin manual tonight! Do you happen to have any good examples
    in any of your code on these commands?

    Thanks
    OBC

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    New to the Propeller?

    Visit the: The Propeller Pages @ Warranty Void.
    <br>
  • BaggersBaggers Posts: 2,965
    edited 2009-12-03 - 14:12:59
    tell you what OBC, mail me your source project so far, with the midi file included, and I'll get it working for you.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    http://www.propgfx.co.uk/forum/·home of the PropGFX Lite

    ·
  • AribaAriba Posts: 2,212
    edited 2009-12-03 - 15:02:26
    OBC

    Attached is a version of the MIDI player which plays a MIDI file from HubRAM.
    You can delete all the show-methode calls in the midiplay methode, and the show methode itself.
    As you see, I have just replaced the pread to a local methode which reads from HubRAM, exactly as
    Baggers suggested (but I've made it some month ago [noparse]:)[/noparse]

    Andy
  • BaggersBaggers Posts: 2,965
    edited 2009-12-03 - 16:02:48
    Thanks Ariba [noparse]:)[/noparse]

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    http://www.propgfx.co.uk/forum/·home of the PropGFX Lite

    ·
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,090
    edited 2009-12-03 - 17:48:14
    Positively perfect! Thank you Andy!

    An excellent drop in for getting simple music/sound into games. AWESOME.

    OBC


    Edit: Still planning to sit down with the book and this source and read up on PREAD & POPEN.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    New to the Propeller?

    Visit the: The Propeller Pages @ Warranty Void.
    <br>
  • HannoHanno Posts: 1,130
    edited 2009-12-08 - 00:04:02
    Here's a great midi site: musicby.jw-music.net/midi.php?critere=taille&lang=us
    Some need to be converted to format 0.
    I do have problems getting ~60% of files to play correctly- many get stuck on one note.
    What should I look out for?
    I would love to include a "Play Midi" block in 12Blocks- is that ok? I might write a simple virtual keyboard that would let kids play music to later download to the Prop.
    Hanno

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Co-author of the official Propeller Guide- available at Amazon
    Developer of ViewPort, the premier visual debugger for the Propeller (read the review here, thread here),
    12Blocks, the block-based programming environment (thread here)
    and PropScope, the multi-function USB oscilloscope/function generator/logic analyzer
    Professional IDE to edit, debug, and run SPIN, PropBasic and C: ViewPort
    Visual programming language: 12Blocks
    Multi-function Oscilloscope/LSA/Function Generator: PropScope
    500 page book of Propeller Projects:Programming and Customizing the Multicore Propeller
    Blog:http://onerobot.org/blog
  • James LongJames Long Posts: 1,181
    edited 2009-12-08 - 00:09:02
    Hanno said...
    Here's a great midi site: musicby.jw-music.net/midi.php?critere=taille〈=us
    Some need to be converted to format 0.
    I do have problems getting ~60% of files to play correctly- many get stuck on one note.
    What should I look out for?
    I would love to include a "Play Midi" block in 12Blocks- is that ok? I might write a simple virtual keyboard that would let kids play music to later download to the Prop.
    Hanno

    Hanno,

    What happens is multi tracks get combined in to one. If the two tracks had a note of the same type, the note on and the note off create a mess in the single track (format 0). There are overlapping notes, and one of those needs to be removed, which can be a major chore. You need to find software to convert which knows to remove the overlapping notes. I found this out when I started playing with midi about a year ago.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer
    Lil Brother SMT Assembly Services

    Are you addicted to technology or Micro-controllers..... then checkout the forums at Savage Circuits. Learn to build your own Gizmos!
  • AribaAriba Posts: 2,212
    edited 2009-12-09 - 04:33:24
    Hanno

    I know there is a problem with some MIDI files. The critical part is the begin of the file. Depending on the Sequencer and Target synthesizer there are a lot of META events and SysEx (system exclusiv) messages at begin. From the description I had, when I
    coded the Midi file interpreter, I found no general code which works for all MIDI files. If I debugged one file and made changes,
    then another doesn't work anymore.

    Because the MIDI file part was only meant as a demo for my Synthesizer object, I was happy how it worked with the demo files.
    Now I have maybe 100 others on my SD card which work well.

    What it needs to improve it is a better description of the special parts of a MIDI file, or perhaps just a MIDI converter which filters out all the SysEx messages.
    If you want look into it, it's the CASE $F0 part of the MIDI byte decoder, that must be changed. Now it's a little mess and only
    made to get the Tempo information out of the file.

    A virtual keyboard that plays notes should not be a problem, the synthesizer object works well, only AllNotesOff seems to have
    a little problem sometimes.

    Andy
  • HannoHanno Posts: 1,130
    edited 2009-12-09 - 06:48:40
    Thanks Andy- I thought as much.

    12Blocks already includes graphical wizards to let you easily do things like draw graphic sprites and vector drawing. I'm thinking of coding up a simple virtual keyboard which reads/writes midi files. That would let people drag a block called "play midi" to their worksheet and click it's parameter to play music. Pressing "run" would compile the spin file and load the Propeller. Currently I support wav files, speech synthesis, tones, and recording wav files in the audio section of the library.
    Got any links that would help me start? Sample program which outputs midi notes? A dll which does this? I'd prefer not to reinvent the wheel...

    A link to 12blocks is in my sig...
    Hanno

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Co-author of the official Propeller Guide- available at Amazon
    Developer of ViewPort, the premier visual debugger for the Propeller (read the review here, thread here),
    12Blocks, the block-based programming environment (thread here)
    and PropScope, the multi-function USB oscilloscope/function generator/logic analyzer
    Professional IDE to edit, debug, and run SPIN, PropBasic and C: ViewPort
    Visual programming language: 12Blocks
    Multi-function Oscilloscope/LSA/Function Generator: PropScope
    500 page book of Propeller Projects:Programming and Customizing the Multicore Propeller
    Blog:http://onerobot.org/blog
  • James LongJames Long Posts: 1,181
    edited 2009-12-20 - 01:53:03
    Does anyone know how fast Midi can output events.

    Like, how many note on and note off events can Midi output per second?

    I'm working to output the notes to a I2C bus with 8 chips. Each chip takes 3 bytes of info, and the bus runs at 400K.

    I wondering if I have enough bandwidth to do it.

    Anyone.....Andy??

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer
    Lil Brother SMT Assembly Services

    Are you addicted to technology or Micro-controllers..... then checkout the forums at Savage Circuits. Learn to build your own Gizmos!
  • kerrywkerryw Posts: 61
    edited 2009-12-20 - 02:58:23
    James Long said...
    Does anyone know how fast Midi can output events.

    Like, how many note on and note off events can Midi output per second?

    I'm working to output the notes to a I2C bus with 8 chips. Each chip takes 3 bytes of info, and the bus runs at 400K.

    I wondering if I have enough bandwidth to do it.

    Anyone.....Andy??

    James L

    Midi runs a 31,250 baud. It uses 10 bits per frame (1 start, 8 bits of data, 1 stop). The various messages look like they take up from 1 to 3 frames each. This links seems to cover the spec fairly well : http://www.srm.com/qtma/davidsmidispec.html


    Kerry

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "Heaven help me Marge, I'm just not that smart." - Homer Simpson
  • James LongJames Long Posts: 1,181
    edited 2009-12-20 - 03:24:13
    kerryw said...
    Midi runs a 31,250 baud. It uses 10 bits per frame (1 start, 8 bits of data, 1 stop). The various messages look like they take up from 1 to 3 frames each. This links seems to cover the spec fairly well : http://www.srm.com/qtma/davidsmidispec.html

    Kerry

    Kerry,

    Thanks for the link, that is exactly what I needed to figure out.

    It looks like I have a very narrow margin but should be able to output to all 8 chips and still have a small amount of bandwidth left on the I2C bus.

    For anyone interested here is how I calculated this:

    400K bus
    8 I2C chips on the bus

    400,000 / 8 = 50,000 bps for each chip in reality.

    It takes about 28 bits to set the outputs on the I2C chip (3 bytes plus start, stop and ack). We are going to use 32 to be safe.

    50,000 / 32 = 1562.5 events per second.


    Midi comes in at 31250 bps

    If each event takes 30 bits then

    31250 / 30 = 1041.6 events per second (note on/note off events).


    All this above means if you want to output each note to an I2C bus with PCF 8575 chips, it is possible. It does require a fast I2C bus communication like Mike Green has in some of his objects. The bus must be run at maximum speed (400K) for it to work.

    The above shows the bus at maximum speed (Fast speed of 400K) will have a speed advantage of 66% (approximately).

    This is great news for the design I've been working on.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer
    Lil Brother SMT Assembly Services

    Are you addicted to technology or Micro-controllers..... then checkout the forums at Savage Circuits. Learn to build your own Gizmos!
  • John A. ZoidbergJohn A. Zoidberg Posts: 514
    edited 2010-10-20 - 07:59:14
    Hello there,

    Sorry to bump up an old post, but I found the object very interesting, and I wished to include it into one of my projects.

    However, the intended pins to the SD card (0-4) are already occupied, and I want to connected it to somewhere between pins 20-24.

    Could it be possible? :)
  • K2K2 Posts: 627
    edited 2010-10-20 - 08:14:53
    I'm glad you bumped it up - I didn't know of its existence. It's fantastic!
  • John A. ZoidbergJohn A. Zoidberg Posts: 514
    edited 2010-10-20 - 08:21:57
    Thanks. I'm trying it for my musical clock, and also a Christmas present (it's an electronic Christmas tree) for my close friend.

    In fact, I tried to design the DDS by myself, but ah, that package offered is better.

    I sorted out the pins - I'll try it later tomorrow morning. I need to find a SD-card holder, or I have to homebrew it by using the header pins. :)
  • AribaAriba Posts: 2,212
    edited 2010-10-20 - 11:22:32
    John

    It should work with any set of pins as long as they are in the right order for the old FSRW object. The first parameter in the CON section defines the SD basepin (as you have noticed already).
    It will be not very hard to change it to the new FSRW, then you can use any pins for the SD and also SD cards over 2GB.

    But if you want to play only 1 MIDI file and this is under 22 kByte then you can use the attached Spin file. It includes the MIDI file into the HubRAM and plays it from there. No SD card is needed!
    You find the "NUTRAC.MID" for this example in the ZIP file of the first post.

    Andy
  • John A. ZoidbergJohn A. Zoidberg Posts: 514
    edited 2010-10-20 - 18:48:47
    Ariba wrote: »
    John

    It should work with any set of pins as long as they are in the right order for the old FSRW object. The first parameter in the CON section defines the SD basepin (as you have noticed already).
    It will be not very hard to change it to the new FSRW, then you can use any pins for the SD and also SD cards over 2GB.

    But if you want to play only 1 MIDI file and this is under 22 kByte then you can use the attached Spin file. It includes the MIDI file into the HubRAM and plays it from there. No SD card is needed!
    You find the "NUTRAC.MID" for this example in the ZIP file of the first post.

    Andy

    Thanks for the help. By the way, could it be possible to switch files inside during playing? Or it's only programmed to play one MIDI file in the SD-card? :)
  • AribaAriba Posts: 2,212
    edited 2010-10-20 - 20:00:37
    What do you mean with "switch files inside during playing" ?
    The player from the first post plays 5 midi files from a list. It's prgrammed to play always the whole file and then the next.
    You can not mix two files or play parts of a file with the existing code. But you can use a MIDI Editor (Sequencer) to mix and cut MIDI files.

    Andy
  • John A. ZoidbergJohn A. Zoidberg Posts: 514
    edited 2010-10-20 - 21:07:40
    Ariba wrote: »
    What do you mean with "switch files inside during playing" ?
    The player from the first post plays 5 midi files from a list. It's prgrammed to play always the whole file and then the next.
    You can not mix two files or play parts of a file with the existing code. But you can use a MIDI Editor (Sequencer) to mix and cut MIDI files.

    Andy

    Don't worry, I'm not trying to play parts of the MIDI files or mix it altogether.

    I mean, I'll program a supplemental interface (like press button) for me to select which song to play (or to skip a song) - is it feasible in the system? :D
  • AribaAriba Posts: 2,212
    edited 2010-10-20 - 22:20:25
    Ah, OK this is easy.

    The midiplay methode plays a whole file, you need to pass only the filename.
    So your a simplified repeat loop in the Main methode can look like that:
    repeat
       'get buttons
       case buttons
         1: midiplay(string("file1.mid"))
         2: midiplay(string("file2.mid"))
         3: midiplay(string("file3.mid"))
         4: midiplay(string("file4.mid"))
    
    The 'get buttons' and the case values depends on your button hardware.

    Andy
  • John A. ZoidbergJohn A. Zoidberg Posts: 514
    edited 2010-10-20 - 22:27:28
    Ariba wrote: »
    Ah, OK this is easy.

    The midiplay methode plays a whole file, you need to pass only the filename.
    So your a simplified repeat loop in the Main methode can look like that:
    repeat
       'get buttons
       case buttons
         1: midiplay(string("file1.mid"))
         2: midiplay(string("file2.mid"))
         3: midiplay(string("file3.mid"))
         4: midiplay(string("file4.mid"))
    
    The 'get buttons' and the case values depends on your button hardware.

    Andy

    Ah I see. Danke for the great code. I'll test it by today, provided if I manage to make my home-made SD-card cradle first. :)
Sign In or Register to comment.