Help with 1-bit sampled sound

2»

Comments

  • edited March 2014
    Wau. This so inspiring reading.

    I (not so proudly) present you ... simple PWM 3-state DAC.

    3-states (ZERO, HALF, FULL) is bare minimum but it allows to play non-square waves like saw or triangle.

    Try here, dont expect too much, its just a toy: https://dl.dropboxusercontent.com/u/68629950/dactrack.sna
  • edited March 2014
    Hey, you could develop the project! :)
    ZX Spectrum 48K BEEPER Music:
    http://mister_beep.republika.pl/
  • utzutz
    edited March 2014
    catmeows wrote: »
    Wau. This so inspiring reading.

    I (not so proudly) present you ... simple PWM 3-state DAC.

    3-states (ZERO, HALF, FULL) is bare minimum but it allows to play non-square waves like saw or triangle.

    Try here, dont expect too much, its just a toy: https://dl.dropboxusercontent.com/u/68629950/dactrack.sna

    Oh, I've been at that point several times already. The real question is how to expand on that. I'm currently fiddeling on a beeper engine that will allow realtime mixing of 2 non-square waveforms, but it still needs a lot of work.
  • edited March 2014
    utz wrote: »
    Oh, I've been at that point several times already. The real question is how to expand on that. I'm currently fiddeling on a beeper engine that will allow realtime mixing of 2 non-square waveforms, but it still needs a lot of work.

    Whilst an oscilloscope would be very handy at this point, you could easily get away with PC software (maybe something like http://code.google.com/p/xoscillo/) and your sound card input to see the waveform.
  • utzutz
    edited March 2014
    For sound purposes, it's pretty much enough to look at the waveform in a standard wave editor.
    The core of my routine is more or less done actually, but there are a few unresolved issues with the framework, ie. how to update note data pointers etc.
  • edited March 2014
    [utz;759648]Oh, I've been at that point several times already. The real question is how to expand on that. I'm currently fiddeling on a beeper engine that will allow realtime mixing of 2 non-square waveforms, but it still needs a lot of work.[/QUOTE]
    Well, same problem here. When Shiru's Phaser appeared I got idea I could mount sw synthesis on one ay channel while other two plays hw. It turned out that stealing shiru's phase is nothing to write proper data parser.

    Back to topic - I hear strong modulation in sound but i wonder I could time the engine in better way. The key check could cause that but it has freq about 9khz so I would not expect it to have so strong effect.
  • utzutz
    edited March 2014
    I/O port contention maybe? See here and here.
  • edited March 2014
    How about something like this. Be warned it's only a first draft and I haven't tried to compile anything yet.
      ld de,sample
      ld h,table/256
      ld c,254
      
      ld a,(de)   7
      and 56      7
      ld l,a      4
    
      ld b,(hl)   inc l   out (c),b   23
      nop         nop                 8   31
    loop
      ld b,(hl)   inc l   out (c),b   23
      ld a,(de)                       7   30
      ld b,(hl)   inc l   out (c),b   23
      rl a                            8   31
      ld b,(hl)   inc l   out (c),b   23
      rl a                            8   31
      ld b,(hl)   inc l   out (c),b   23
      rl a                            8   31
      ld b,(hl)   inc l   out (c),b   23
      inc de      nop                 10  33
      ld b,(hl)   inc l   out (c),b   23
      and 56                          7   30
      ld b,(hl)   inc l   out (c),b   23
      ld l,a      nop                 8   31
      
      ld b,(hl)   inc l   out (c),b   23
      nop         nop                 8   31
      ld b,(hl)   inc l   out (c),b   23
      ld a,(de)                       7   30
      ld b,(hl)   inc l   out (c),b   23
      nop         nop                 8   31
      ld b,(hl)   inc l   out (c),b   23
      cp 64                           7   30
      ld b,(hl)   inc l   out (c),b   23
      jp c,EXIT                       10  33
      ld b,(hl)   inc l   out (c),b   23
      and 56                          7   30
      ld b,(hl)   inc l   out (c),b   23
      nop         nop                 8   31
      ld b,(hl)   inc l   out (c),b   23
      ld l,a      nop                 8   31
      
      ld b,(hl)   inc l   out (c),b   23
      jp loop                         10  33
    exit
    

    The idea is that between each Fetch/Inc/OUT, there's around 8-10 T-states of operation in part-prepping the next data from the sample (from (DE) into A, then finally into L). So the speaker is hit with a new value roughly every 31 T-States.

    The sample is read from (DE). Each byte holds 2 x 7-bit values. Bits 3-5 are first used as a table lookup in HL, then 8 PDM bytes are replayed from the table. Then the lower 3 bits are used for the next 8 PDM bytes. there's also a check for a high bit being set, which will exit the loop.

    The table should start on a page boundary and consist of 64 bytes in sets of 8. In each set of 8 bytes, most are zero, but an increasing number of them are set to 24.

    e.g.
    0,0,0,0,0,0,0,0
    0,0,0,0,0,0,0,0,24
    0,0,0,24,0,0,0,24
    0,0,24,0,0,24,0,24
    0,24,0,24,0,24,0,24
    0,24,0,24,24,0,24,24
    0,24,24,24,0,24,24,24
    0,24,24,24,24,24,24,24
    24,24,24,24,24,24,24,24

    The timing for the PDM isn't perfect to the T-State, but then does it need to be?

    I figure this should eat through around 7000 bytes per second, with two samples per byte, allowing reproduction of up to 7kHz. You could lengthen the table entries, or repeat them, with half the sample rate. Maybe the table could be tweaked for more representative volume levels, too.

    Come to think of it, since it's taking a 7-bit value and outputting 8 values then it's no better than a pre-arranged 1-bit PDM datastream. So I think this approach is only useful if you do actually replay each table entry two or four times, thus reducing the sample rate and the amount of data required, whilst still keeping up the PDM output rate.
    Joefish
    - IONIAN-GAMES.com -
  • edited March 2014
    Let me invite you... to another trip to world of cheap programing.

    Thanks to Utz ...
    utz wrote: »
    I/O port contention maybe? See here and here.

    ... I solved modulation problem. Code is still very lazy in timing but at least the inner loop is contention proof.

    Another change is that for now tere is 5 state DAC (0%,25%,50%,75%,100%).

    And finaly, of five predefined waves one is customizable in very crude 'editor'.

    So enjoy -> https://dl.dropboxusercontent.com/u/68629950/dactrack3.sna
  • edited March 2014
    BACK TO BEEPER 3: RETURN OF SQUEEKER

    I was wondering what cause high frequency tones in sound so returned back to 3 state DAC and did inner loop contention proof. Square wave at full volume is now very clean, other are wheezing. And wheezing. And wheezing :confused:

    Try if you dare https://dl.dropboxusercontent.com/u/68629950/dactrack4.sna

    Does anyone know what could cause the high freq tone ?

    Also, I wonder if switching from 3-state to 5-state DAC could truly improve sound quality. So far I'm not convinced.
  • edited March 2014
    Here's a scope screenshot of the beeper circuit on an Issue 4B (taken at the speaker's terminal). The command used was "beep 1,60".

    Note the rise and fall times of the signal - only 2.78us rise and 14.3us fall, so it's pretty much impossible to do anything in code to make the actual signal analogue (this differs from, say, a Spectrum +3 whose "beeper" output is very amenable to this kind of thing). A single OUT instruction takes about 2us, so you just don't have time to really play with it as an actual signal.

    Of course that doesn't mean you CAN'T do it (you can! people have!) but on the 48K machines it will be entirely down to the inertia in the speaker cone rather than the electrical circuit that leads to the speaker cone.

    ZXBeeper.png
  • edited March 2014
    Thank you, Winston.

    So here is CHAPTER IV: FINAL BEEP

    This time 3 state DAC, thanks to Winston's diagram I realized that I had too long delay between consequent OUTs.

    https://dl.dropboxusercontent.com/u/68629950/dactrack5.sna
    https://dl.dropboxusercontent.com/u/68629950/dactrack4.lst

    This pretty much final version as I'm quite satisfied with the result.
  • utzutz
    edited March 2014
    Hey, pretty neat. The triangle could be more "rounded" but I guess it's about as good as it gets with this approach. Now let's hear mixing of 2 samples!
  • edited March 2014
    Well, I have no idea how to mix n-bit samples. The only thing I ever tried was XOR over two square waves.
    And as I'm not musician I really don't know if these crude waves have some use for musicians.
  • edited March 2014
    Combining two samples is simply a matter of adding the points from each sample and dividing by 2. The complication comes if they're sampled at different rates (and/or different to the replay rate) in which case you have to 're-sample' (skip or interpolate) to lengthen or shorten them..
    Joefish
    - IONIAN-GAMES.com -
  • utzutz
    edited March 2014
    joefish wrote: »
    The complication comes if they're sampled at different rates (and/or different to the replay rate) in which case you have to 're-sample' (skip or interpolate) to lengthen or shorten them..

    Which is pretty much the whole point when trying to adapt this for musical use. In addition to allowing multiple pitches (by up/downsampling, obviously), you also have to keep track of the play length and update data pointers frequently. My approach to this is interrupt based, which naturally introduces quite a bit of extra noise, but oh well... better than nothing. Or at least better sounding than Sampetracker, I hope.
  • edited March 2014
    Wow, okay, so this thread has exploded since I last checked. Tonnes of stuff to read. Not sure I'm going to understand all of it. Going back to my original query, with the machine that I'm using, I'm able to turn the speaker on or off and am able to tell it which frequency (via the timer chip) to produce. I understand the modulation technique of sending 1's and 0's to 'hold' the speaker at a particular position etc but if I have all these 'variables' to set (speaker on or off, frequency to set for speaker) how do I just carry out a '1' or a '0' ? I can switch speaker on or off with a '1' or '0' but which frequency to choose, is it a lower frequency I should be choosing (presumably because low frequencies cause the most movement in the speaker cone) ?
    thanks
    kelp
    MZ-80A Secrets
    http://mz-80a.com
  • edited March 2014
    Also, have begun my new website detailing techniques which I'm trying out for the first time on this Sharp computer:

    http://sharpgames.atspace.com/

    so thanks to all you guys for any audio advice you've been giving me (or still yet to come). I do really appreciate it and hope to put it all into practice soon.
    kelp

    [edit] : still playing about with the layout of the site so bear with me!
    MZ-80A Secrets
    http://mz-80a.com
  • edited March 2014
    kelp7 wrote: »
    Going back to my original query, with the machine that I'm using, I'm able to turn the speaker on or off and am able to tell it which frequency (via the timer chip) to produce. I understand the modulation technique of sending 1's and 0's to 'hold' the speaker at a particular position etc but if I have all these 'variables' to set (speaker on or off, frequency to set for speaker) how do I just carry out a '1' or a '0' ? I can switch speaker on or off with a '1' or '0' but which frequency to choose, is it a lower frequency I should be choosing (presumably because low frequencies cause the most movement in the speaker cone) ?

    I love how you can see the steady degradation in the quality of user documentation through time for computers. Your sharp machine manual comes with a complete disassembly of the rom and circuit diagrams. Go forward four years and we have the spectrum manual with information on how to program but nothing on the rom and no circuit diagram. Fast forward to today and you get a single sheet of paper telling you to "plug it in, dummy" :D

    Anyway page 173 of the sharp manual gives the circuit diagram for sound output (that's page 163 in pdf pages). There's a toggle flip-flop driving the speaker circuit which means only 50% duty square waves can be generated automatically by the 8253.

    To manually switch state of the speaker output you have to deliver a single edge to that flip-flop and I'm guessing that's what you're doing when you say you can out a 1 or 0 to the speaker? The 8253 probably has to be in "Software triggerred strobe" mode with a very short delay (0 if possible) so that it generates a single pulse on command.

    If you can do this, then you can select among waveforms during a sample period to set the diaphragm's position.

    00000000 = 0/8 ones = 0% volume
    10001000 = 2/8 ones = 25% volume
    10101010 = 4/8 ones = 50% volume
    etc

    A really rough idea:

    get_sample:
    ...
    ld h,codeblock/256
    ld l,volume*3 ; volume*3 read from sample data
    jp (hl)

    ...
    codeblock: ; jump table
    jp volume_0
    jp volume_25
    jp volume_50
    ...

    volume_25:
    toggle_to_1 ; 1
    toggle_to_0 ; 0
    do_nothing ; 0
    do_nothing ; 0
    ...
    jp get_sample

    The difficult is you have to make sure everything is timed correctly. The time from the end of the volume_25 subroutine which includes the "jp get_sample" plus the time spent getting the next same and travelling through the jp table to the next volume subroutine has to be some multiple of the time to output the 1/0 string to the speaker. If that time is four 1/0 periods, then your waveforms must take that into account. Maybe all your waveforms end in zero: 10101010 = 50% then the time take to get the next sample will be four implied zeros: 10101010 0000, no longer 50%. So your waveform might change to 11101110 0000 = 50% (ie your subroutine outputs the string 11101110 for 50%). You can see the signal has drifted toward PWM rather than PDM and maybe you would want to try to toggle during that fixed period to help with that. Anyway you get the idea.



    A longer string of 0/1 (the length I showed above was 8-bits) sent to the speaker in each period gives more time for the diaphragm to settle at the correct position but lowers the sample rate. The quicker you can send each bit (which affords a longer string for a given sample rate too) will drive the undesirable portion of the signal higher, making the the high pitch hum heard in pwm less audible.

    So there's no thinking about what frequency square wave you are generating -- you're taking control of the speaker bit directly here.

    But given the way the sharp's speaker works, another thing you can look at is fm-synthesis as it looks like you may be able to vary the output frequeny by altering the 8253 period on the fly.


    Beyond playing samples, there is the set of beeper engines mentioned before -- they are not concerned with replaying samples but with generating a set of waves that can include multiple channels, drums and different synthetic instrument sounds. Anyway, that's old territory you know about already :)
  • edited March 2014
    kelp7 wrote: »
    Going back to my original query, with the machine that I'm using, I'm able to turn the speaker on or off and am able to tell it which frequency (via the timer chip) to produce. I understand the modulation technique of sending 1's and 0's to 'hold' the speaker at a particular position etc but if I have all these 'variables' to set (speaker on or off, frequency to set for speaker) how do I just carry out a '1' or a '0' ? I can switch speaker on or off with a '1' or '0' but which frequency to choose, is it a lower frequency I should be choosing (presumably because low frequencies cause the most movement in the speaker cone) ?
    Sounds like you're not turning the 'speaker' on or off, you're turning the oscillator circuit on and off. To derive speaker control from that, you may have to experiment.

    You're right, if you pick the lowest frequency then the speaker should stay 1 for longer. Hopefully longer than you will need it to; then you could control it by turning the circuit on and off. However, this presumes three things: (1) that the oscillator goes to 1 immediately that you turn it on, then drops to 0 after half the defined period; (2) that when you turn it off, it drops back to 0, and (3) when you turn it on again it resets so it goes to 1 again.

    If any of these conditions aren't true, then you'll struggle to control the speaker directly. Unless you can find some other method.
    Joefish
    - IONIAN-GAMES.com -
  • edited March 2014
    Okay, thanks for all the help everyone, I shall do lots of reading and play around a bit on the machine to see what is possible! Thanks again.
    kelp
    MZ-80A Secrets
    http://mz-80a.com
  • edited March 2014
    kelp7 wrote: »
    Going back
    I can't give any particular advice but pc speaker has same capability and some early dos games had sampled sound over speaker so perhaps you could find some inspiration there.
  • edited March 2014
    catmeows wrote: »
    I can't give any particular advice but pc speaker has same capability and some early dos games had sampled sound over speaker so perhaps you could find some inspiration there.

    Indeed, I remember the title screen of Wizball on my Amstrad PC1512 which had an awesome piece of digitised music, always wondered how they'd achieved that with essentially something that could only produce bleeps :-) I also remember Quadralien which managed polyphony beeper music, very clear sound too, no distortion or anything. Bear in mind I'm coming into this as somewhat of a beginner, or perhaps intermediate, assembly language coder, just trying to work out the theory of how this sort of thing is achieved before diving into the hardware / software details
    MZ-80A Secrets
    http://mz-80a.com
  • edited March 2014
    Hi Kelp, do you have any resource that explains sound on sharp? I noticed the manual is incomplete, there are missing pages from 60 to 86 or so and while I tried to google for two hours last weekend, I did not find anything truly useful.
  • edited March 2014
    catmeows wrote: »
    Hi Kelp, do you have any resource that explains sound on sharp? I noticed the manual is incomplete, there are missing pages from 60 to 86 or so and while I tried to google for two hours last weekend, I did not find anything truly useful.

    It's all in the manual he posted in one of the first posts. There is a circuit diagram (I mentioned in my last post) that shows exactly how things work.

    The speaker is driven by an 8253 counter/timer fed through a toggle flip flop. The toggle flip flop means only 50% duty cycle waves can be generated automatically by the 8253. To control the speaker exactly you have to send edges to the toggle flip flop to get it to switch state. To do that you have to put the 8253 into software-triggered-mode and command the 8253 to generate a one-shot pulse in software. There is a programmable delay in the 8253 when that pulse is fired after the software command and the length of the pulse is configurable as well -- these are things that have to be set as small as possible.

    The information is all there.. you just need a databook for the 8253.

    Sometimes I wonder if anyone reads my posts :P Maybe they're too long.
  • edited April 2014
    To be honest I love your posts AA ! Very informative and clearly written. Programming something like the 8253 is a challenge for me, I won't deny it. It's completely new territory for me but I like a challenge :-)

    and to catmeows, i have one very good book on this machine that is probably better than the manual which I'm going to scan in at some point in the near future. There are precious few resources online (or anywhere else really) for this machine so it'd be good to get something up on the web. I've got a couple of books to scan in (but one is much more orientated around peeking and pokeing in BASIC and extending the BASIC). I'm trying to track down either authors / publishers for both books to see if they'll mind them being put up on my site for download but so far it's not been easy trying to track them down at all...

    Anyway, thanks again everyone for your time and advice on this, I'll try my best!
    kelp
    MZ-80A Secrets
    http://mz-80a.com
  • edited February 2015
    I have a few questions about the concepts developped above.

    1/ First, I can understand roughly the thing about changing the position of the speaker diaphragm. So for a given speaker, for example the one on the ZX 48, we can make it "sing" according to its physical caracteristics. But how does this work too for the audio output (jack) of the same spectrum? We connect earphones or an audio recorder and the music will be the same, while the equipment is different. Does the diaphragm in my earphones or my recorder react the same way as the spectrum speaker? How is that possible?

    2/ Is it the same for emulators? Is the signal the same as in the Spectrum, and my earphones reacting the same, or are the spectrum emulators "faking" some parts to give the impression we're listening to those fast interruptions?

    3/ I thought first all 1-bit musics were generating sounds using a similar trick as the Trautonium is using (https://en.wikipedia.org/wiki/Trautonium): a high frequency, inaudible sound is generated, and a second one, with a slighly different cycle is generated too, thus making a lower sound. But from what I read before, unless I misunderstood, it's not the case.
  • utzutz
    edited February 2015
    Hi there ;)
    1/ Yes, it is the same. Simply put, a speaker is speaker. The only difference is in relative volume, because a larger speaker might not fully extend in the same time as the Speccy beeper.
    2/ Emulators are faking the sound output via PCM WAV. PC soundcards are not fast enough to process actual real-life 1-bit audio.
    3/ There is inaudible sound (sometimes not so inaudible, actually), but it is not directly used as a means for synthesis. Not yet, at least ;)
  • edited March 2015
    thank you for the precisions, it's much appreciated :)

    I've also found some interesting informations for 3/, in a document there: http://www.zenpho.co.uk/PhillPhelps-ChiptuneSynth.pdf

    (I don't know if the information is accurate, but it should be possible to check this because it's a similar engine, made by reverse engineering into beepola)
    Another quite different method is that found in the game “SAVAGE”, with music (and sound
    playback routines) by Jason. C. Brooke. This technique involves the DAC hardware driving an
    internal speaker with 1-bit information at an extremely high frequency above the audio
    range. Using this method it is possible to create an approximation of a desired (non-square)
    wave by driving the DAC with squarewaves that exceed the audio bandwidth causing the
    piezoelectric loudspeaker plate to move around at audio frequencies.
    This method of over sampling was also used on the IBM PC: “magic mushroom demo”. For
    more information see the “history of soundcards” (Crossfire-designs.de, 2007), SPEAKER.txt
    and PIT.txt (FELDMAN, M., 2007a, and 2007b)

    to save you time, I've found the speaker article there: http://crossfire-designs.de/index.php?lang=en&what=articles&name=showarticle.htm&article=soundcards&page=3
  • edited March 2015
    I did some experiments using a variant of PWM called sigma-delta modulation. It has the advantage that is fairly easy to implement with Z80 machine code, and allowed me to play an "yeaaaahhhh!" sample at a request by a fellow woser :)
    http://www.worldofspectrum.org/forums/showthread.php?p=641855&postcount=8

    For these techniques, you need a fast method of outputting square waves. The IBM PC and many microcontrollers can use a timer configured to output a variable duty cycle square wave. The Z80 has not either that hardware nor the necessary speed to go for 8 bits samples without avoiding the high pitch sound of the original square wave.

    Using PWM I used 3 bit per sample. With sigma-delta, I managed to use 4 bit signed samples.
Sign In or Register to comment.