EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

Multiple Sine Waves/Chords from a DAC

Started by zylaxice April 24, 2012
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

Maybe create a dynamic 32 value table that you use to generate the output and load that table on the fly whenever you change tones/chords by ether taking values from your existing lookup tables for pure tones, or the combination (addition of multiple pure tone table entries?) for a chord?
----- Original Message -----
From: zylaxice
To: m...
Sent: Tuesday, April 24, 2012 14:35
Subject: [msp430] Multiple Sine Waves/Chords from a DAC

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 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
>
>
On 04/24/2012 04:35 PM, 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
>

The obvious way, although you can't use the DMAC for it, is the Direct
Digital Synthesis (DDS) method of using a phase register.

You have one phase register of say 16 bits per tone. At each timer tick
you add a number to the phase register. The magnitude is different for
each tone and determines the period. Then use the upper 5 bits of each
phase register to index into the sine table, add the results together,
and send it to the DAC.

--
David W. Schultz
http://home.earthlink.net/~david.schultz
"Who? What? Where? When? Aahhhg!" - Duck Dodgers
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
>>

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
>>>
>
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
>>>>

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?
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.
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
>>>>>
>
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 new to this discussion, but the way I've done this before
is to use a single cosine table and direct-digital synthesis
to produce multiple sine waves at the same time. I "mix"
them simply by adding (taking care to avoid overflow).

Only a single repetitive timer is required - I usually do it under
interrupt. I minimuze jitter (though it's not really a problem anyway),
each cycle of the ISR pre-calculates the next output sample after
first outputting the previously-calculated sample.

You'll generate a sequence of digital samples, which then are
fed to a DAC/filter. I've used PWM output as a 1-bit DAC
into a single stage low-pass filter, for example.

Dana K6JQ
On Thu, Apr 26, 2012 at 2:02 AM, 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
> >>>>>>
> >>
> >>
> >>
> >>
> >>
> >>
> >>

The 2024 Embedded Online Conference