Posted: 16th Jun 2007 23:03
I've heard that you can produce frequencies with no media using DB Pro ... Is it true? If so how would I do it?
Posted: 16th Jun 2007 23:07
Here is how I do it, there's alot to it, but...this will work. I use DBPro6.3, there is an issue with later versions, I think.

+ Code Snippet
type SINUSOIDAL_WAVE
    fundamental as float            rem Frequency
    theta       as float            rem Instantaneous angle
    delta       as float            rem Incremental rotation
    amplitude   as float            rem Peak amplitude
endtype

dim signal() as SINUSOIDAL_WAVE

function GenerateSignal16(ptr as dword, numsamples as integer)
    local integrator as integer = 0
    local i as integer
    local j as integer
    local wtemp as word

    for i = 0 to numsamples - 1
        integrator = 0
        for j = 0 to array count(signal())
            inc integrator, int(sin(wrapvalue(signal(j).theta)) * ...
                                signal(j).amplitude)
            inc signal(j).theta, signal(j).delta
        next j
        wtemp = integrator
        *ptr = wtemp
        inc ptr, 2
    next i
endfunction

function WritePCMWaveFile(filename as string, wavedata_blk as integer, ...
                          size as integer)
    local i as integer
    local j as integer
    local k as integer
    local s2 as integer
    local ptr as dword

    ptr = get memblock ptr(wavedata_blk)
    s2 = size << 1
    if file exist(filename)
        delete file filename
    endif
    open to write 1, filename
    write byte 1, asc("R")
    write byte 1, asc("I")
    write byte 1, asc("F")
    write byte 1, asc("F")
    write long 1, s2 + 42
    write byte 1, asc("W")
    write byte 1, asc("A")
    write byte 1, asc("V")
    write byte 1, asc("E")
    write byte 1, asc("f")
    write byte 1, asc("m")
    write byte 1, asc("t")
    write byte 1, asc(" ")
    write long 1, 16
    write word 1, *ptr
    inc ptr, 4
    write word 1, *ptr
    inc ptr, 4
    write long 1, *ptr
    inc ptr, 4
    write long 1, *ptr
    inc ptr, 4
    write word 1, *ptr
    inc ptr, 4
    write word 1, *ptr
    inc ptr, 4
    write byte 1, asc("d")
    write byte 1, asc("a")
    write byte 1, asc("t")
    write byte 1, asc("a")
    write long 1, s2

    for k = 0 to size - 1
        write word 1, *ptr
        inc ptr, 2
    next k
    close file 1
endfunction
Posted: 16th Jun 2007 23:11
How do I change the sounds I make with that?

And it doesn't work with later versions? Because I use version 6.6... At least I think that is what I use.
Posted: 17th Jun 2007 1:33
Here is something similar, with the header in question in plain view, being dummied up. There is a Bug Report about this issue in that forum, check for the difference there.

This is a little more commented, but...the first code is much more flexible, you use an array in that case. You must be careful about mixing the volumes, you can't simply keep adding amplitude, it will clip, and you will get garbage...annoying garbage!

Actually, there is work being done as we speak on making guitar chords using samples...should be a real hoot when its done. That thread is still on this first page, check it out!

This code will produce what my friend says is the EBS test signal, 440.0 hz stereo, very annoying, but a pure sinewave, nontheless. Adding more frequencies is part of the key to making it sound pretty. I will be making chords of my own using waveform synthesis and ADSR. I will post a simple example that could be used to write something more musical, like a roll piano player, those are pretty cool, and not so tough to write, it just takes alot of time.

+ Code Snippet
REM Project: siggen1
REM Created: 6/16/2007 5:05:46 PM
REM
REM ***** Main Source File *****
REM Project: binauralBeats
REM Created: 3/9/2007 7:26:52 PM
REM
REM ***** Main Source File *****
REM
#constant MAX_VOLUME = 32767
#constant WAVE_FORMAT_PCM = 1
#constant SIZEOF_WAVEFORMATEX = 18

type WAVEFORMATEX
    wFormat                 as word
    nChannels               as word
    nSamplesPerSec          as dword
    nAvgBytesPerSec         as dword
    nBlockAlign             as word
    wBitsPerSample          as word
    cbSize                  as word
endtype

global sound_block as integer
global offset as integer
global waveformatex_f as WAVEFORMATEX
global sample_rate as integer
global sample_size as integer
global num_channels as integer
global data_rate as integer
global left_frequency as float
global right_frequency as float
global thetaL as float
global thetaR as float
global deltaL as float
global deltaR as float
global wLTemp as word
global wRTemp as word
global volume as integer
global fileHandle as integer = 1

volume = MAX_VOLUME
sound_block = 1

sample_rate = 11025
sample_size = 16
num_channels = 2
data_rate = ((sample_size * num_channels) / 8) * sample_rate
left_frequency = 440.0
right_frequency = 440.0

waveformatex_f.wFormat = WAVE_FORMAT_PCM
waveformatex_f.nChannels = 2
waveformatex_f.nSamplesPerSec = 11025
waveformatex_f.nAvgBytesPerSec = 44100
waveformatex_f.nBlockAlign = 4
waveformatex_f.wBitsPerSample = 16
waveformatex_f.cbSize = SIZEOF_WAVEFORMATEX

rem WritePCMWaveFile("test.wav", 0, 0)
rem end

make memblock sound_block, 44100 + 28   rem 2 * samples + size of structure.
offset = 0

rem Make the DBPro version of the waveformatex structure. Note it is all DWORDs.

write memblock dword sound_block, offset, WAVE_FORMAT_PCM   rem wFormatTag
inc offset, 4
write memblock dword sound_block, offset, num_channels      rem nChannels = STEREO
inc offset, 4
write memblock dword sound_block, offset, sample_rate  rem nSamplesPerSec
inc offset, 4
write memblock dword sound_block, offset, data_rate  rem nAvgBytesPerSec
inc offset, 4
write memblock dword sound_block, offset, 4      rem nBlockAlign = word
inc offset, 4
write memblock dword sound_block, offset, 16     rem wBitsPerSample = 16
inc offset, 4
write memblock dword sound_block, offset, 0     rem cbSize = 0
inc offset, 4

deltaL = left_frequency * 360.0 / sample_rate
deltaR = right_frequency * 360.0 / sample_rate
thetaL = 0.0
thetaR = 180.0 * deltaR
volume = 16384

for i = 0 to 11024
    wLTemp = int(sin(wrapvalue(thetaL)) * volume)
    wRTemp = int(sin(wrapvalue(thetaR)) * volume)
    write memblock word sound_block, offset, wLTemp
    inc offset, 2
    write memblock word sound_block, offset, wRTemp
    inc offset, 2
    inc thetaL, deltaL
    inc thetaR, deltaR
next i

if sound exist(1)
    delete sound 1
endif
make sound from memblock 1, sound_block
delete memblock sound_block
loop sound 1
wait key
end

function WritePCMWaveFile(filename as string, wavedata_blk as integer, ...
                          size as integer)
    local i as integer
    local j as integer
    local k as integer
    local wavesize as integer = 0

    if file exist(filename)
        delete file filename
    endif
    wavesize = 44100 * 1         rem 1 second
    open to write 1, filename
    write byte 1, asc("R")
    write byte 1, asc("I")
    write byte 1, asc("F")
    write byte 1, asc("F")
    write long 1, wavesize + 38
    write byte 1, asc("W")
    write byte 1, asc("A")
    write byte 1, asc("V")
    write byte 1, asc("E")
    write byte 1, asc("f")
    write byte 1, asc("m")
    write byte 1, asc("t")
    write byte 1, asc(" ")
    write long 1, waveformatex_f.cbSize
    write word 1, waveformatex_f.wFormat
    write word 1, waveformatex_f.nChannels
    write long 1, waveformatex_f.nSamplesPerSec
    write long 1, waveformatex_f.nAvgBytesPerSec
    write word 1, waveformatex_f.nBlockAlign
    write word 1, waveformatex_f.wBitsPerSample
    write word 1, 0
    write byte 1, asc("d")
    write byte 1, asc("a")
    write byte 1, asc("t")
    write byte 1, asc("a")
    write long 1, wavesize

    deltaL = 28800.0 / 11025.0  rem delta = angular rotation per sample period.
    deltaR = 30240.0 / 11025.0
    volume = 16384

rem FANCY NAME = 2-channel time domain sinewave generator filter.

    thetaL = 0.0                 rem theta is the angle at any given instant.
    thetaR = 180.0 * deltaR      rem signals 180 deg out of phase.
    for k = 0 to 11024
        wLTemp = int(sin(wrapvalue(thetaL)) * volume)
        wRTemp = int(sin(wrapvalue(thetaR)) * volume)
        write word 1, wLTemp
        write word 1, wRTemp
        inc thetaL, deltaL
        inc thetaR, deltaR
    next k
    close file 1
endfunction
REM


There is a rem'ed out section and a function to write to a file, this is a piece of code I used to experiment with for a few days, I have some DSP code but I am building a test setup right now. I hope that you can get this working, its svery encouraging to be able to do this type of coding and it is really a good tool in game programming.
Cheers.
Posted: 17th Jun 2007 2:03
Nice demonstration of beats in that last bit of code. Interesting, playing around with different frequencies - I noticed that the point at which the beats become too quick to really notice, is where the two frequencies are about a semitone apart .... I wonder if that's why a semitone is the smallest increment in our musical scale, or if it's just lucky coincidence. Musical instruments would sound all wobbly if an octave was made of 24 quarter tones, instead of 12 semitones, and you played adjacent notes!!
Posted: 17th Jun 2007 2:35
I knew I was supposed to strip the original header out! You have found the code I was working on in there! Binaural beats, or beat frequencies.

I use it strictly by frequency...I think it was 78.0 and 82.0 BUT, you need to run them 180 degrees out of phase, that's the trick on that one! The beat there is 4hz, wobbly like you would not believe! I took that, and made a long wave file...20 minutes. Then, I made an mp3 of that. I put that on my mp3 player. It is very odd, to say the least, but...also, very relaxing! wuh-wuh-wuh-wuh
Here, try it out:
Posted: 17th Jun 2007 2:47
One more thing, Ric...that is the file that I used to make the test wave for that DSP project. It did all kinds of cool stuff when I made it mono!
Posted: 17th Jun 2007 5:24
With that program Jinzai, can I do anything to make it sound more instrumental?
Posted: 17th Jun 2007 6:29
The second version (actually, it was the egg, originally, not the chicken, I think?) is limited to using two frequencies, which by itself is not all that great. That sound I produced sounds more like a B-25, or other twin-engine plane than it does music.

In order to add the fullness, and ambience of true musical instruments...it is necessary to mimic them more completely. As obvious as that sounds, it is not at all obvious what it takes to realistically mimic musical instruments. It is a collection of harmonics, which is what the first snippet addresses. You start to get good sound at three, which is not surprising, either since that is a chord, which you can use in a variety of ways. VanB, if I understand his posts correctly is taking a path that already uses samples. As I said, that's going to be some great code. Someone had mentioned Keytar, that program is wicked! Its brilliant, and great fun and also...educational and entertaining. Go VanB!

Harmonics are multiples of the fundamental. That is why I named the frequency fundamental in the first snippet. Select a note, like I did...A. Now, you can either build a chord in the same octave, or you can put higher As in there to beef it up. The proportion of harmonics, their relationship to one another, etc...determine which instrument you are sounding like.

Now, once you have that sort of thing going, the next step is to add the operators that musicians use to phrase the notes, like tremelo, vibrato, bending and generally speaking - modulating the signal in some manner. How, you say? Either by varying its volume (amplitude) or its frequency in a deliberate and systematic manner. This sort of code is called envelope generation. What I have posted so far is called waveform synthesis. It takes both to do it right. Ric has posted some great envelope code, the method is called ADSR (Attack, Decay, Sustain, Release. Wiki ADSR, there is a nice write up about it all.

It also helps to be able to process the data like Digital Signal Processors do. I have code for that, but it is quite complex, and I want to ensure its general applicability before I share it too much. It would confuse things too much and besides, you have enough work to do just getting it making a decent wave to work with in the first place.

I was going to write some stuff to put better data in the array, it is really nice the way it works, but as I said...its easy to break right now. So, I guess I will put on some coffee, and see if I can get a nice violin...I think they show up first, but that is just a guess at this point. I'll post up something later.
Posted: 17th Jun 2007 10:50
Here, try this snippet. It uses 8 signals to make a more warm sound. The harmonic method is detailed in the comments, so I will not repeat the explanation here, I'll just say that you can produce squarewaves, sawtooth waves, and triangle waves by varying harmonic content, as well.

DSP theory is largely predicated upon the notion that any sound can be reduced to a collection of pure sinewaves. The trouble with my code is that it is not yet hooked into a realtime variable GUI, but that will be forthcoming as finances and time permit. It is really lacking true mixer like volume control.

+ Code Snippet
REM Project: siggen1
REM Created: 6/16/2007 5:05:46 PM
REM
#constant MAX_VOLUME = 32767
#constant WAVE_FORMAT_PCM = 1
#constant SIZEOF_WAVEFORMATEX = 18
#constant SIZEOF_dbWAVEFORMATEX = 28

type WAVEFORMATEX
    wFormat                 as word
    nChannels               as word
    nSamplesPerSec          as dword
    nAvgBytesPerSec         as dword
    nBlockAlign             as word
    wBitsPerSample          as word
    cbSize                  as word
endtype

type SINUSOIDAL_WAVE
    fundamental as float            rem Frequency
    theta       as float            rem Instantaneous angle
    delta       as float            rem Incremental rotation
    amplitude   as float            rem Peak amplitude
endtype

dim signal() as SINUSOIDAL_WAVE

global sound_block as integer
global offset as integer
global waveformatex_f as WAVEFORMATEX
global sample_rate as integer
global sample_size as integer
global num_channels as integer
global data_rate as integer
global basefreq as float
global thetaL as float
global thetaR as float
global deltaL as float
global deltaR as float
global wLTemp as word
global wRTemp as word
global volume as integer
global fileHandle as integer = 1

volume = MAX_VOLUME
sound_block = 1

sample_rate = 11025
sample_size = 16
num_channels = 1
data_rate = ((sample_size * num_channels) / 8) * sample_rate

waveformatex_f.wFormat = WAVE_FORMAT_PCM
waveformatex_f.nChannels = 1
waveformatex_f.nSamplesPerSec = 11025
waveformatex_f.nAvgBytesPerSec = 22050
waveformatex_f.nBlockAlign = 2
waveformatex_f.wBitsPerSample = 16
waveformatex_f.cbSize = SIZEOF_WAVEFORMATEX

make memblock sound_block, 22050 + SIZEOF_dbWAVEFORMATEX
offset = 0

rem Make the DBPro version of the waveformatex structure. Note it is all DWORDs.

write memblock dword sound_block, offset, WAVE_FORMAT_PCM   rem wFormatTag
inc offset, 4
write memblock dword sound_block, offset, num_channels      rem nChannels = MONO
inc offset, 4
write memblock dword sound_block, offset, sample_rate  rem nSamplesPerSec
inc offset, 4
write memblock dword sound_block, offset, data_rate  rem nAvgBytesPerSec
inc offset, 4
write memblock dword sound_block, offset, 2      rem nBlockAlign = word
inc offset, 4
write memblock dword sound_block, offset, 16     rem wBitsPerSample = 16
inc offset, 4
write memblock dword sound_block, offset, 0     rem cbSize = 0
inc offset, 4

remstart
    This array is the basis for creating instruments with different resonance
    characteristics, as well as for creating polyphonic sounds using them. This
    example contains fractional subharmonics, which are important in creating
    instruments with cavities, like most woodwinds, and stringed instruments.
    Percussion is also like this. Keep in mind that this is only the basic
    waveform's shape. As an instrument is played, there are alot of independent
    forces acting upon it. That is what I meant about then applying envelope
    generation for making individual expressions using the basic wave. At this
    point, it might be good to use this code, and put some ADSR into it, the
    trouble is that it would not be very flexible. A better method involves
    DSP techniques, which I will be elaborating upon in the coming weeks, I
    hope. That stuff is easier to implement using DSP functions. This is a DSP
    function; its called convolution. There are others, but this one is the
    easiest to understand. The way I do it here is a summation of the waves'
    amplitudes. Ultimately, it would be nice to have code that matched the
    harmonic content in terms of volume ratios by the instrument being
    emulated.

    The audible click every second is the waves not crossing zero at the right
    moment. More well suited code would account for this. DSP techniques are
    available for fixing this, too...but we are simply making a periodic wave
    here, a real application would not use a static buffer, it would use the
    MultiMedia dll in Windows to implement a circular buffer. I will also
    demonstrate that, as it is far more useful, just a little more involved to
    set up. The plus is that this code will work with that system, too. Also,
    it will not matter which version of DBPro it is implemented in, another
    plus.
remend

basefreq = 440.0                        rem A above middle C on a piano.

remstart
    Subharmonics are below the fundamental in frequency, so they are fractional.
    (The first subharmonic is 1/2 the fundamental, the next 1/4, and so on.) They
    are important to give the sound richness and make it warmer sounding.
remend

empty array signal()

array insert at bottom signal()
signal().fundamental = basefreq / 2                 rem [1st subharmonic]
signal().theta = 0.0
signal().delta = (signal().fundamental * 360.0) / (1.0 * sample_rate)
signal().amplitude = 12288

array insert at bottom signal()
signal().fundamental = basefreq                     rem [fundamental]
signal().theta = 0.0
signal().delta = (signal().fundamental * 360.0) / (1.0 * sample_rate)
signal().amplitude = 6044

array insert at bottom signal()
signal().fundamental = basefreq * 3                 rem 3rd harmonic
signal().theta = 0.0
signal().delta = (signal().fundamental * 360.0) / (1.0 * sample_rate)
signal().amplitude = 64

array insert at bottom signal()
signal().fundamental = basefreq * 5                 rem 5th harmonic
signal().theta = 0.0
signal().delta = (signal().fundamental * 360.0) / (1.0 * sample_rate)
signal().amplitude = 32

array insert at bottom signal()
signal().fundamental = basefreq * 7                 rem 7th harmonic
signal().theta = 0.0
signal().delta = (signal().fundamental * 360.0) / (1.0 * sample_rate)
signal().amplitude = 8

array insert at bottom signal()
signal().fundamental = basefreq * 9                 rem 9th harmonic
signal().theta = 0.0
signal().delta = (signal().fundamental * 360.0) / (1.0 * sample_rate)
signal().amplitude = 4

array insert at bottom signal()
signal().fundamental = basefreq * 11                rem 11th harmonic
signal().theta = 0.0
signal().delta = (signal().fundamental * 360.0) / (1.0 * sample_rate)
signal().amplitude = 4

array insert at bottom signal()
signal().fundamental = basefreq * 13                rem 13th harmonic
signal().theta = 0.0
signal().delta = (signal().fundamental * 360.0) / (1.0 * sample_rate)
signal().amplitude = 4

waveptr = get memblock ptr(sound_block)
inc waveptr, SIZEOF_dbWAVEFORMATEX
GenerateSignal16(waveptr, sample_rate)

if sound exist(1)
    delete sound 1
endif
make sound from memblock 1, sound_block
delete memblock sound_block
loop sound 1
wait key
end

function GenerateSignal16(ptr as dword, numsamples as integer)
    local integrator as integer = 0
    local i as integer
    local j as integer
    local wtemp as word

    for i = 0 to numsamples - 1
        integrator = 0
        for j = 0 to array count(signal())
            inc integrator, int(sin(wrapvalue(signal(j).theta)) * ...
                                signal(j).amplitude)
            inc signal(j).theta, signal(j).delta
        next j
        wtemp = integrator
        *ptr = wtemp
        inc ptr, 2
    next i
endfunction

function WritePCMWaveFile(filename as string, wavedata_blk as integer, ...
                          size as integer)
    local i as integer
    local j as integer
    local k as integer
    local wavesize as integer = 0

    if file exist(filename)
        delete file filename
    endif
    wavesize = 44100 * 1         rem 1 second
    open to write 1, filename
    write byte 1, asc("R")
    write byte 1, asc("I")
    write byte 1, asc("F")
    write byte 1, asc("F")
    write long 1, wavesize + 38
    write byte 1, asc("W")
    write byte 1, asc("A")
    write byte 1, asc("V")
    write byte 1, asc("E")
    write byte 1, asc("f")
    write byte 1, asc("m")
    write byte 1, asc("t")
    write byte 1, asc(" ")
    write long 1, waveformatex_f.cbSize
    write word 1, waveformatex_f.wFormat
    write word 1, waveformatex_f.nChannels
    write long 1, waveformatex_f.nSamplesPerSec
    write long 1, waveformatex_f.nAvgBytesPerSec
    write word 1, waveformatex_f.nBlockAlign
    write word 1, waveformatex_f.wBitsPerSample
    write word 1, 0
    write byte 1, asc("d")
    write byte 1, asc("a")
    write byte 1, asc("t")
    write byte 1, asc("a")
    write long 1, wavesize

    deltaL = 28800.0 / 11025.0  rem delta = angular rotation per sample period.
    deltaR = 30240.0 / 11025.0
    volume = 16384

rem FANCY NAME = 2-channel time domain sinewave generator filter.

    thetaL = 0.0                 rem theta is the angle at any given instant.
    thetaR = 180.0 * deltaR      rem signals 180 deg out of phase.
    for k = 0 to 11024
        wLTemp = int(sin(wrapvalue(thetaL)) * volume)
        wRTemp = int(sin(wrapvalue(thetaR)) * volume)
        write word 1, wLTemp
        write word 1, wRTemp
        inc thetaL, deltaL
        inc thetaR, deltaR
    next k
    close file 1
endfunction
Posted: 17th Jun 2007 17:05
Well that's freaking awesome! Thanks a lot.

Do you mind explaining how I could make more than 1 sound at a time...
I get lost reading the code.
Posted: 17th Jun 2007 20:36
I know, sorry. The part of that that I wanted to point out is the use of the signal() array. What I have is a little test code, its pretty hardcoded, but those harmonics are the ones you use to create a rich note. You are now discussing polyphony, and that array is for that, too. You [simply!] use more signal() elements in that case. Create a set like the one I've shown you for each note in an interval (2 notes) or a chord (3, or more notes).

I was anxious to get you something that showed the potential of this, and now I have succeeded in making you want more. That's good, but...man, you are asking me to write my whole app today. Okay, but I've got to go get my big piece of chicken from my sons today sometime!

I have a nice book somewhere that explains creating scales, and thus chord theory, its all from Pythagoras, and other long dead Greeks. I can't seem to track it down, but...I will continue to flesh this code out, I don't think it would be nice of me to leave you hanging like that, but...you should be able to get a lot of different sound qualities by varying just the amplitudes in the snippet above. Just remember that the total of the amplitudes cannot be more than 32768, and you will not break the code. (If you do, I think it would just clip the output, but not in a very obvious way; I think it would just sound very odd.)

So then, give me a few days to get to polyphony, and then we can begin to discuss the envelope generation. That is another really fun part, which is also freaking awesome.
Posted: 18th Jun 2007 0:13
Oh ok I'll try using more signal(). And thanks for all of the work you've done. I'm not using a computer that has dbpro on it so I'll have to try it when I get home.
Posted: 18th Jun 2007 0:59
More cowbell, Gene!
Posted: 18th Jun 2007 3:20
Huh?
Posted: 18th Jun 2007 3:36
How'd you know about that?
Posted: 18th Jun 2007 3:44
What are you talking about?
Posted: 18th Jun 2007 4:17
Its from a skit on SNL. Will Ferrell at the recording of Blue Oytser Cult's "Don't Fear the Reaper". Its very funny, although maybe not so well-known. He is playing Gene, the cowbell player.
Posted: 18th Jun 2007 4:22
Oh ok, I thought for a second it was some secret code word everyone should know
Posted: 18th Jun 2007 18:31
Jinzai, your code doesn't really make a lot of sense to me seeing as you wrote it and I didn't.

I'm not sure where to place the new signal() and all of the code following it. When I tried it crashed