EmbeddedRelated.com
Forums

Multiple Sine Waves/Chords from a DAC

Started by zylaxice April 24, 2012
Hi Matthias, I still think you're misunderstanding. First of all two
tones would never require the same CCR, secondly chords don't contain
duplicates of the same note (at least in my experience), so 7 unique
tones can be played simultaneously with 7 CCr registers. And those tones
can vary in real time, ie register CCRB1 doesn't have to be C, at each
note, a fresh set of unique notes are available. My code sample
executes in 25 cycles plus interrupt latency of 1-6 cycles, so 31 worst
case. In the case of a 16MHz clock 7 tones would require 217 clock
cycles maximum to process in the isrs, plus whatever overhead is needed
in the foreground routine, so there would still be obvious limits to the
maximum access rate. However since the OP wants a range from 130.8Hz to
987.7Hz a worst case event would mean an upper limit of 74 entries per
table. however there is nothign to stop the user from having tables of
different resolutions, or of varying the step size for different notes.
ie a 256 entry table played at a fixed rate to give 100Hz would give a
200hz output if the step through the table was 2 instead of 1. this is,
of course, another way of getting a wide frequency range using a single
clock source. In this case longer tables sound better than short ones.
In the case of a single table with variable step sizes to create notes
of different frequencies it would be possible to use a single timer, and
simply add all the different values for each step interval in a single
ISR, however the range of frequencies is more limited. If I remember my
note frequencies there are a limited number of base frequencies, and
then the harmonics of these, which the above method results in, form the
remaining available notes, so a combination of methods could well work.

Of course, having said all of this, if the OP only needs to play a few
different note combinations, or a few short tunes then, given the huge
amount of available memory why note simply pre-calculate them and store
them in flash?

Al
On 26/04/2012 6:32 PM, Matthias Weingart wrote:
> Onestone:
>
>> Hi Matthias. The idea is to use a single table, but probably a higher
>> resolution table than 32 entries, and to still use the basic concept of
>> timed access to generate different notes. It would be virtually
>> impossible, I believe, to do this with a single timer, and limited
>> jitter, how would you work out the next time interval for, say a 4 note
>> chord? where the frequencies are not some convenient binary multiple?
> Hi Al,
>
> It seems I understand your algorithm quite different then you. Each note
> use it's own counter (e.g. the 7 CCR registers) for the sample clock.
>
>> Then, where one note is a much higher frequency than another you have
>> multiple instances of the higher note per instance of the lower note, so
>> tracking order of tones with a single timer becomes cpu intensive.
> That is true. You need to prepare the settings for the next tone in the
> mainloop (CCR and position in the lookup table). One idea is a sorted table
> in RAM, sorted by next CCR. A special case has to be handled if 2 (or more)
> tones requires the same CCR. Even with your method the jitter is higher in
> case 2 (or more) CCR-interrupts need to get served at the same CCR-value.
> However my method is only suitable for very low frequencies.
>
> M.
>
>> Certainly it would be far easier to use a single timer per tone. Also
>> you'd need to take scaling into account. By using a timer per note you
>> reduce the calculations (using a differential sine table) to DACOUT >> DACOUT + TABLE(Nx) where N is the table entry, x is the tone, and TABLE
>> is a table of signed differential sine values. The simplest form of this
>> would use 1 register per note, so N1 =R4, N2 = R5 etc. Then the ISR
>> becomes simply:-
>>
>> TB1_ISR:
>> ADD&DACOUT,TABLE(R4)
>> ADD&N1INTERVAL,&CCRB1
>> BIC #CCIFG,&CCTLB1
>> INCD R4
>> ;ENTRIES ARE WORDS
>> AND #0X00FE,R4 ;ASSUMES
>> 256 ENTRY TABLE
>> RETI
>>
>> To overcome scaling issues and the need for a little maths you could
>> easily have tables for single tones, 2 note chords, up to 7 note chords
>>
>> Cheers
>>
>> Al
>>
>> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
>>> Onestone,
>>> your code is quite good (after some thinking I am now sure that it is
>>> working:-). One improvement: should be possible to do it with just one
>>> CCR- channel.
>>>
>>> M.
>>>
>>> Onestone:
>>>
>>>> being a simple kind of guy I think my method is just too easy! 8-)
>>>>
>>>> Al
>>>>
>>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
>>>>> In case you are comfortable with 2 mixed sine waves just use both
>>>>> DAC's, feed them independly and add them analog at the output pin
>>>>> with 2 resistors.
>>>>>
>>>>> Another way: create a lookup table for your "chords".
>>>>> In your current code you always use 32 steps for each sine wave - for
>>>>> one wave a step is e.g. 300us for the 2nd wave it is 310us. To mix
>>>>> them digitally you need finer steps.
>>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine waves
>>>>> into that RAM-table according to the frequencies you want. Feed this
>>>>> RAM-table with the fastest clock (987Hz is about 1ms) to the DAC. For
>>>>> example in a 256 table the 987Hz wave fits 7.55 times, the 130Hz
>>>>> waves only 1 time. For the 130Hz tone you have to calculate 256
>>>>> values from you 32 values of the lookup table. This is called
>>>>> upsampling. You can use linear interpolation (lines) to calculate the
>>>>> values between the 32 lookup values from your Flash table (or drop
>>>>> the lookup table and use sin() from the C compiler, no idea how fast
>>>>> this is). The big deal here is, that in most cases the end of the RAM
>>>>> table does not fit to the begin of the RAM table, the waves are not
>>>>> connected smoothly together. You have to calculate a new RAM table
>>>>> once the first one has been sent, so that the waves are smoothly
>>>>> connected. Use 2 RAM tables, one for output (DMA), one for
>>>>> precalculation (in the main loop).
>>>>>
>>>>> You can do the interpolation on the fly in the CCR-Interrupt routine,
>>>>> no extra RAM table, no DMA needed (however, the sound will not be
>>>>> clear because of the jitter of the interrupt response time).
>>>>>
>>>>> M.
>>>>>
>>>>> Onestone:
>>>>>
>>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd use
>>>>>> 1 timer per tone, and track the last value for each tone. Then on
>>>>>> timer interrupt:-
>>>>>>
>>>>>> DACx = DACx - oldsinevalue + newsinevalue.
>>>>>>
>>>>>> Effectively remove the old value for the tone from the DAC and add
>>>>>> the new one at a variabe time interval. Also, given something with
>>>>>> as much memory as the 2619 i'd not limit myself to a 32 entry table.
>>>>>> In theory, and assuming that you aren't using the other timers, you
>>>>>> could get 7 tones using timer B. I'd also go for the highest clock
>>>>>> frequency possible, and in the ISR keep the code short and clean.
>>>>>> for minimal jitter. Rather than absolute values your table could
>>>>>> comprise differential values so that your calculation is simple DACx
>>>>>> = DACx + sine(n).
>>>>>>
>>>>>> Hope this helps
>>>>>>
>>>>>> Al
>>>>>>
>>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
>>>>>>> Hi all,
>>>>>>> I am currently working on some code for an MSP430F2619. I am
>>>>>>> using a 32-value sine lookup table, and pushing the sequential
>>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
>>>>>>> note frequency) controlled by TimerB. So far,this is working
>>>>>>> well. I can produce any number of reasonable sounding pure
>>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
>>>>>>> and this is more than enough range for my needs, three full
>>>>>>> octaves.
>>>>>>>
>>>>>>> However, this only plays one tone at a time. I have been wracking
>>>>>>> my brains trying to figure out how to use this to generate a
>>>>>>> "chord," more than one note played at the same time. I know that
>>>>>>> musically this can be as simple as adding two sine waves together,
>>>>>>> but since I don't have two simultaneously existing sine waves, just
>>>>>>> one that I change the frequency of, I am at a loss.
>>>>>>>
>>>>>>> Does anyone have any suggestions on how to achieve this, or
>>>>>>> references that they think might push me in the right direction?
>>>>>>>
>>>>>>> Thank you,
>>>>>>> David
>>>>>>>
>>>
>>>
>>>
>>>
>>>
>>>

Beginning Microcontrollers with the MSP430

More detail on DDS - you create a single sine-wave table (it's actually a
cosine)
and 'step' through it based on a constant sample rate. The frequency is
determined by the size of the step.

For example, let's just assume we have a 1024-word table:

int costab[1024] = { ... }; // initialize with pre-calculated single wave
of
// cos() * 32767 (-32767 .. 32767)

If you use a sample rate of 10240 samples/sec, an increment of
1 will produce a 10Hz sine wave, an increment of 10 will produce 100Hz,
etc.

You maintain a "phase accumulator" for each desired output frequency.
The output is then a stream of samples at whatever precision your
cosine table is.

You can use a large phase accumulator to provide better/finer
frequency granularity and intermediate bits of the accumulator
to effectively implement fractional increments between frequencies.

You only need one periodic interrupt to drive the output ISR,

Cheers,
Dana K6JQ

On Thu, Apr 26, 2012 at 6:53 AM, Onestone wrote:

> **
> Hi Matthias, I still think you're misunderstanding. First of all two
> tones would never require the same CCR, secondly chords don't contain
> duplicates of the same note (at least in my experience), so 7 unique
> tones can be played simultaneously with 7 CCr registers. And those tones
> can vary in real time, ie register CCRB1 doesn't have to be C, at each
> note, a fresh set of unique notes are available. My code sample
> executes in 25 cycles plus interrupt latency of 1-6 cycles, so 31 worst
> case. In the case of a 16MHz clock 7 tones would require 217 clock
> cycles maximum to process in the isrs, plus whatever overhead is needed
> in the foreground routine, so there would still be obvious limits to the
> maximum access rate. However since the OP wants a range from 130.8Hz to
> 987.7Hz a worst case event would mean an upper limit of 74 entries per
> table. however there is nothign to stop the user from having tables of
> different resolutions, or of varying the step size for different notes.
> ie a 256 entry table played at a fixed rate to give 100Hz would give a
> 200hz output if the step through the table was 2 instead of 1. this is,
> of course, another way of getting a wide frequency range using a single
> clock source. In this case longer tables sound better than short ones.
> In the case of a single table with variable step sizes to create notes
> of different frequencies it would be possible to use a single timer, and
> simply add all the different values for each step interval in a single
> ISR, however the range of frequencies is more limited. If I remember my
> note frequencies there are a limited number of base frequencies, and
> then the harmonics of these, which the above method results in, form the
> remaining available notes, so a combination of methods could well work.
>
> Of course, having said all of this, if the OP only needs to play a few
> different note combinations, or a few short tunes then, given the huge
> amount of available memory why note simply pre-calculate them and store
> them in flash?
>
> Al
> On 26/04/2012 6:32 PM, Matthias Weingart wrote:
> > Onestone:
> >
> >> Hi Matthias. The idea is to use a single table, but probably a higher
> >> resolution table than 32 entries, and to still use the basic concept of
> >> timed access to generate different notes. It would be virtually
> >> impossible, I believe, to do this with a single timer, and limited
> >> jitter, how would you work out the next time interval for, say a 4 note
> >> chord? where the frequencies are not some convenient binary multiple?
> > Hi Al,
> >
> > It seems I understand your algorithm quite different then you. Each note
> > use it's own counter (e.g. the 7 CCR registers) for the sample clock.
> >
> >> Then, where one note is a much higher frequency than another you have
> >> multiple instances of the higher note per instance of the lower note, so
> >> tracking order of tones with a single timer becomes cpu intensive.
> > That is true. You need to prepare the settings for the next tone in the
> > mainloop (CCR and position in the lookup table). One idea is a sorted
> table
> > in RAM, sorted by next CCR. A special case has to be handled if 2 (or
> more)
> > tones requires the same CCR. Even with your method the jitter is higher
> in
> > case 2 (or more) CCR-interrupts need to get served at the same CCR-value.
> > However my method is only suitable for very low frequencies.
> >
> > M.
> >
> >> Certainly it would be far easier to use a single timer per tone. Also
> >> you'd need to take scaling into account. By using a timer per note you
> >> reduce the calculations (using a differential sine table) to DACOUT > >> DACOUT + TABLE(Nx) where N is the table entry, x is the tone, and TABLE
> >> is a table of signed differential sine values. The simplest form of this
> >> would use 1 register per note, so N1 =R4, N2 = R5 etc. Then the ISR
> >> becomes simply:-
> >>
> >> TB1_ISR:
> >> ADD&DACOUT,TABLE(R4)
> >> ADD&N1INTERVAL,&CCRB1
> >> BIC #CCIFG,&CCTLB1
> >> INCD R4
> >> ;ENTRIES ARE WORDS
> >> AND #0X00FE,R4 ;ASSUMES
> >> 256 ENTRY TABLE
> >> RETI
> >>
> >> To overcome scaling issues and the need for a little maths you could
> >> easily have tables for single tones, 2 note chords, up to 7 note chords
> >>
> >> Cheers
> >>
> >> Al
> >>
> >> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
> >>> Onestone,
> >>> your code is quite good (after some thinking I am now sure that it is
> >>> working:-). One improvement: should be possible to do it with just one
> >>> CCR- channel.
> >>>
> >>> M.
> >>>
> >>> Onestone:
> >>>
> >>>> being a simple kind of guy I think my method is just too easy! 8-)
> >>>>
> >>>> Al
> >>>>
> >>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
> >>>>> In case you are comfortable with 2 mixed sine waves just use both
> >>>>> DAC's, feed them independly and add them analog at the output pin
> >>>>> with 2 resistors.
> >>>>>
> >>>>> Another way: create a lookup table for your "chords".
> >>>>> In your current code you always use 32 steps for each sine wave - for
> >>>>> one wave a step is e.g. 300us for the 2nd wave it is 310us. To mix
> >>>>> them digitally you need finer steps.
> >>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine waves
> >>>>> into that RAM-table according to the frequencies you want. Feed this
> >>>>> RAM-table with the fastest clock (987Hz is about 1ms) to the DAC. For
> >>>>> example in a 256 table the 987Hz wave fits 7.55 times, the 130Hz
> >>>>> waves only 1 time. For the 130Hz tone you have to calculate 256
> >>>>> values from you 32 values of the lookup table. This is called
> >>>>> upsampling. You can use linear interpolation (lines) to calculate the
> >>>>> values between the 32 lookup values from your Flash table (or drop
> >>>>> the lookup table and use sin() from the C compiler, no idea how fast
> >>>>> this is). The big deal here is, that in most cases the end of the RAM
> >>>>> table does not fit to the begin of the RAM table, the waves are not
> >>>>> connected smoothly together. You have to calculate a new RAM table
> >>>>> once the first one has been sent, so that the waves are smoothly
> >>>>> connected. Use 2 RAM tables, one for output (DMA), one for
> >>>>> precalculation (in the main loop).
> >>>>>
> >>>>> You can do the interpolation on the fly in the CCR-Interrupt routine,
> >>>>> no extra RAM table, no DMA needed (however, the sound will not be
> >>>>> clear because of the jitter of the interrupt response time).
> >>>>>
> >>>>> M.
> >>>>>
> >>>>> Onestone:
> >>>>>
> >>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd use
> >>>>>> 1 timer per tone, and track the last value for each tone. Then on
> >>>>>> timer interrupt:-
> >>>>>>
> >>>>>> DACx = DACx - oldsinevalue + newsinevalue.
> >>>>>>
> >>>>>> Effectively remove the old value for the tone from the DAC and add
> >>>>>> the new one at a variabe time interval. Also, given something with
> >>>>>> as much memory as the 2619 i'd not limit myself to a 32 entry table.
> >>>>>> In theory, and assuming that you aren't using the other timers, you
> >>>>>> could get 7 tones using timer B. I'd also go for the highest clock
> >>>>>> frequency possible, and in the ISR keep the code short and clean.
> >>>>>> for minimal jitter. Rather than absolute values your table could
> >>>>>> comprise differential values so that your calculation is simple DACx
> >>>>>> = DACx + sine(n).
> >>>>>>
> >>>>>> Hope this helps
> >>>>>>
> >>>>>> Al
> >>>>>>
> >>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
> >>>>>>> Hi all,
> >>>>>>> I am currently working on some code for an MSP430F2619. I am
> >>>>>>> using a 32-value sine lookup table, and pushing the sequential
> >>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
> >>>>>>> note frequency) controlled by TimerB. So far,this is working
> >>>>>>> well. I can produce any number of reasonable sounding pure
> >>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
> >>>>>>> and this is more than enough range for my needs, three full
> >>>>>>> octaves.
> >>>>>>>
> >>>>>>> However, this only plays one tone at a time. I have been wracking
> >>>>>>> my brains trying to figure out how to use this to generate a
> >>>>>>> "chord," more than one note played at the same time. I know that
> >>>>>>> musically this can be as simple as adding two sine waves together,
> >>>>>>> but since I don't have two simultaneously existing sine waves, just
> >>>>>>> one that I change the frequency of, I am at a loss.
> >>>>>>>
> >>>>>>> Does anyone have any suggestions on how to achieve this, or
> >>>>>>> references that they think might push me in the right direction?
> >>>>>>>
> >>>>>>> Thank you,
> >>>>>>> David
> >>>>>>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
Dana,

I do not have time to analyze this for myself, but is this just (a special case of a) Fast Fourier Transform?

Emmett Redd, Ph.D., Professor mailto:E...@MissouriState.Edu
Physics, Astronomy, and Materials Science Office: 417-836-5221
Missouri State University Dept: 417-838-5131
901 S NATIONAL AVENUE FAX: 417-836-6226
SPRINGFIELD, MO 65897 USA Lab: 417-836-3770

It is clear that thought is not free if the profession of certain opinions make it impossible to earn a living. -- Bertrand Russell

> -----Original Message-----
> From: m... [mailto:m...] On Behalf
> Of Dana Myers
> Sent: Thursday, April 26, 2012 2:55 PM
> To: m...
> Subject: Re: [msp430] Re: Re: Re: Multiple Sine Waves/Chords from a DAC
>
> More detail on DDS - you create a single sine-wave table (it's actually
> a
> cosine)
> and 'step' through it based on a constant sample rate. The frequency is
> determined by the size of the step.
>
> For example, let's just assume we have a 1024-word table:
>
> int costab[1024] = { ... }; // initialize with pre-calculated single
> wave
> of
> // cos() * 32767 (-32767 ..
> 32767)
>
> If you use a sample rate of 10240 samples/sec, an increment of
> 1 will produce a 10Hz sine wave, an increment of 10 will produce 100Hz,
> etc.
>
> You maintain a "phase accumulator" for each desired output frequency.
> The output is then a stream of samples at whatever precision your
> cosine table is.
>
> You can use a large phase accumulator to provide better/finer frequency
> granularity and intermediate bits of the accumulator to effectively
> implement fractional increments between frequencies.
>
> You only need one periodic interrupt to drive the output ISR,
>
> Cheers,
> Dana K6JQ
>
> On Thu, Apr 26, 2012 at 6:53 AM, Onestone
> wrote:
>
> > **
> >
> >
> > Hi Matthias, I still think you're misunderstanding. First of all two
> > tones would never require the same CCR, secondly chords don't contain
> > duplicates of the same note (at least in my experience), so 7 unique
> > tones can be played simultaneously with 7 CCr registers. And those
> > tones can vary in real time, ie register CCRB1 doesn't have to be C,
> > at each note, a fresh set of unique notes are available. My code
> > sample executes in 25 cycles plus interrupt latency of 1-6 cycles, so
> > 31 worst case. In the case of a 16MHz clock 7 tones would require 217
> > clock cycles maximum to process in the isrs, plus whatever overhead
> is
> > needed in the foreground routine, so there would still be obvious
> > limits to the maximum access rate. However since the OP wants a range
> > from 130.8Hz to 987.7Hz a worst case event would mean an upper limit
> > of 74 entries per table. however there is nothign to stop the user
> > from having tables of different resolutions, or of varying the step
> size for different notes.
> > ie a 256 entry table played at a fixed rate to give 100Hz would give
> a
> > 200hz output if the step through the table was 2 instead of 1. this
> > is, of course, another way of getting a wide frequency range using a
> > single clock source. In this case longer tables sound better than
> short ones.
> > In the case of a single table with variable step sizes to create
> notes
> > of different frequencies it would be possible to use a single timer,
> > and simply add all the different values for each step interval in a
> > single ISR, however the range of frequencies is more limited. If I
> > remember my note frequencies there are a limited number of base
> > frequencies, and then the harmonics of these, which the above method
> > results in, form the remaining available notes, so a combination of
> methods could well work.
> >
> > Of course, having said all of this, if the OP only needs to play a
> few
> > different note combinations, or a few short tunes then, given the
> huge
> > amount of available memory why note simply pre-calculate them and
> > store them in flash?
> >
> > Al
> >
> >
> > On 26/04/2012 6:32 PM, Matthias Weingart wrote:
> > > Onestone:
> > >
> > >> Hi Matthias. The idea is to use a single table, but probably a
> > >> higher resolution table than 32 entries, and to still use the
> basic
> > >> concept of timed access to generate different notes. It would be
> > >> virtually impossible, I believe, to do this with a single timer,
> > >> and limited jitter, how would you work out the next time interval
> > >> for, say a 4 note chord? where the frequencies are not some
> convenient binary multiple?
> > > Hi Al,
> > >
> > > It seems I understand your algorithm quite different then you. Each
> > > note use it's own counter (e.g. the 7 CCR registers) for the sample
> clock.
> > >
> > >> Then, where one note is a much higher frequency than another you
> > >> have multiple instances of the higher note per instance of the
> > >> lower note, so tracking order of tones with a single timer becomes
> cpu intensive.
> > > That is true. You need to prepare the settings for the next tone in
> > > the mainloop (CCR and position in the lookup table). One idea is a
> > > sorted
> > table
> > > in RAM, sorted by next CCR. A special case has to be handled if 2
> > > (or
> > more)
> > > tones requires the same CCR. Even with your method the jitter is
> > > higher
> > in
> > > case 2 (or more) CCR-interrupts need to get served at the same CCR-
> value.
> > > However my method is only suitable for very low frequencies.
> > >
> > > M.
> > >
> > >> Certainly it would be far easier to use a single timer per tone.
> > >> Also you'd need to take scaling into account. By using a timer per
> > >> note you reduce the calculations (using a differential sine table)
> > >> to DACOUT = DACOUT + TABLE(Nx) where N is the table entry, x is
> the
> > >> tone, and TABLE is a table of signed differential sine values. The
> > >> simplest form of this would use 1 register per note, so N1 =R4, N2
> > >> = R5 etc. Then the ISR becomes simply:-
> > >>
> > >> TB1_ISR:
> > >> ADD&DACOUT,TABLE(R4)
> > >> ADD&N1INTERVAL,&CCRB1
> > >> BIC #CCIFG,&CCTLB1
> > >> INCD R4
> > >> ;ENTRIES ARE WORDS
> > >> AND #0X00FE,R4 ;ASSUMES
> > >> 256 ENTRY TABLE
> > >> RETI
> > >>
> > >> To overcome scaling issues and the need for a little maths you
> > >> could easily have tables for single tones, 2 note chords, up to 7
> > >> note chords
> > >>
> > >> Cheers
> > >>
> > >> Al
> > >>
> > >> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
> > >>> Onestone,
> > >>> your code is quite good (after some thinking I am now sure that
> it
> > >>> is working:-). One improvement: should be possible to do it with
> > >>> just one
> > >>> CCR- channel.
> > >>>
> > >>> M.
> > >>>
> > >>> Onestone:
> > >>>
> > >>>> being a simple kind of guy I think my method is just too easy!
> > >>>> 8-)
> > >>>>
> > >>>> Al
> > >>>>
> > >>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
> > >>>>> In case you are comfortable with 2 mixed sine waves just use
> > >>>>> both DAC's, feed them independly and add them analog at the
> > >>>>> output pin with 2 resistors.
> > >>>>>
> > >>>>> Another way: create a lookup table for your "chords".
> > >>>>> In your current code you always use 32 steps for each sine wave
> > >>>>> - for one wave a step is e.g. 300us for the 2nd wave it is
> > >>>>> 310us. To mix them digitally you need finer steps.
> > >>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine
> > >>>>> waves into that RAM-table according to the frequencies you
> want.
> > >>>>> Feed this RAM-table with the fastest clock (987Hz is about 1ms)
> > >>>>> to the DAC. For example in a 256 table the 987Hz wave fits 7.55
> > >>>>> times, the 130Hz waves only 1 time. For the 130Hz tone you have
> > >>>>> to calculate 256 values from you 32 values of the lookup table.
> > >>>>> This is called upsampling. You can use linear interpolation
> > >>>>> (lines) to calculate the values between the 32 lookup values
> > >>>>> from your Flash table (or drop the lookup table and use sin()
> > >>>>> from the C compiler, no idea how fast this is). The big deal
> > >>>>> here is, that in most cases the end of the RAM table does not
> > >>>>> fit to the begin of the RAM table, the waves are not connected
> > >>>>> smoothly together. You have to calculate a new RAM table once
> > >>>>> the first one has been sent, so that the waves are smoothly
> > >>>>> connected. Use 2 RAM tables, one for output (DMA), one for
> precalculation (in the main loop).
> > >>>>>
> > >>>>> You can do the interpolation on the fly in the CCR-Interrupt
> > >>>>> routine, no extra RAM table, no DMA needed (however, the sound
> > >>>>> will not be clear because of the jitter of the interrupt
> response time).
> > >>>>>
> > >>>>> M.
> > >>>>>
> > >>>>> Onestone:
> > >>>>>
> > >>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd
> > >>>>>> use
> > >>>>>> 1 timer per tone, and track the last value for each tone. Then
> > >>>>>> on timer interrupt:-
> > >>>>>>
> > >>>>>> DACx = DACx - oldsinevalue + newsinevalue.
> > >>>>>>
> > >>>>>> Effectively remove the old value for the tone from the DAC and
> > >>>>>> add the new one at a variabe time interval. Also, given
> > >>>>>> something with as much memory as the 2619 i'd not limit myself
> to a 32 entry table.
> > >>>>>> In theory, and assuming that you aren't using the other
> timers,
> > >>>>>> you could get 7 tones using timer B. I'd also go for the
> > >>>>>> highest clock frequency possible, and in the ISR keep the code
> short and clean.
> > >>>>>> for minimal jitter. Rather than absolute values your table
> > >>>>>> could comprise differential values so that your calculation is
> > >>>>>> simple DACx = DACx + sine(n).
> > >>>>>>
> > >>>>>> Hope this helps
> > >>>>>>
> > >>>>>> Al
> > >>>>>>
> > >>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
> > >>>>>>> Hi all,
> > >>>>>>> I am currently working on some code for an MSP430F2619. I am
> > >>>>>>> using a 32-value sine lookup table, and pushing the
> sequential
> > >>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
> > >>>>>>> note frequency) controlled by TimerB. So far,this is working
> > >>>>>>> well. I can produce any number of reasonable sounding pure
> > >>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
> > >>>>>>> and this is more than enough range for my needs, three full
> > >>>>>>> octaves.
> > >>>>>>>
> > >>>>>>> However, this only plays one tone at a time. I have been
> > >>>>>>> wracking my brains trying to figure out how to use this to
> > >>>>>>> generate a "chord," more than one note played at the same
> > >>>>>>> time. I know that musically this can be as simple as adding
> > >>>>>>> two sine waves together, but since I don't have two
> > >>>>>>> simultaneously existing sine waves, just one that I change
> the frequency of, I am at a loss.
> > >>>>>>>
> > >>>>>>> Does anyone have any suggestions on how to achieve this, or
> > >>>>>>> references that they think might push me in the right
> direction?
> > >>>>>>>
> > >>>>>>> Thank you,
> > >>>>>>> David
> > >>>>>>>
> > >>>
> > >>>
> > >>>
> > >>>
> > >>>
> > >>>
No, it's not an FFT, It's Direct-Digital-Synthesis, aka
Numerically-Controlled-Oscillator
(NCO) - the wiki explanation is pretty good:

http://en.wikipedia.org/wiki/Numerically_controlled_oscillator

It's trivial to implement in software and computationally very cheap. Each
sample
requires one addition and one look-up, substantially less than an FFT
implementation.

Cheers,
Dana K6JQ

On Thu, Apr 26, 2012 at 1:07 PM, Redd, Emmett R <
E...@missouristate.edu> wrote:

> **
> Dana,
>
> I do not have time to analyze this for myself, but is this just (a special
> case of a) Fast Fourier Transform?
>
> Emmett Redd, Ph.D., Professor mailto:E...@MissouriState.Edu
> Physics, Astronomy, and Materials Science Office: 417-836-5221
> Missouri State University Dept: 417-838-5131
> 901 S NATIONAL AVENUE FAX: 417-836-6226
> SPRINGFIELD, MO 65897 USA Lab: 417-836-3770
>
> It is clear that thought is not free if the profession of certain opinions
> make it impossible to earn a living. -- Bertrand Russell
> > -----Original Message-----
> > From: m... [mailto:m...] On Behalf
> > Of Dana Myers
> > Sent: Thursday, April 26, 2012 2:55 PM
> > To: m...
> > Subject: Re: [msp430] Re: Re: Re: Multiple Sine Waves/Chords from a DAC
> >
> > More detail on DDS - you create a single sine-wave table (it's actually
> > a
> > cosine)
> > and 'step' through it based on a constant sample rate. The frequency is
> > determined by the size of the step.
> >
> > For example, let's just assume we have a 1024-word table:
> >
> > int costab[1024] = { ... }; // initialize with pre-calculated single
> > wave
> > of
> > // cos() * 32767 (-32767 ..
> > 32767)
> >
> > If you use a sample rate of 10240 samples/sec, an increment of
> > 1 will produce a 10Hz sine wave, an increment of 10 will produce 100Hz,
> > etc.
> >
> > You maintain a "phase accumulator" for each desired output frequency.
> > The output is then a stream of samples at whatever precision your
> > cosine table is.
> >
> > You can use a large phase accumulator to provide better/finer frequency
> > granularity and intermediate bits of the accumulator to effectively
> > implement fractional increments between frequencies.
> >
> > You only need one periodic interrupt to drive the output ISR,
> >
> > Cheers,
> > Dana K6JQ
> >
> > On Thu, Apr 26, 2012 at 6:53 AM, Onestone
> > wrote:
> >
> > > **
>
> > >
> > >
> > > Hi Matthias, I still think you're misunderstanding. First of all two
> > > tones would never require the same CCR, secondly chords don't contain
> > > duplicates of the same note (at least in my experience), so 7 unique
> > > tones can be played simultaneously with 7 CCr registers. And those
> > > tones can vary in real time, ie register CCRB1 doesn't have to be C,
> > > at each note, a fresh set of unique notes are available. My code
> > > sample executes in 25 cycles plus interrupt latency of 1-6 cycles, so
> > > 31 worst case. In the case of a 16MHz clock 7 tones would require 217
> > > clock cycles maximum to process in the isrs, plus whatever overhead
> > is
> > > needed in the foreground routine, so there would still be obvious
> > > limits to the maximum access rate. However since the OP wants a range
> > > from 130.8Hz to 987.7Hz a worst case event would mean an upper limit
> > > of 74 entries per table. however there is nothign to stop the user
> > > from having tables of different resolutions, or of varying the step
> > size for different notes.
> > > ie a 256 entry table played at a fixed rate to give 100Hz would give
> > a
> > > 200hz output if the step through the table was 2 instead of 1. this
> > > is, of course, another way of getting a wide frequency range using a
> > > single clock source. In this case longer tables sound better than
> > short ones.
> > > In the case of a single table with variable step sizes to create
> > notes
> > > of different frequencies it would be possible to use a single timer,
> > > and simply add all the different values for each step interval in a
> > > single ISR, however the range of frequencies is more limited. If I
> > > remember my note frequencies there are a limited number of base
> > > frequencies, and then the harmonics of these, which the above method
> > > results in, form the remaining available notes, so a combination of
> > methods could well work.
> > >
> > > Of course, having said all of this, if the OP only needs to play a
> > few
> > > different note combinations, or a few short tunes then, given the
> > huge
> > > amount of available memory why note simply pre-calculate them and
> > > store them in flash?
> > >
> > > Al
> > >
> > >
> > > On 26/04/2012 6:32 PM, Matthias Weingart wrote:
> > > > Onestone:
> > > >
> > > >> Hi Matthias. The idea is to use a single table, but probably a
> > > >> higher resolution table than 32 entries, and to still use the
> > basic
> > > >> concept of timed access to generate different notes. It would be
> > > >> virtually impossible, I believe, to do this with a single timer,
> > > >> and limited jitter, how would you work out the next time interval
> > > >> for, say a 4 note chord? where the frequencies are not some
> > convenient binary multiple?
> > > > Hi Al,
> > > >
> > > > It seems I understand your algorithm quite different then you. Each
> > > > note use it's own counter (e.g. the 7 CCR registers) for the sample
> > clock.
> > > >
> > > >> Then, where one note is a much higher frequency than another you
> > > >> have multiple instances of the higher note per instance of the
> > > >> lower note, so tracking order of tones with a single timer becomes
> > cpu intensive.
> > > > That is true. You need to prepare the settings for the next tone in
> > > > the mainloop (CCR and position in the lookup table). One idea is a
> > > > sorted
> > > table
> > > > in RAM, sorted by next CCR. A special case has to be handled if 2
> > > > (or
> > > more)
> > > > tones requires the same CCR. Even with your method the jitter is
> > > > higher
> > > in
> > > > case 2 (or more) CCR-interrupts need to get served at the same CCR-
> > value.
> > > > However my method is only suitable for very low frequencies.
> > > >
> > > > M.
> > > >
> > > >> Certainly it would be far easier to use a single timer per tone.
> > > >> Also you'd need to take scaling into account. By using a timer per
> > > >> note you reduce the calculations (using a differential sine table)
> > > >> to DACOUT = DACOUT + TABLE(Nx) where N is the table entry, x is
> > the
> > > >> tone, and TABLE is a table of signed differential sine values. The
> > > >> simplest form of this would use 1 register per note, so N1 =R4, N2
> > > >> = R5 etc. Then the ISR becomes simply:-
> > > >>
> > > >> TB1_ISR:
> > > >> ADD&DACOUT,TABLE(R4)
> > > >> ADD&N1INTERVAL,&CCRB1
> > > >> BIC #CCIFG,&CCTLB1
> > > >> INCD R4
> > > >> ;ENTRIES ARE WORDS
> > > >> AND #0X00FE,R4 ;ASSUMES
> > > >> 256 ENTRY TABLE
> > > >> RETI
> > > >>
> > > >> To overcome scaling issues and the need for a little maths you
> > > >> could easily have tables for single tones, 2 note chords, up to 7
> > > >> note chords
> > > >>
> > > >> Cheers
> > > >>
> > > >> Al
> > > >>
> > > >> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
> > > >>> Onestone,
> > > >>> your code is quite good (after some thinking I am now sure that
> > it
> > > >>> is working:-). One improvement: should be possible to do it with
> > > >>> just one
> > > >>> CCR- channel.
> > > >>>
> > > >>> M.
> > > >>>
> > > >>> Onestone:
> > > >>>
> > > >>>> being a simple kind of guy I think my method is just too easy!
> > > >>>> 8-)
> > > >>>>
> > > >>>> Al
> > > >>>>
> > > >>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
> > > >>>>> In case you are comfortable with 2 mixed sine waves just use
> > > >>>>> both DAC's, feed them independly and add them analog at the
> > > >>>>> output pin with 2 resistors.
> > > >>>>>
> > > >>>>> Another way: create a lookup table for your "chords".
> > > >>>>> In your current code you always use 32 steps for each sine wave
> > > >>>>> - for one wave a step is e.g. 300us for the 2nd wave it is
> > > >>>>> 310us. To mix them digitally you need finer steps.
> > > >>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine
> > > >>>>> waves into that RAM-table according to the frequencies you
> > want.
> > > >>>>> Feed this RAM-table with the fastest clock (987Hz is about 1ms)
> > > >>>>> to the DAC. For example in a 256 table the 987Hz wave fits 7.55
> > > >>>>> times, the 130Hz waves only 1 time. For the 130Hz tone you have
> > > >>>>> to calculate 256 values from you 32 values of the lookup table.
> > > >>>>> This is called upsampling. You can use linear interpolation
> > > >>>>> (lines) to calculate the values between the 32 lookup values
> > > >>>>> from your Flash table (or drop the lookup table and use sin()
> > > >>>>> from the C compiler, no idea how fast this is). The big deal
> > > >>>>> here is, that in most cases the end of the RAM table does not
> > > >>>>> fit to the begin of the RAM table, the waves are not connected
> > > >>>>> smoothly together. You have to calculate a new RAM table once
> > > >>>>> the first one has been sent, so that the waves are smoothly
> > > >>>>> connected. Use 2 RAM tables, one for output (DMA), one for
> > precalculation (in the main loop).
> > > >>>>>
> > > >>>>> You can do the interpolation on the fly in the CCR-Interrupt
> > > >>>>> routine, no extra RAM table, no DMA needed (however, the sound
> > > >>>>> will not be clear because of the jitter of the interrupt
> > response time).
> > > >>>>>
> > > >>>>> M.
> > > >>>>>
> > > >>>>> Onestone:
> > > >>>>>
> > > >>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd
> > > >>>>>> use
> > > >>>>>> 1 timer per tone, and track the last value for each tone. Then
> > > >>>>>> on timer interrupt:-
> > > >>>>>>
> > > >>>>>> DACx = DACx - oldsinevalue + newsinevalue.
> > > >>>>>>
> > > >>>>>> Effectively remove the old value for the tone from the DAC and
> > > >>>>>> add the new one at a variabe time interval. Also, given
> > > >>>>>> something with as much memory as the 2619 i'd not limit myself
> > to a 32 entry table.
> > > >>>>>> In theory, and assuming that you aren't using the other
> > timers,
> > > >>>>>> you could get 7 tones using timer B. I'd also go for the
> > > >>>>>> highest clock frequency possible, and in the ISR keep the code
> > short and clean.
> > > >>>>>> for minimal jitter. Rather than absolute values your table
> > > >>>>>> could comprise differential values so that your calculation is
> > > >>>>>> simple DACx = DACx + sine(n).
> > > >>>>>>
> > > >>>>>> Hope this helps
> > > >>>>>>
> > > >>>>>> Al
> > > >>>>>>
> > > >>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
> > > >>>>>>> Hi all,
> > > >>>>>>> I am currently working on some code for an MSP430F2619. I am
> > > >>>>>>> using a 32-value sine lookup table, and pushing the
> > sequential
> > > >>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
> > > >>>>>>> note frequency) controlled by TimerB. So far,this is working
> > > >>>>>>> well. I can produce any number of reasonable sounding pure
> > > >>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
> > > >>>>>>> and this is more than enough range for my needs, three full
> > > >>>>>>> octaves.
> > > >>>>>>>
> > > >>>>>>> However, this only plays one tone at a time. I have been
> > > >>>>>>> wracking my brains trying to figure out how to use this to
> > > >>>>>>> generate a "chord," more than one note played at the same
> > > >>>>>>> time. I know that musically this can be as simple as adding
> > > >>>>>>> two sine waves together, but since I don't have two
> > > >>>>>>> simultaneously existing sine waves, just one that I change
> > the frequency of, I am at a loss.
> > > >>>>>>>
> > > >>>>>>> Does anyone have any suggestions on how to achieve this, or
> > > >>>>>>> references that they think might push me in the right
> > direction?
> > > >>>>>>>
> > > >>>>>>> Thank you,
> > > >>>>>>> David
> > > >>>>>>>
> > > >>>
> > > >>>
> > > >>>
> > > >>>
> > > >>>
> > > >>>
I'm aware of DDS, but I don't think it fits so well here. You have two
issues. It is still an integer step, therefore frequencies are limited,
and producing ad hoc chords, where more than one note has a fractional
frequency, or non integer step is impossible. The chord would sound
'off'. For example you cannot reproduce the 103.7Hz required by the OP,
and still produce 987.7Hz from the same table. How do you get ad hoc
chords with a single periodic interrupt? I know you can simply add all
the step values, but since they are integer stepped this won't produce a
true chord.

Al

On 27/04/2012 5:24 AM, Dana Myers wrote:
> More detail on DDS - you create a single sine-wave table (it's actually a
> cosine)
> and 'step' through it based on a constant sample rate. The frequency is
> determined by the size of the step.
>
> For example, let's just assume we have a 1024-word table:
>
> int costab[1024] = { ... }; // initialize with pre-calculated single wave
> of
> // cos() * 32767 (-32767 .. 32767)
>
> If you use a sample rate of 10240 samples/sec, an increment of
> 1 will produce a 10Hz sine wave, an increment of 10 will produce 100Hz,
> etc.
>
> You maintain a "phase accumulator" for each desired output frequency.
> The output is then a stream of samples at whatever precision your
> cosine table is.
>
> You can use a large phase accumulator to provide better/finer
> frequency granularity and intermediate bits of the accumulator
> to effectively implement fractional increments between frequencies.
>
> You only need one periodic interrupt to drive the output ISR,
>
> Cheers,
> Dana K6JQ
>
> On Thu, Apr 26, 2012 at 6:53 AM, Onestone wrote:
>
>> **
>> Hi Matthias, I still think you're misunderstanding. First of all two
>> tones would never require the same CCR, secondly chords don't contain
>> duplicates of the same note (at least in my experience), so 7 unique
>> tones can be played simultaneously with 7 CCr registers. And those tones
>> can vary in real time, ie register CCRB1 doesn't have to be C, at each
>> note, a fresh set of unique notes are available. My code sample
>> executes in 25 cycles plus interrupt latency of 1-6 cycles, so 31 worst
>> case. In the case of a 16MHz clock 7 tones would require 217 clock
>> cycles maximum to process in the isrs, plus whatever overhead is needed
>> in the foreground routine, so there would still be obvious limits to the
>> maximum access rate. However since the OP wants a range from 130.8Hz to
>> 987.7Hz a worst case event would mean an upper limit of 74 entries per
>> table. however there is nothign to stop the user from having tables of
>> different resolutions, or of varying the step size for different notes.
>> ie a 256 entry table played at a fixed rate to give 100Hz would give a
>> 200hz output if the step through the table was 2 instead of 1. this is,
>> of course, another way of getting a wide frequency range using a single
>> clock source. In this case longer tables sound better than short ones.
>> In the case of a single table with variable step sizes to create notes
>> of different frequencies it would be possible to use a single timer, and
>> simply add all the different values for each step interval in a single
>> ISR, however the range of frequencies is more limited. If I remember my
>> note frequencies there are a limited number of base frequencies, and
>> then the harmonics of these, which the above method results in, form the
>> remaining available notes, so a combination of methods could well work.
>>
>> Of course, having said all of this, if the OP only needs to play a few
>> different note combinations, or a few short tunes then, given the huge
>> amount of available memory why note simply pre-calculate them and store
>> them in flash?
>>
>> Al
>> On 26/04/2012 6:32 PM, Matthias Weingart wrote:
>>> Onestone:
>>>
>>>> Hi Matthias. The idea is to use a single table, but probably a higher
>>>> resolution table than 32 entries, and to still use the basic concept of
>>>> timed access to generate different notes. It would be virtually
>>>> impossible, I believe, to do this with a single timer, and limited
>>>> jitter, how would you work out the next time interval for, say a 4 note
>>>> chord? where the frequencies are not some convenient binary multiple?
>>> Hi Al,
>>>
>>> It seems I understand your algorithm quite different then you. Each note
>>> use it's own counter (e.g. the 7 CCR registers) for the sample clock.
>>>
>>>> Then, where one note is a much higher frequency than another you have
>>>> multiple instances of the higher note per instance of the lower note, so
>>>> tracking order of tones with a single timer becomes cpu intensive.
>>> That is true. You need to prepare the settings for the next tone in the
>>> mainloop (CCR and position in the lookup table). One idea is a sorted
>> table
>>> in RAM, sorted by next CCR. A special case has to be handled if 2 (or
>> more)
>>> tones requires the same CCR. Even with your method the jitter is higher
>> in
>>> case 2 (or more) CCR-interrupts need to get served at the same CCR-value.
>>> However my method is only suitable for very low frequencies.
>>>
>>> M.
>>>
>>>> Certainly it would be far easier to use a single timer per tone. Also
>>>> you'd need to take scaling into account. By using a timer per note you
>>>> reduce the calculations (using a differential sine table) to DACOUT >>>> DACOUT + TABLE(Nx) where N is the table entry, x is the tone, and TABLE
>>>> is a table of signed differential sine values. The simplest form of this
>>>> would use 1 register per note, so N1 =R4, N2 = R5 etc. Then the ISR
>>>> becomes simply:-
>>>>
>>>> TB1_ISR:
>>>> ADD&DACOUT,TABLE(R4)
>>>> ADD&N1INTERVAL,&CCRB1
>>>> BIC #CCIFG,&CCTLB1
>>>> INCD R4
>>>> ;ENTRIES ARE WORDS
>>>> AND #0X00FE,R4 ;ASSUMES
>>>> 256 ENTRY TABLE
>>>> RETI
>>>>
>>>> To overcome scaling issues and the need for a little maths you could
>>>> easily have tables for single tones, 2 note chords, up to 7 note chords
>>>>
>>>> Cheers
>>>>
>>>> Al
>>>>
>>>> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
>>>>> Onestone,
>>>>> your code is quite good (after some thinking I am now sure that it is
>>>>> working:-). One improvement: should be possible to do it with just one
>>>>> CCR- channel.
>>>>>
>>>>> M.
>>>>>
>>>>> Onestone:
>>>>>
>>>>>> being a simple kind of guy I think my method is just too easy! 8-)
>>>>>>
>>>>>> Al
>>>>>>
>>>>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
>>>>>>> In case you are comfortable with 2 mixed sine waves just use both
>>>>>>> DAC's, feed them independly and add them analog at the output pin
>>>>>>> with 2 resistors.
>>>>>>>
>>>>>>> Another way: create a lookup table for your "chords".
>>>>>>> In your current code you always use 32 steps for each sine wave - for
>>>>>>> one wave a step is e.g. 300us for the 2nd wave it is 310us. To mix
>>>>>>> them digitally you need finer steps.
>>>>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine waves
>>>>>>> into that RAM-table according to the frequencies you want. Feed this
>>>>>>> RAM-table with the fastest clock (987Hz is about 1ms) to the DAC. For
>>>>>>> example in a 256 table the 987Hz wave fits 7.55 times, the 130Hz
>>>>>>> waves only 1 time. For the 130Hz tone you have to calculate 256
>>>>>>> values from you 32 values of the lookup table. This is called
>>>>>>> upsampling. You can use linear interpolation (lines) to calculate the
>>>>>>> values between the 32 lookup values from your Flash table (or drop
>>>>>>> the lookup table and use sin() from the C compiler, no idea how fast
>>>>>>> this is). The big deal here is, that in most cases the end of the RAM
>>>>>>> table does not fit to the begin of the RAM table, the waves are not
>>>>>>> connected smoothly together. You have to calculate a new RAM table
>>>>>>> once the first one has been sent, so that the waves are smoothly
>>>>>>> connected. Use 2 RAM tables, one for output (DMA), one for
>>>>>>> precalculation (in the main loop).
>>>>>>>
>>>>>>> You can do the interpolation on the fly in the CCR-Interrupt routine,
>>>>>>> no extra RAM table, no DMA needed (however, the sound will not be
>>>>>>> clear because of the jitter of the interrupt response time).
>>>>>>>
>>>>>>> M.
>>>>>>>
>>>>>>> Onestone:
>>>>>>>
>>>>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd use
>>>>>>>> 1 timer per tone, and track the last value for each tone. Then on
>>>>>>>> timer interrupt:-
>>>>>>>>
>>>>>>>> DACx = DACx - oldsinevalue + newsinevalue.
>>>>>>>>
>>>>>>>> Effectively remove the old value for the tone from the DAC and add
>>>>>>>> the new one at a variabe time interval. Also, given something with
>>>>>>>> as much memory as the 2619 i'd not limit myself to a 32 entry table.
>>>>>>>> In theory, and assuming that you aren't using the other timers, you
>>>>>>>> could get 7 tones using timer B. I'd also go for the highest clock
>>>>>>>> frequency possible, and in the ISR keep the code short and clean.
>>>>>>>> for minimal jitter. Rather than absolute values your table could
>>>>>>>> comprise differential values so that your calculation is simple DACx
>>>>>>>> = DACx + sine(n).
>>>>>>>>
>>>>>>>> Hope this helps
>>>>>>>>
>>>>>>>> Al
>>>>>>>>
>>>>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
>>>>>>>>> Hi all,
>>>>>>>>> I am currently working on some code for an MSP430F2619. I am
>>>>>>>>> using a 32-value sine lookup table, and pushing the sequential
>>>>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
>>>>>>>>> note frequency) controlled by TimerB. So far,this is working
>>>>>>>>> well. I can produce any number of reasonable sounding pure
>>>>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
>>>>>>>>> and this is more than enough range for my needs, three full
>>>>>>>>> octaves.
>>>>>>>>>
>>>>>>>>> However, this only plays one tone at a time. I have been wracking
>>>>>>>>> my brains trying to figure out how to use this to generate a
>>>>>>>>> "chord," more than one note played at the same time. I know that
>>>>>>>>> musically this can be as simple as adding two sine waves together,
>>>>>>>>> but since I don't have two simultaneously existing sine waves, just
>>>>>>>>> one that I change the frequency of, I am at a loss.
>>>>>>>>>
>>>>>>>>> Does anyone have any suggestions on how to achieve this, or
>>>>>>>>> references that they think might push me in the right direction?
>>>>>>>>>
>>>>>>>>> Thank you,
>>>>>>>>> David
>>>>>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
Dana,

in case I understand Al's comment right :-) - he is doing it in a different
way. The same lockup table is sent to the DAC with different sample rates.
No value from the table is skipped, they are sent to the DAC with different
speeds. e.g. for the first tone every 200 timer clocks, for the 2nd tone
with every 201 timer clocks...

M.

Dana Myers :

> More detail on DDS - you create a single sine-wave table (it's actually
> a cosine)
> and 'step' through it based on a constant sample rate. The frequency is
> determined by the size of the step.
>
> For example, let's just assume we have a 1024-word table:
>
> int costab[1024] = { ... }; // initialize with pre-calculated single
> wave of
> // cos() * 32767 (-32767 .. 32767)
>
> If you use a sample rate of 10240 samples/sec, an increment of
> 1 will produce a 10Hz sine wave, an increment of 10 will produce 100Hz,
> etc.
>
> You maintain a "phase accumulator" for each desired output frequency.
> The output is then a stream of samples at whatever precision your
> cosine table is.
>
> You can use a large phase accumulator to provide better/finer
> frequency granularity and intermediate bits of the accumulator
> to effectively implement fractional increments between frequencies.
>
> You only need one periodic interrupt to drive the output ISR,
>
> Cheers,
> Dana K6JQ
>
> On Thu, Apr 26, 2012 at 6:53 AM, Onestone
> wrote:
>
>> **
>> Hi Matthias, I still think you're misunderstanding. First of all two
>> tones would never require the same CCR, secondly chords don't contain
>> duplicates of the same note (at least in my experience), so 7 unique
>> tones can be played simultaneously with 7 CCr registers. And those
>> tones can vary in real time, ie register CCRB1 doesn't have to be C, at
>> each note, a fresh set of unique notes are available. My code sample
>> executes in 25 cycles plus interrupt latency of 1-6 cycles, so 31 worst
>> case. In the case of a 16MHz clock 7 tones would require 217 clock
>> cycles maximum to process in the isrs, plus whatever overhead is needed
>> in the foreground routine, so there would still be obvious limits to
>> the maximum access rate. However since the OP wants a range from
>> 130.8Hz to 987.7Hz a worst case event would mean an upper limit of 74
>> entries per table. however there is nothign to stop the user from
>> having tables of different resolutions, or of varying the step size for
>> different notes. ie a 256 entry table played at a fixed rate to give
>> 100Hz would give a 200hz output if the step through the table was 2
>> instead of 1. this is, of course, another way of getting a wide
>> frequency range using a single clock source. In this case longer tables
>> sound better than short ones. In the case of a single table with
>> variable step sizes to create notes of different frequencies it would
>> be possible to use a single timer, and simply add all the different
>> values for each step interval in a single ISR, however the range of
>> frequencies is more limited. If I remember my note frequencies there
>> are a limited number of base frequencies, and then the harmonics of
>> these, which the above method results in, form the remaining available
>> notes, so a combination of methods could well work.
>>
>> Of course, having said all of this, if the OP only needs to play a few
>> different note combinations, or a few short tunes then, given the huge
>> amount of available memory why note simply pre-calculate them and store
>> them in flash?
>>
>> Al
>> On 26/04/2012 6:32 PM, Matthias Weingart wrote:
>> > Onestone:
>> >
>> >> Hi Matthias. The idea is to use a single table, but probably a
>> >> higher resolution table than 32 entries, and to still use the basic
>> >> concept of timed access to generate different notes. It would be
>> >> virtually impossible, I believe, to do this with a single timer, and
>> >> limited jitter, how would you work out the next time interval for,
>> >> say a 4 note chord? where the frequencies are not some convenient
>> >> binary multiple?
>> > Hi Al,
>> >
>> > It seems I understand your algorithm quite different then you. Each
>> > note use it's own counter (e.g. the 7 CCR registers) for the sample
>> > clock.
>> >
>> >> Then, where one note is a much higher frequency than another you
>> >> have multiple instances of the higher note per instance of the lower
>> >> note, so tracking order of tones with a single timer becomes cpu
>> >> intensive.
>> > That is true. You need to prepare the settings for the next tone in
>> > the mainloop (CCR and position in the lookup table). One idea is a
>> > sorted
>> table
>> > in RAM, sorted by next CCR. A special case has to be handled if 2 (or
>> more)
>> > tones requires the same CCR. Even with your method the jitter is
>> > higher
>> in
>> > case 2 (or more) CCR-interrupts need to get served at the same
>> > CCR-value. However my method is only suitable for very low
>> > frequencies.
>> >
>> > M.
>> >
>> >> Certainly it would be far easier to use a single timer per tone.
>> >> Also you'd need to take scaling into account. By using a timer per
>> >> note you reduce the calculations (using a differential sine table)
>> >> to DACOUT = DACOUT + TABLE(Nx) where N is the table entry, x is the
>> >> tone, and TABLE is a table of signed differential sine values. The
>> >> simplest form of this would use 1 register per note, so N1 =R4, N2 >> >> R5 etc. Then the ISR becomes simply:-
>> >>
>> >> TB1_ISR:
>> >> ADD&DACOUT,TABLE(R4)
>> >> ADD&N1INTERVAL,&CCRB1
>> >> BIC #CCIFG,&CCTLB1
>> >> INCD R4
>> >> ;ENTRIES ARE WORDS
>> >> AND #0X00FE,R4 ;ASSUMES
>> >> 256 ENTRY TABLE
>> >> RETI
>> >>
>> >> To overcome scaling issues and the need for a little maths you could
>> >> easily have tables for single tones, 2 note chords, up to 7 note
>> >> chords
>> >>
>> >> Cheers
>> >>
>> >> Al
>> >>
>> >> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
>> >>> Onestone,
>> >>> your code is quite good (after some thinking I am now sure that it
>> >>> is working:-). One improvement: should be possible to do it with
>> >>> just one CCR- channel.
>> >>>
>> >>> M.
>> >>>
>> >>> Onestone:
>> >>>
>> >>>> being a simple kind of guy I think my method is just too easy! 8-)
>> >>>>
>> >>>> Al
>> >>>>
>> >>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
>> >>>>> In case you are comfortable with 2 mixed sine waves just use both
>> >>>>> DAC's, feed them independly and add them analog at the output pin
>> >>>>> with 2 resistors.
>> >>>>>
>> >>>>> Another way: create a lookup table for your "chords".
>> >>>>> In your current code you always use 32 steps for each sine wave -
>> >>>>> for one wave a step is e.g. 300us for the 2nd wave it is 310us.
>> >>>>> To mix them digitally you need finer steps.
>> >>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine
>> >>>>> waves into that RAM-table according to the frequencies you want.
>> >>>>> Feed this RAM-table with the fastest clock (987Hz is about 1ms)
>> >>>>> to the DAC. For example in a 256 table the 987Hz wave fits 7.55
>> >>>>> times, the 130Hz waves only 1 time. For the 130Hz tone you have
>> >>>>> to calculate 256 values from you 32 values of the lookup table.
>> >>>>> This is called upsampling. You can use linear interpolation
>> >>>>> (lines) to calculate the values between the 32 lookup values from
>> >>>>> your Flash table (or drop the lookup table and use sin() from the
>> >>>>> C compiler, no idea how fast this is). The big deal here is, that
>> >>>>> in most cases the end of the RAM table does not fit to the begin
>> >>>>> of the RAM table, the waves are not connected smoothly together.
>> >>>>> You have to calculate a new RAM table once the first one has been
>> >>>>> sent, so that the waves are smoothly connected. Use 2 RAM tables,
>> >>>>> one for output (DMA), one for precalculation (in the main loop).
>> >>>>>
>> >>>>> You can do the interpolation on the fly in the CCR-Interrupt
>> >>>>> routine, no extra RAM table, no DMA needed (however, the sound
>> >>>>> will not be clear because of the jitter of the interrupt response
>> >>>>> time).
>> >>>>>
>> >>>>> M.
>> >>>>>
>> >>>>> Onestone:
>> >>>>>
>> >>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd
>> >>>>>> use 1 timer per tone, and track the last value for each tone.
>> >>>>>> Then on timer interrupt:-
>> >>>>>>
>> >>>>>> DACx = DACx - oldsinevalue + newsinevalue.
>> >>>>>>
>> >>>>>> Effectively remove the old value for the tone from the DAC and
>> >>>>>> add the new one at a variabe time interval. Also, given
>> >>>>>> something with as much memory as the 2619 i'd not limit myself
>> >>>>>> to a 32 entry table. In theory, and assuming that you aren't
>> >>>>>> using the other timers, you could get 7 tones using timer B. I'd
>> >>>>>> also go for the highest clock frequency possible, and in the ISR
>> >>>>>> keep the code short and clean. for minimal jitter. Rather than
>> >>>>>> absolute values your table could comprise differential values so
>> >>>>>> that your calculation is simple DACx = DACx + sine(n).
>> >>>>>>
>> >>>>>> Hope this helps
>> >>>>>>
>> >>>>>> Al
>> >>>>>>
>> >>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
>> >>>>>>> Hi all,
>> >>>>>>> I am currently working on some code for an MSP430F2619. I am
>> >>>>>>> using a 32-value sine lookup table, and pushing the sequential
>> >>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
>> >>>>>>> note frequency) controlled by TimerB. So far,this is working
>> >>>>>>> well. I can produce any number of reasonable sounding pure
>> >>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
>> >>>>>>> and this is more than enough range for my needs, three full
>> >>>>>>> octaves.
>> >>>>>>>
>> >>>>>>> However, this only plays one tone at a time. I have been
>> >>>>>>> wracking my brains trying to figure out how to use this to
>> >>>>>>> generate a "chord," more than one note played at the same time.
>> >>>>>>> I know that musically this can be as simple as adding two sine
>> >>>>>>> waves together, but since I don't have two simultaneously
>> >>>>>>> existing sine waves, just one that I change the frequency of, I
>> >>>>>>> am at a loss.
>> >>>>>>>
>> >>>>>>> Does anyone have any suggestions on how to achieve this, or
>> >>>>>>> references that they think might push me in the right
>> >>>>>>> direction?
>> >>>>>>>
>> >>>>>>> Thank you,
>> >>>>>>> David
>> >>>>>>>
>> >>>

That's correct. With a high speed clock it offers better granularity
than integer stepping through the table. It is still DDS, although it's
something I've been doing for at least 30 years, although I've never had
to generate chords before, and I've only recently heard it called DDS.
I'm sure somebody somewhere (probably some young sprog) sits and invents
all these TLA's just to confuse us old farts.

Al

On 27/04/2012 4:16 PM, Matthias Weingart wrote:
> Dana,
>
> in case I understand Al's comment right :-) - he is doing it in a different
> way. The same lockup table is sent to the DAC with different sample rates.
> No value from the table is skipped, they are sent to the DAC with different
> speeds. e.g. for the first tone every 200 timer clocks, for the 2nd tone
> with every 201 timer clocks...
>
> M.
>
> Dana Myers:
>
>> More detail on DDS - you create a single sine-wave table (it's actually
>> a cosine)
>> and 'step' through it based on a constant sample rate. The frequency is
>> determined by the size of the step.
>>
>> For example, let's just assume we have a 1024-word table:
>>
>> int costab[1024] = { ... }; // initialize with pre-calculated single
>> wave of
>> // cos() * 32767 (-32767 .. 32767)
>>
>> If you use a sample rate of 10240 samples/sec, an increment of
>> 1 will produce a 10Hz sine wave, an increment of 10 will produce 100Hz,
>> etc.
>>
>> You maintain a "phase accumulator" for each desired output frequency.
>> The output is then a stream of samples at whatever precision your
>> cosine table is.
>>
>> You can use a large phase accumulator to provide better/finer
>> frequency granularity and intermediate bits of the accumulator
>> to effectively implement fractional increments between frequencies.
>>
>> You only need one periodic interrupt to drive the output ISR,
>>
>> Cheers,
>> Dana K6JQ
>>
>> On Thu, Apr 26, 2012 at 6:53 AM, Onestone
>> wrote:
>>
>>> **
>>>
>>>
>>> Hi Matthias, I still think you're misunderstanding. First of all two
>>> tones would never require the same CCR, secondly chords don't contain
>>> duplicates of the same note (at least in my experience), so 7 unique
>>> tones can be played simultaneously with 7 CCr registers. And those
>>> tones can vary in real time, ie register CCRB1 doesn't have to be C, at
>>> each note, a fresh set of unique notes are available. My code sample
>>> executes in 25 cycles plus interrupt latency of 1-6 cycles, so 31 worst
>>> case. In the case of a 16MHz clock 7 tones would require 217 clock
>>> cycles maximum to process in the isrs, plus whatever overhead is needed
>>> in the foreground routine, so there would still be obvious limits to
>>> the maximum access rate. However since the OP wants a range from
>>> 130.8Hz to 987.7Hz a worst case event would mean an upper limit of 74
>>> entries per table. however there is nothign to stop the user from
>>> having tables of different resolutions, or of varying the step size for
>>> different notes. ie a 256 entry table played at a fixed rate to give
>>> 100Hz would give a 200hz output if the step through the table was 2
>>> instead of 1. this is, of course, another way of getting a wide
>>> frequency range using a single clock source. In this case longer tables
>>> sound better than short ones. In the case of a single table with
>>> variable step sizes to create notes of different frequencies it would
>>> be possible to use a single timer, and simply add all the different
>>> values for each step interval in a single ISR, however the range of
>>> frequencies is more limited. If I remember my note frequencies there
>>> are a limited number of base frequencies, and then the harmonics of
>>> these, which the above method results in, form the remaining available
>>> notes, so a combination of methods could well work.
>>>
>>> Of course, having said all of this, if the OP only needs to play a few
>>> different note combinations, or a few short tunes then, given the huge
>>> amount of available memory why note simply pre-calculate them and store
>>> them in flash?
>>>
>>> Al
>>>
>>>
>>> On 26/04/2012 6:32 PM, Matthias Weingart wrote:
>>>> Onestone:
>>>>
>>>>> Hi Matthias. The idea is to use a single table, but probably a
>>>>> higher resolution table than 32 entries, and to still use the basic
>>>>> concept of timed access to generate different notes. It would be
>>>>> virtually impossible, I believe, to do this with a single timer, and
>>>>> limited jitter, how would you work out the next time interval for,
>>>>> say a 4 note chord? where the frequencies are not some convenient
>>>>> binary multiple?
>>>> Hi Al,
>>>>
>>>> It seems I understand your algorithm quite different then you. Each
>>>> note use it's own counter (e.g. the 7 CCR registers) for the sample
>>>> clock.
>>>>
>>>>> Then, where one note is a much higher frequency than another you
>>>>> have multiple instances of the higher note per instance of the lower
>>>>> note, so tracking order of tones with a single timer becomes cpu
>>>>> intensive.
>>>> That is true. You need to prepare the settings for the next tone in
>>>> the mainloop (CCR and position in the lookup table). One idea is a
>>>> sorted
>>> table
>>>> in RAM, sorted by next CCR. A special case has to be handled if 2 (or
>>> more)
>>>> tones requires the same CCR. Even with your method the jitter is
>>>> higher
>>> in
>>>> case 2 (or more) CCR-interrupts need to get served at the same
>>>> CCR-value. However my method is only suitable for very low
>>>> frequencies.
>>>>
>>>> M.
>>>>
>>>>> Certainly it would be far easier to use a single timer per tone.
>>>>> Also you'd need to take scaling into account. By using a timer per
>>>>> note you reduce the calculations (using a differential sine table)
>>>>> to DACOUT = DACOUT + TABLE(Nx) where N is the table entry, x is the
>>>>> tone, and TABLE is a table of signed differential sine values. The
>>>>> simplest form of this would use 1 register per note, so N1 =R4, N2 >>>>> R5 etc. Then the ISR becomes simply:-
>>>>>
>>>>> TB1_ISR:
>>>>> ADD&DACOUT,TABLE(R4)
>>>>> ADD&N1INTERVAL,&CCRB1
>>>>> BIC #CCIFG,&CCTLB1
>>>>> INCD R4
>>>>> ;ENTRIES ARE WORDS
>>>>> AND #0X00FE,R4 ;ASSUMES
>>>>> 256 ENTRY TABLE
>>>>> RETI
>>>>>
>>>>> To overcome scaling issues and the need for a little maths you could
>>>>> easily have tables for single tones, 2 note chords, up to 7 note
>>>>> chords
>>>>>
>>>>> Cheers
>>>>>
>>>>> Al
>>>>>
>>>>> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
>>>>>> Onestone,
>>>>>> your code is quite good (after some thinking I am now sure that it
>>>>>> is working:-). One improvement: should be possible to do it with
>>>>>> just one CCR- channel.
>>>>>>
>>>>>> M.
>>>>>>
>>>>>> Onestone:
>>>>>>
>>>>>>> being a simple kind of guy I think my method is just too easy! 8-)
>>>>>>>
>>>>>>> Al
>>>>>>>
>>>>>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
>>>>>>>> In case you are comfortable with 2 mixed sine waves just use both
>>>>>>>> DAC's, feed them independly and add them analog at the output pin
>>>>>>>> with 2 resistors.
>>>>>>>>
>>>>>>>> Another way: create a lookup table for your "chords".
>>>>>>>> In your current code you always use 32 steps for each sine wave -
>>>>>>>> for one wave a step is e.g. 300us for the 2nd wave it is 310us.
>>>>>>>> To mix them digitally you need finer steps.
>>>>>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine
>>>>>>>> waves into that RAM-table according to the frequencies you want.
>>>>>>>> Feed this RAM-table with the fastest clock (987Hz is about 1ms)
>>>>>>>> to the DAC. For example in a 256 table the 987Hz wave fits 7.55
>>>>>>>> times, the 130Hz waves only 1 time. For the 130Hz tone you have
>>>>>>>> to calculate 256 values from you 32 values of the lookup table.
>>>>>>>> This is called upsampling. You can use linear interpolation
>>>>>>>> (lines) to calculate the values between the 32 lookup values from
>>>>>>>> your Flash table (or drop the lookup table and use sin() from the
>>>>>>>> C compiler, no idea how fast this is). The big deal here is, that
>>>>>>>> in most cases the end of the RAM table does not fit to the begin
>>>>>>>> of the RAM table, the waves are not connected smoothly together.
>>>>>>>> You have to calculate a new RAM table once the first one has been
>>>>>>>> sent, so that the waves are smoothly connected. Use 2 RAM tables,
>>>>>>>> one for output (DMA), one for precalculation (in the main loop).
>>>>>>>>
>>>>>>>> You can do the interpolation on the fly in the CCR-Interrupt
>>>>>>>> routine, no extra RAM table, no DMA needed (however, the sound
>>>>>>>> will not be clear because of the jitter of the interrupt response
>>>>>>>> time).
>>>>>>>>
>>>>>>>> M.
>>>>>>>>
>>>>>>>> Onestone:
>>>>>>>>
>>>>>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd
>>>>>>>>> use 1 timer per tone, and track the last value for each tone.
>>>>>>>>> Then on timer interrupt:-
>>>>>>>>>
>>>>>>>>> DACx = DACx - oldsinevalue + newsinevalue.
>>>>>>>>>
>>>>>>>>> Effectively remove the old value for the tone from the DAC and
>>>>>>>>> add the new one at a variabe time interval. Also, given
>>>>>>>>> something with as much memory as the 2619 i'd not limit myself
>>>>>>>>> to a 32 entry table. In theory, and assuming that you aren't
>>>>>>>>> using the other timers, you could get 7 tones using timer B. I'd
>>>>>>>>> also go for the highest clock frequency possible, and in the ISR
>>>>>>>>> keep the code short and clean. for minimal jitter. Rather than
>>>>>>>>> absolute values your table could comprise differential values so
>>>>>>>>> that your calculation is simple DACx = DACx + sine(n).
>>>>>>>>>
>>>>>>>>> Hope this helps
>>>>>>>>>
>>>>>>>>> Al
>>>>>>>>>
>>>>>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
>>>>>>>>>> Hi all,
>>>>>>>>>> I am currently working on some code for an MSP430F2619. I am
>>>>>>>>>> using a 32-value sine lookup table, and pushing the sequential
>>>>>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
>>>>>>>>>> note frequency) controlled by TimerB. So far,this is working
>>>>>>>>>> well. I can produce any number of reasonable sounding pure
>>>>>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
>>>>>>>>>> and this is more than enough range for my needs, three full
>>>>>>>>>> octaves.
>>>>>>>>>>
>>>>>>>>>> However, this only plays one tone at a time. I have been
>>>>>>>>>> wracking my brains trying to figure out how to use this to
>>>>>>>>>> generate a "chord," more than one note played at the same time.
>>>>>>>>>> I know that musically this can be as simple as adding two sine
>>>>>>>>>> waves together, but since I don't have two simultaneously
>>>>>>>>>> existing sine waves, just one that I change the frequency of, I
>>>>>>>>>> am at a loss.
>>>>>>>>>>
>>>>>>>>>> Does anyone have any suggestions on how to achieve this, or
>>>>>>>>>> references that they think might push me in the right
>>>>>>>>>> direction?
>>>>>>>>>>
>>>>>>>>>> Thank you,
>>>>>>>>>> David
>>>>>>>>>>
>
To get finer frequency granularity with an NCO, you use more bits in the
phase accumulator; let's say 32-bits. Then you use the highest-order
bits of the accumulator as the cosine-table lookup value. This sets the
basic frequency granule as one "cycle" through the full phase accumulator.
If the clock rate - the sample rate - is 10KHz, one cycle will be
10KHz/2^32,
or 2.3283 microHertz, which means you can synthesize any audio frequency
better than your oscillator tolerance. I certainly generated DTMF to
better than
telco-standards this way 25 years ago.

You can scale this down to fit better; for example using a 256-entry
cosine table and a 24-bit accumulator clocked at 10KHz, you'd have a
frequency step of .59605 milliHz and the lookup would be:

outputsample = cosine_table[(phase_accum >> 16) & 0xff];
phase_accum += frequency_increment.

which probably compiles to something that avoids shifting.

But, hey, it's not my project :-)

Cheers -
Dana K6JQ

On Thu, Apr 26, 2012 at 11:46 PM, Matthias Weingart
wrote:

> **
> Dana,
>
> in case I understand Al's comment right :-) - he is doing it in a
> different
> way. The same lockup table is sent to the DAC with different sample rates.
> No value from the table is skipped, they are sent to the DAC with
> different
> speeds. e.g. for the first tone every 200 timer clocks, for the 2nd tone
> with every 201 timer clocks...
>
> M.
>
> Dana Myers :
> > More detail on DDS - you create a single sine-wave table (it's actually
> > a cosine)
> > and 'step' through it based on a constant sample rate. The frequency is
> > determined by the size of the step.
> >
> > For example, let's just assume we have a 1024-word table:
> >
> > int costab[1024] = { ... }; // initialize with pre-calculated single
> > wave of
> > // cos() * 32767 (-32767 .. 32767)
> >
> > If you use a sample rate of 10240 samples/sec, an increment of
> > 1 will produce a 10Hz sine wave, an increment of 10 will produce 100Hz,
> > etc.
> >
> > You maintain a "phase accumulator" for each desired output frequency.
> > The output is then a stream of samples at whatever precision your
> > cosine table is.
> >
> > You can use a large phase accumulator to provide better/finer
> > frequency granularity and intermediate bits of the accumulator
> > to effectively implement fractional increments between frequencies.
> >
> > You only need one periodic interrupt to drive the output ISR,
> >
> > Cheers,
> > Dana K6JQ
> >
> > On Thu, Apr 26, 2012 at 6:53 AM, Onestone
> > wrote:
> >
> >> **
>
> >>
> >>
> >> Hi Matthias, I still think you're misunderstanding. First of all two
> >> tones would never require the same CCR, secondly chords don't contain
> >> duplicates of the same note (at least in my experience), so 7 unique
> >> tones can be played simultaneously with 7 CCr registers. And those
> >> tones can vary in real time, ie register CCRB1 doesn't have to be C, at
> >> each note, a fresh set of unique notes are available. My code sample
> >> executes in 25 cycles plus interrupt latency of 1-6 cycles, so 31 worst
> >> case. In the case of a 16MHz clock 7 tones would require 217 clock
> >> cycles maximum to process in the isrs, plus whatever overhead is needed
> >> in the foreground routine, so there would still be obvious limits to
> >> the maximum access rate. However since the OP wants a range from
> >> 130.8Hz to 987.7Hz a worst case event would mean an upper limit of 74
> >> entries per table. however there is nothign to stop the user from
> >> having tables of different resolutions, or of varying the step size for
> >> different notes. ie a 256 entry table played at a fixed rate to give
> >> 100Hz would give a 200hz output if the step through the table was 2
> >> instead of 1. this is, of course, another way of getting a wide
> >> frequency range using a single clock source. In this case longer tables
> >> sound better than short ones. In the case of a single table with
> >> variable step sizes to create notes of different frequencies it would
> >> be possible to use a single timer, and simply add all the different
> >> values for each step interval in a single ISR, however the range of
> >> frequencies is more limited. If I remember my note frequencies there
> >> are a limited number of base frequencies, and then the harmonics of
> >> these, which the above method results in, form the remaining available
> >> notes, so a combination of methods could well work.
> >>
> >> Of course, having said all of this, if the OP only needs to play a few
> >> different note combinations, or a few short tunes then, given the huge
> >> amount of available memory why note simply pre-calculate them and store
> >> them in flash?
> >>
> >> Al
> >>
> >>
> >> On 26/04/2012 6:32 PM, Matthias Weingart wrote:
> >> > Onestone:
> >> >
> >> >> Hi Matthias. The idea is to use a single table, but probably a
> >> >> higher resolution table than 32 entries, and to still use the basic
> >> >> concept of timed access to generate different notes. It would be
> >> >> virtually impossible, I believe, to do this with a single timer, and
> >> >> limited jitter, how would you work out the next time interval for,
> >> >> say a 4 note chord? where the frequencies are not some convenient
> >> >> binary multiple?
> >> > Hi Al,
> >> >
> >> > It seems I understand your algorithm quite different then you. Each
> >> > note use it's own counter (e.g. the 7 CCR registers) for the sample
> >> > clock.
> >> >
> >> >> Then, where one note is a much higher frequency than another you
> >> >> have multiple instances of the higher note per instance of the lower
> >> >> note, so tracking order of tones with a single timer becomes cpu
> >> >> intensive.
> >> > That is true. You need to prepare the settings for the next tone in
> >> > the mainloop (CCR and position in the lookup table). One idea is a
> >> > sorted
> >> table
> >> > in RAM, sorted by next CCR. A special case has to be handled if 2 (or
> >> more)
> >> > tones requires the same CCR. Even with your method the jitter is
> >> > higher
> >> in
> >> > case 2 (or more) CCR-interrupts need to get served at the same
> >> > CCR-value. However my method is only suitable for very low
> >> > frequencies.
> >> >
> >> > M.
> >> >
> >> >> Certainly it would be far easier to use a single timer per tone.
> >> >> Also you'd need to take scaling into account. By using a timer per
> >> >> note you reduce the calculations (using a differential sine table)
> >> >> to DACOUT = DACOUT + TABLE(Nx) where N is the table entry, x is the
> >> >> tone, and TABLE is a table of signed differential sine values. The
> >> >> simplest form of this would use 1 register per note, so N1 =R4, N2 > >> >> R5 etc. Then the ISR becomes simply:-
> >> >>
> >> >> TB1_ISR:
> >> >> ADD&DACOUT,TABLE(R4)
> >> >> ADD&N1INTERVAL,&CCRB1
> >> >> BIC #CCIFG,&CCTLB1
> >> >> INCD R4
> >> >> ;ENTRIES ARE WORDS
> >> >> AND #0X00FE,R4 ;ASSUMES
> >> >> 256 ENTRY TABLE
> >> >> RETI
> >> >>
> >> >> To overcome scaling issues and the need for a little maths you could
> >> >> easily have tables for single tones, 2 note chords, up to 7 note
> >> >> chords
> >> >>
> >> >> Cheers
> >> >>
> >> >> Al
> >> >>
> >> >> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
> >> >>> Onestone,
> >> >>> your code is quite good (after some thinking I am now sure that it
> >> >>> is working:-). One improvement: should be possible to do it with
> >> >>> just one CCR- channel.
> >> >>>
> >> >>> M.
> >> >>>
> >> >>> Onestone:
> >> >>>
> >> >>>> being a simple kind of guy I think my method is just too easy! 8-)
> >> >>>>
> >> >>>> Al
> >> >>>>
> >> >>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
> >> >>>>> In case you are comfortable with 2 mixed sine waves just use both
> >> >>>>> DAC's, feed them independly and add them analog at the output pin
> >> >>>>> with 2 resistors.
> >> >>>>>
> >> >>>>> Another way: create a lookup table for your "chords".
> >> >>>>> In your current code you always use 32 steps for each sine wave -
> >> >>>>> for one wave a step is e.g. 300us for the 2nd wave it is 310us.
> >> >>>>> To mix them digitally you need finer steps.
> >> >>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine
> >> >>>>> waves into that RAM-table according to the frequencies you want.
> >> >>>>> Feed this RAM-table with the fastest clock (987Hz is about 1ms)
> >> >>>>> to the DAC. For example in a 256 table the 987Hz wave fits 7.55
> >> >>>>> times, the 130Hz waves only 1 time. For the 130Hz tone you have
> >> >>>>> to calculate 256 values from you 32 values of the lookup table.
> >> >>>>> This is called upsampling. You can use linear interpolation
> >> >>>>> (lines) to calculate the values between the 32 lookup values from
> >> >>>>> your Flash table (or drop the lookup table and use sin() from the
> >> >>>>> C compiler, no idea how fast this is). The big deal here is, that
> >> >>>>> in most cases the end of the RAM table does not fit to the begin
> >> >>>>> of the RAM table, the waves are not connected smoothly together.
> >> >>>>> You have to calculate a new RAM table once the first one has been
> >> >>>>> sent, so that the waves are smoothly connected. Use 2 RAM tables,
> >> >>>>> one for output (DMA), one for precalculation (in the main loop).
> >> >>>>>
> >> >>>>> You can do the interpolation on the fly in the CCR-Interrupt
> >> >>>>> routine, no extra RAM table, no DMA needed (however, the sound
> >> >>>>> will not be clear because of the jitter of the interrupt response
> >> >>>>> time).
> >> >>>>>
> >> >>>>> M.
> >> >>>>>
> >> >>>>> Onestone:
> >> >>>>>
> >> >>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd
> >> >>>>>> use 1 timer per tone, and track the last value for each tone.
> >> >>>>>> Then on timer interrupt:-
> >> >>>>>>
> >> >>>>>> DACx = DACx - oldsinevalue + newsinevalue.
> >> >>>>>>
> >> >>>>>> Effectively remove the old value for the tone from the DAC and
> >> >>>>>> add the new one at a variabe time interval. Also, given
> >> >>>>>> something with as much memory as the 2619 i'd not limit myself
> >> >>>>>> to a 32 entry table. In theory, and assuming that you aren't
> >> >>>>>> using the other timers, you could get 7 tones using timer B. I'd
> >> >>>>>> also go for the highest clock frequency possible, and in the ISR
> >> >>>>>> keep the code short and clean. for minimal jitter. Rather than
> >> >>>>>> absolute values your table could comprise differential values so
> >> >>>>>> that your calculation is simple DACx = DACx + sine(n).
> >> >>>>>>
> >> >>>>>> Hope this helps
> >> >>>>>>
> >> >>>>>> Al
> >> >>>>>>
> >> >>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
> >> >>>>>>> Hi all,
> >> >>>>>>> I am currently working on some code for an MSP430F2619. I am
> >> >>>>>>> using a 32-value sine lookup table, and pushing the sequential
> >> >>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
> >> >>>>>>> note frequency) controlled by TimerB. So far,this is working
> >> >>>>>>> well. I can produce any number of reasonable sounding pure
> >> >>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
> >> >>>>>>> and this is more than enough range for my needs, three full
> >> >>>>>>> octaves.
> >> >>>>>>>
> >> >>>>>>> However, this only plays one tone at a time. I have been
> >> >>>>>>> wracking my brains trying to figure out how to use this to
> >> >>>>>>> generate a "chord," more than one note played at the same time.
> >> >>>>>>> I know that musically this can be as simple as adding two sine
> >> >>>>>>> waves together, but since I don't have two simultaneously
> >> >>>>>>> existing sine waves, just one that I change the frequency of, I
> >> >>>>>>> am at a loss.
> >> >>>>>>>
> >> >>>>>>> Does anyone have any suggestions on how to achieve this, or
> >> >>>>>>> references that they think might push me in the right
> >> >>>>>>> direction?
> >> >>>>>>>
> >> >>>>>>> Thank you,
> >> >>>>>>> David
> >> >>>>>>>
> >> >>>
>


How about you have a sine table, but instead of setting your interrupt time based on a single frequency divided by the # of samples, you instead pick a fixed interrupt time. For example, it seems your fastest interrupt rate currently would be at (1/987.7 Hz)/32 = 31.6 us. So use that as your interrupt time all the time.

At each interrupt, you calculate for each sine wave where you'll be in that list of 32 samples, 0 - 31. If you're trying to create a sine wave at 400 Hz, your increment each interrupt would be (400 Hz/987.7 Hz). Calculate this using fixed-point #s, or course, such as 8p8. Since you have a limited number of tones, the increment can be precalculated for each one.

Now, you when you get an interrupt, for each frequency, you add to a position value by the increment for each frequency and take the corresponding value from the sine wave table. Since you're in between two values, you'll probably want to extrapolate between the two values in the table. Do this for each frequency and add them together.

Whether you can get this done in 31.6 us, I don't know, but it seems like a clean way of getting tones, and is scalable up until you run out of CPU cycles.

--- In m..., Onestone wrote:
>
> Hi Matthias, I still think you're misunderstanding. First of all two
> tones would never require the same CCR, secondly chords don't contain
> duplicates of the same note (at least in my experience), so 7 unique
> tones can be played simultaneously with 7 CCr registers. And those tones
> can vary in real time, ie register CCRB1 doesn't have to be C, at each
> note, a fresh set of unique notes are available. My code sample
> executes in 25 cycles plus interrupt latency of 1-6 cycles, so 31 worst
> case. In the case of a 16MHz clock 7 tones would require 217 clock
> cycles maximum to process in the isrs, plus whatever overhead is needed
> in the foreground routine, so there would still be obvious limits to the
> maximum access rate. However since the OP wants a range from 130.8Hz to
> 987.7Hz a worst case event would mean an upper limit of 74 entries per
> table. however there is nothign to stop the user from having tables of
> different resolutions, or of varying the step size for different notes.
> ie a 256 entry table played at a fixed rate to give 100Hz would give a
> 200hz output if the step through the table was 2 instead of 1. this is,
> of course, another way of getting a wide frequency range using a single
> clock source. In this case longer tables sound better than short ones.
> In the case of a single table with variable step sizes to create notes
> of different frequencies it would be possible to use a single timer, and
> simply add all the different values for each step interval in a single
> ISR, however the range of frequencies is more limited. If I remember my
> note frequencies there are a limited number of base frequencies, and
> then the harmonics of these, which the above method results in, form the
> remaining available notes, so a combination of methods could well work.
>
> Of course, having said all of this, if the OP only needs to play a few
> different note combinations, or a few short tunes then, given the huge
> amount of available memory why note simply pre-calculate them and store
> them in flash?
>
> Al
> On 26/04/2012 6:32 PM, Matthias Weingart wrote:
> > Onestone:
> >
> >> Hi Matthias. The idea is to use a single table, but probably a higher
> >> resolution table than 32 entries, and to still use the basic concept of
> >> timed access to generate different notes. It would be virtually
> >> impossible, I believe, to do this with a single timer, and limited
> >> jitter, how would you work out the next time interval for, say a 4 note
> >> chord? where the frequencies are not some convenient binary multiple?
> > Hi Al,
> >
> > It seems I understand your algorithm quite different then you. Each note
> > use it's own counter (e.g. the 7 CCR registers) for the sample clock.
> >
> >> Then, where one note is a much higher frequency than another you have
> >> multiple instances of the higher note per instance of the lower note, so
> >> tracking order of tones with a single timer becomes cpu intensive.
> > That is true. You need to prepare the settings for the next tone in the
> > mainloop (CCR and position in the lookup table). One idea is a sorted table
> > in RAM, sorted by next CCR. A special case has to be handled if 2 (or more)
> > tones requires the same CCR. Even with your method the jitter is higher in
> > case 2 (or more) CCR-interrupts need to get served at the same CCR-value.
> > However my method is only suitable for very low frequencies.
> >
> > M.
> >
> >> Certainly it would be far easier to use a single timer per tone. Also
> >> you'd need to take scaling into account. By using a timer per note you
> >> reduce the calculations (using a differential sine table) to DACOUT > >> DACOUT + TABLE(Nx) where N is the table entry, x is the tone, and TABLE
> >> is a table of signed differential sine values. The simplest form of this
> >> would use 1 register per note, so N1 =R4, N2 = R5 etc. Then the ISR
> >> becomes simply:-
> >>
> >> TB1_ISR:
> >> ADD&DACOUT,TABLE(R4)
> >> ADD&N1INTERVAL,&CCRB1
> >> BIC #CCIFG,&CCTLB1
> >> INCD R4
> >> ;ENTRIES ARE WORDS
> >> AND #0X00FE,R4 ;ASSUMES
> >> 256 ENTRY TABLE
> >> RETI
> >>
> >> To overcome scaling issues and the need for a little maths you could
> >> easily have tables for single tones, 2 note chords, up to 7 note chords
> >>
> >> Cheers
> >>
> >> Al
> >>
> >> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
> >>> Onestone,
> >>> your code is quite good (after some thinking I am now sure that it is
> >>> working:-). One improvement: should be possible to do it with just one
> >>> CCR- channel.
> >>>
> >>> M.
> >>>
> >>> Onestone:
> >>>
> >>>> being a simple kind of guy I think my method is just too easy! 8-)
> >>>>
> >>>> Al
> >>>>
> >>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
> >>>>> In case you are comfortable with 2 mixed sine waves just use both
> >>>>> DAC's, feed them independly and add them analog at the output pin
> >>>>> with 2 resistors.
> >>>>>
> >>>>> Another way: create a lookup table for your "chords".
> >>>>> In your current code you always use 32 steps for each sine wave - for
> >>>>> one wave a step is e.g. 300us for the 2nd wave it is 310us. To mix
> >>>>> them digitally you need finer steps.
> >>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine waves
> >>>>> into that RAM-table according to the frequencies you want. Feed this
> >>>>> RAM-table with the fastest clock (987Hz is about 1ms) to the DAC. For
> >>>>> example in a 256 table the 987Hz wave fits 7.55 times, the 130Hz
> >>>>> waves only 1 time. For the 130Hz tone you have to calculate 256
> >>>>> values from you 32 values of the lookup table. This is called
> >>>>> upsampling. You can use linear interpolation (lines) to calculate the
> >>>>> values between the 32 lookup values from your Flash table (or drop
> >>>>> the lookup table and use sin() from the C compiler, no idea how fast
> >>>>> this is). The big deal here is, that in most cases the end of the RAM
> >>>>> table does not fit to the begin of the RAM table, the waves are not
> >>>>> connected smoothly together. You have to calculate a new RAM table
> >>>>> once the first one has been sent, so that the waves are smoothly
> >>>>> connected. Use 2 RAM tables, one for output (DMA), one for
> >>>>> precalculation (in the main loop).
> >>>>>
> >>>>> You can do the interpolation on the fly in the CCR-Interrupt routine,
> >>>>> no extra RAM table, no DMA needed (however, the sound will not be
> >>>>> clear because of the jitter of the interrupt response time).
> >>>>>
> >>>>> M.
> >>>>>
> >>>>> Onestone:
> >>>>>
> >>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd use
> >>>>>> 1 timer per tone, and track the last value for each tone. Then on
> >>>>>> timer interrupt:-
> >>>>>>
> >>>>>> DACx = DACx - oldsinevalue + newsinevalue.
> >>>>>>
> >>>>>> Effectively remove the old value for the tone from the DAC and add
> >>>>>> the new one at a variabe time interval. Also, given something with
> >>>>>> as much memory as the 2619 i'd not limit myself to a 32 entry table.
> >>>>>> In theory, and assuming that you aren't using the other timers, you
> >>>>>> could get 7 tones using timer B. I'd also go for the highest clock
> >>>>>> frequency possible, and in the ISR keep the code short and clean.
> >>>>>> for minimal jitter. Rather than absolute values your table could
> >>>>>> comprise differential values so that your calculation is simple DACx
> >>>>>> = DACx + sine(n).
> >>>>>>
> >>>>>> Hope this helps
> >>>>>>
> >>>>>> Al
> >>>>>>
> >>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
> >>>>>>> Hi all,
> >>>>>>> I am currently working on some code for an MSP430F2619. I am
> >>>>>>> using a 32-value sine lookup table, and pushing the sequential
> >>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
> >>>>>>> note frequency) controlled by TimerB. So far,this is working
> >>>>>>> well. I can produce any number of reasonable sounding pure
> >>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
> >>>>>>> and this is more than enough range for my needs, three full
> >>>>>>> octaves.
> >>>>>>>
> >>>>>>> However, this only plays one tone at a time. I have been wracking
> >>>>>>> my brains trying to figure out how to use this to generate a
> >>>>>>> "chord," more than one note played at the same time. I know that
> >>>>>>> musically this can be as simple as adding two sine waves together,
> >>>>>>> but since I don't have two simultaneously existing sine waves, just
> >>>>>>> one that I change the frequency of, I am at a loss.
> >>>>>>>
> >>>>>>> Does anyone have any suggestions on how to achieve this, or
> >>>>>>> references that they think might push me in the right direction?
> >>>>>>>
> >>>>>>> Thank you,
> >>>>>>> David
> >>>>>>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
So many ways to skin that cat!

Al

On 28/04/2012 12:20 AM, Robert wrote:
> How about you have a sine table, but instead of setting your interrupt time based on a single frequency divided by the # of samples, you instead pick a fixed interrupt time. For example, it seems your fastest interrupt rate currently would be at (1/987.7 Hz)/32 = 31.6 us. So use that as your interrupt time all the time.
>
> At each interrupt, you calculate for each sine wave where you'll be in that list of 32 samples, 0 - 31. If you're trying to create a sine wave at 400 Hz, your increment each interrupt would be (400 Hz/987.7 Hz). Calculate this using fixed-point #s, or course, such as 8p8. Since you have a limited number of tones, the increment can be precalculated for each one.
>
> Now, you when you get an interrupt, for each frequency, you add to a position value by the increment for each frequency and take the corresponding value from the sine wave table. Since you're in between two values, you'll probably want to extrapolate between the two values in the table. Do this for each frequency and add them together.
>
> Whether you can get this done in 31.6 us, I don't know, but it seems like a clean way of getting tones, and is scalable up until you run out of CPU cycles.
>
> --- In m..., Onestone wrote:
>> Hi Matthias, I still think you're misunderstanding. First of all two
>> tones would never require the same CCR, secondly chords don't contain
>> duplicates of the same note (at least in my experience), so 7 unique
>> tones can be played simultaneously with 7 CCr registers. And those tones
>> can vary in real time, ie register CCRB1 doesn't have to be C, at each
>> note, a fresh set of unique notes are available. My code sample
>> executes in 25 cycles plus interrupt latency of 1-6 cycles, so 31 worst
>> case. In the case of a 16MHz clock 7 tones would require 217 clock
>> cycles maximum to process in the isrs, plus whatever overhead is needed
>> in the foreground routine, so there would still be obvious limits to the
>> maximum access rate. However since the OP wants a range from 130.8Hz to
>> 987.7Hz a worst case event would mean an upper limit of 74 entries per
>> table. however there is nothign to stop the user from having tables of
>> different resolutions, or of varying the step size for different notes.
>> ie a 256 entry table played at a fixed rate to give 100Hz would give a
>> 200hz output if the step through the table was 2 instead of 1. this is,
>> of course, another way of getting a wide frequency range using a single
>> clock source. In this case longer tables sound better than short ones.
>> In the case of a single table with variable step sizes to create notes
>> of different frequencies it would be possible to use a single timer, and
>> simply add all the different values for each step interval in a single
>> ISR, however the range of frequencies is more limited. If I remember my
>> note frequencies there are a limited number of base frequencies, and
>> then the harmonics of these, which the above method results in, form the
>> remaining available notes, so a combination of methods could well work.
>>
>> Of course, having said all of this, if the OP only needs to play a few
>> different note combinations, or a few short tunes then, given the huge
>> amount of available memory why note simply pre-calculate them and store
>> them in flash?
>>
>> Al
>> On 26/04/2012 6:32 PM, Matthias Weingart wrote:
>>> Onestone:
>>>
>>>> Hi Matthias. The idea is to use a single table, but probably a higher
>>>> resolution table than 32 entries, and to still use the basic concept of
>>>> timed access to generate different notes. It would be virtually
>>>> impossible, I believe, to do this with a single timer, and limited
>>>> jitter, how would you work out the next time interval for, say a 4 note
>>>> chord? where the frequencies are not some convenient binary multiple?
>>> Hi Al,
>>>
>>> It seems I understand your algorithm quite different then you. Each note
>>> use it's own counter (e.g. the 7 CCR registers) for the sample clock.
>>>
>>>> Then, where one note is a much higher frequency than another you have
>>>> multiple instances of the higher note per instance of the lower note, so
>>>> tracking order of tones with a single timer becomes cpu intensive.
>>> That is true. You need to prepare the settings for the next tone in the
>>> mainloop (CCR and position in the lookup table). One idea is a sorted table
>>> in RAM, sorted by next CCR. A special case has to be handled if 2 (or more)
>>> tones requires the same CCR. Even with your method the jitter is higher in
>>> case 2 (or more) CCR-interrupts need to get served at the same CCR-value.
>>> However my method is only suitable for very low frequencies.
>>>
>>> M.
>>>
>>>> Certainly it would be far easier to use a single timer per tone. Also
>>>> you'd need to take scaling into account. By using a timer per note you
>>>> reduce the calculations (using a differential sine table) to DACOUT >>>> DACOUT + TABLE(Nx) where N is the table entry, x is the tone, and TABLE
>>>> is a table of signed differential sine values. The simplest form of this
>>>> would use 1 register per note, so N1 =R4, N2 = R5 etc. Then the ISR
>>>> becomes simply:-
>>>>
>>>> TB1_ISR:
>>>> ADD&DACOUT,TABLE(R4)
>>>> ADD&N1INTERVAL,&CCRB1
>>>> BIC #CCIFG,&CCTLB1
>>>> INCD R4
>>>> ;ENTRIES ARE WORDS
>>>> AND #0X00FE,R4 ;ASSUMES
>>>> 256 ENTRY TABLE
>>>> RETI
>>>>
>>>> To overcome scaling issues and the need for a little maths you could
>>>> easily have tables for single tones, 2 note chords, up to 7 note chords
>>>>
>>>> Cheers
>>>>
>>>> Al
>>>>
>>>> On 26/04/2012 4:14 PM, Matthias Weingart wrote:
>>>>> Onestone,
>>>>> your code is quite good (after some thinking I am now sure that it is
>>>>> working:-). One improvement: should be possible to do it with just one
>>>>> CCR- channel.
>>>>>
>>>>> M.
>>>>>
>>>>> Onestone:
>>>>>
>>>>>> being a simple kind of guy I think my method is just too easy! 8-)
>>>>>>
>>>>>> Al
>>>>>>
>>>>>> On 25/04/2012 6:11 PM, Matthias Weingart wrote:
>>>>>>> In case you are comfortable with 2 mixed sine waves just use both
>>>>>>> DAC's, feed them independly and add them analog at the output pin
>>>>>>> with 2 resistors.
>>>>>>>
>>>>>>> Another way: create a lookup table for your "chords".
>>>>>>> In your current code you always use 32 steps for each sine wave - for
>>>>>>> one wave a step is e.g. 300us for the 2nd wave it is 310us. To mix
>>>>>>> them digitally you need finer steps.
>>>>>>> Create a table in RAM, and upsample (stretch) your 32 step-sine waves
>>>>>>> into that RAM-table according to the frequencies you want. Feed this
>>>>>>> RAM-table with the fastest clock (987Hz is about 1ms) to the DAC. For
>>>>>>> example in a 256 table the 987Hz wave fits 7.55 times, the 130Hz
>>>>>>> waves only 1 time. For the 130Hz tone you have to calculate 256
>>>>>>> values from you 32 values of the lookup table. This is called
>>>>>>> upsampling. You can use linear interpolation (lines) to calculate the
>>>>>>> values between the 32 lookup values from your Flash table (or drop
>>>>>>> the lookup table and use sin() from the C compiler, no idea how fast
>>>>>>> this is). The big deal here is, that in most cases the end of the RAM
>>>>>>> table does not fit to the begin of the RAM table, the waves are not
>>>>>>> connected smoothly together. You have to calculate a new RAM table
>>>>>>> once the first one has been sent, so that the waves are smoothly
>>>>>>> connected. Use 2 RAM tables, one for output (DMA), one for
>>>>>>> precalculation (in the main loop).
>>>>>>>
>>>>>>> You can do the interpolation on the fly in the CCR-Interrupt routine,
>>>>>>> no extra RAM table, no DMA needed (however, the sound will not be
>>>>>>> clear because of the jitter of the interrupt response time).
>>>>>>>
>>>>>>> M.
>>>>>>>
>>>>>>> Onestone:
>>>>>>>
>>>>>>>> i DON'T SEE HOW YOU COULD DO THIS WITH DMA. As a first try I'd use
>>>>>>>> 1 timer per tone, and track the last value for each tone. Then on
>>>>>>>> timer interrupt:-
>>>>>>>>
>>>>>>>> DACx = DACx - oldsinevalue + newsinevalue.
>>>>>>>>
>>>>>>>> Effectively remove the old value for the tone from the DAC and add
>>>>>>>> the new one at a variabe time interval. Also, given something with
>>>>>>>> as much memory as the 2619 i'd not limit myself to a 32 entry table.
>>>>>>>> In theory, and assuming that you aren't using the other timers, you
>>>>>>>> could get 7 tones using timer B. I'd also go for the highest clock
>>>>>>>> frequency possible, and in the ISR keep the code short and clean.
>>>>>>>> for minimal jitter. Rather than absolute values your table could
>>>>>>>> comprise differential values so that your calculation is simple DACx
>>>>>>>> = DACx + sine(n).
>>>>>>>>
>>>>>>>> Hope this helps
>>>>>>>>
>>>>>>>> Al
>>>>>>>>
>>>>>>>> On 25/04/2012 7:05 AM, zylaxice wrote:
>>>>>>>>> Hi all,
>>>>>>>>> I am currently working on some code for an MSP430F2619. I am
>>>>>>>>> using a 32-value sine lookup table, and pushing the sequential
>>>>>>>>> values out to the DAC12 via DMA, with the speed (and thus the
>>>>>>>>> note frequency) controlled by TimerB. So far,this is working
>>>>>>>>> well. I can produce any number of reasonable sounding pure
>>>>>>>>> tones/notes at frequencies from 130.8Hz (C3) to 987.7Hz (B5),
>>>>>>>>> and this is more than enough range for my needs, three full
>>>>>>>>> octaves.
>>>>>>>>>
>>>>>>>>> However, this only plays one tone at a time. I have been wracking
>>>>>>>>> my brains trying to figure out how to use this to generate a
>>>>>>>>> "chord," more than one note played at the same time. I know that
>>>>>>>>> musically this can be as simple as adding two sine waves together,
>>>>>>>>> but since I don't have two simultaneously existing sine waves, just
>>>>>>>>> one that I change the frequency of, I am at a loss.
>>>>>>>>>
>>>>>>>>> Does anyone have any suggestions on how to achieve this, or
>>>>>>>>> references that they think might push me in the right direction?
>>>>>>>>>
>>>>>>>>> Thank you,
>>>>>>>>> David
>>>>>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>