-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding more voices #2
Comments
You can add more voices but we have found that the best compromise between
|
Last week I've tried to make a 8-voices version of the synth. It's seems to work great, but I haven't try it with anything else than a very little program, and no other things running (i.e. graphical interface, buttons/pots reanding and processing, etc.) |
The number of voices are limited by processing power vs. sampling rate. Mega has the same processing power as UNO and DUE is a different hardware architecture. I am currently working on a hardware abstracted version of the synth that will run on different platforms. |
I think 4 voices/oscillators they are enough for these kind of audio engines. I've been looking around about what I want do with this great libray and I have few issues I would like to talk about. First at all, I couldn't find a technological explanation about why sample rate is set to 20kHz. Isn't possible to get higher sample rate just because Atmel limitations? Or its because with an higher sample rate the microprocessor is too much busy in generating pwm that it doesn't let any more coding? Another doubt I have (because I'm a noob and autodidact in diy) it's about wavetables and related PWM output. Wavetables have values in between -128 and 127 (8 bit range), as PWM digital output. And I have seen in the library's code the zero point of the wave is 127. So -127 in the wavetable is 0 in the PWM output. so, ground pin of the output jack should be connected to some voltage corresponding to 127? 2.5 V? So, I would like to speak about few improvement I don't know if they are possible to get (I just wantto know if they are technologically possible):
Mainly I can understand how the libray works, my main problem it's about output value calculation, because I can't really understand those lines of code :-) Last. How can I get the output value? I want it to drive a led in relátion with the output. Alberto |
Hi Alberto, I won't be able to answer all your question, but I can tell about what I've done. I think the 20kHz is a right choice between what you can output that will be earable with a simple DC filter, and the arduino limitations. At 20kHz you have 800 ticks per timer interrupt. These 800 ticks are the room left for everything that has to be computed: your program, the ISR routine, etc. I don't know exactly how much it takes, but I think every operation in your program takes at least one tick to compute. Digital filters are probably feasible. But I wouldn't be able to do it. LFO are probably easy to implement. In the formula that creates the PWM output there is the amplitude parameters that already do it. (AMP[0], AMP[1], etc.) This progressively decreases the output level by a ratio that is taken from the envelope table. One can imagine a second ratio that can be taken from one of the waveform table, but with a low frequency. ADSR can be made too. I wanted to create something similar, but I stopped working on my project as suddenly as I started to. The idea would be to replace the value from the enveloppe table by values from dedicated tables, or values generated on the fly. You've got the EPCW table that stores an accumulator that is used to know what value to take in the enveloppe table, given the length the sound must have. The sound is started by setting the EPCW to 0, and stopped when it's more than 0x8000. This EPCW value could be tested against ADSR limits. For example between 0 and 1000 you use an attack table, then from 1000 to 3000 a decay table, and so on. As the memory space is limited, tables could be the same as those used for enveloppes and waveforms. Output computation deals with pointers. This is clearly a hard tool to get, but when you understand it it changes life! ;) There are some greats readings about it around the Internet, you'll maybe need to read several ones before to get it. I understood it with this one: http://www.cplusplus.com/doc/tutorial/pointers/ The easiest way to drive your led would simply be to connect it on the OCR2A or OCR2B pin, as these are updated with the output value. Or, you can write the value to a var instead of the pin in SIR routine. Or you can also read the OCR2A/B value when needed. Pierre-Loup. |
Wow! Pierre! I really appreciate your complete answer. It makes me undestanding a lot of useful stuff. Sure lfo and adsr should be quite easy to get, filter sure I'm not able. I'll tweak a little bit. Thanks for suggesting me to use OCRA/B values to drive a led. This explanation open another doubt: do OCRA/B pins occupy by timer interrupts when using them or can be used for other purposes? I understand pointers, but I can't get how to optimize code using them. Anyway thank you very much, keep in touch in git hub Albertl |
Hi Alberto, You should have a look at the Atmel documentation for atmega328p. You can use timers and keep the OCRxA/B pins free for another use, but if you want to output PWM the pins will be tied to the timer. So, in our case, you can use the timer2 and output it on its two pwm pins (that is what The Synth does), or leave the pins behave as input/output. In that case I don't know how you can get the output values. I've written a mistake yesterday about the value you can get by reading OCR2A/B: the value you get is the pwm ratio (i.e. a value between 0 and 255), not the 0 or 1 that is readable on the pin. |
Hi Pierre
|
Hi Mirco, About external DAC, yes I think you can use it. You will have to modify the main ISR to send the updated value to SPI instead of PWM. Maybe you will have to use direct register access to save time on the Arduino SPI library. I've never used it, so I cannot tell. I don't know exactly what's the goal with your project, but maybe you could use a DAC that is more audio-oriented than the which you plan to use. This one will output a signal centered on a positive tension, so waving between, say, 0 and 5V. An audio DAC will have charge pumps to have a "real" audio waveform, waving around 0v. Pimoroni make a great board with such a DAC, but sadly it's driven by I2S, so is not usable with Atmega. I plan to port the library to the Arduin Due, one day... :) Hope it helps. Pierre-Loup M. |
Hi Pierre, thanks for response, so about question 1 i means to play 4 polyphinic notes on same midi channel, so i play with a keyboard i need to listen a chord , in this moment i don't undenstan ho to manage your 4 trigger with 4 key play on my keyboard (i undenstand that trigger have a different channel each for each notes and it's different of midi channel), about DAC i try to modify the code in sunth.h but unfortunally dac out is mute (writeDac function is working in other code). In attach synth.h modified... have you any idea ? |
First, I have to clarify something: I didn't wrote this library, but I've used it a lot for a project of mine, and bring some modifications to it, that you can see in my repository. So I suppose your keyboard is sending MIDI notes on a MIDI channel, say channel 1, and that you want your Arduino running the synth to dispatch your four notes each on different synth channel. One way to do it is to keep track of the last channel you've used by using a counter. When you receive your first note, you play it on first channel (numbered 0), and increment your counter. Then you receive another note, and play it on the next channel. When the counter overlaps the number of channels, then you set it back to 0. In fact each time you receive a note from your MIDI interface, then you play it on the channel number given by the counter. For your DAC problem, I won't be able to help you without viewing code. Only things I can say is basic advice, like: Have you set the pins direction right? Isn't there a kind of interrupt masking (Timer interrupt have greater priority than SPI interrupts)? Have you used volatiles variables for access in ISR? Pierre-Loup. |
I've just seen you attached code to your previous message. I'm looking at it. |
I can see two things in your code that could be a problem.
|
Great, this evening i try your suggest about midi and DAC, i will let know if works fine. |
I check the code and is correct , i just try with one channel, but don't work, then the the highbyte 0b0000xxxx is also correct last 4 bit are used for outA setting, low byte (8) and first 4 byte of highbyte (togher 8+4=12bit) are used for dac resolution, yesterdasy i just try it but dac didn't play, furthermore i try to uncomment OCR2A but didn't play ... do you think that writedac function is in a right place of code ? any other idea ? Below the info about dac (this have 2 out) 0b01x1000000000000 where X is GAIN selector. 0b011X000000000000 where X SHUTDOWN. 0b0111XXXXXXXXXXXX where X is the 12 bits to be written to the active channel. |
I've read the datasheet this morning before to answer. I know the writeDac function is correct, except what I was pointing out. Your DAC is 12 bits, but as the output computed in the ISR is 8 bits, you have to send it as if it were an 8 bit DAC: I don't know if there is a best place for writing writeDac(). I would have put it in the main program, so as the SPI initialisation. You maybe don't need to make this improvements, but time can be critical when generating waveforms.. |
Great, so i try to move SPI inizialization and writeDac in main function, about 12bit for DAC value have you a suggestion ? and about port Manipulation maybe is better to user PORTD command ? if yes what is the better way to maniputale CS pin ? after that i think that i can user SPI.transfer16 . Thanks Regards |
Bit shifting? Have you tried it? Does it work? I cannot say more. If I had lying around a MCP4822 I would have try it.
I suppose you mean using direct register access, so yes: you will gain a lot of time in your ISR (setting a bit in a port, or several at once, is one clock cycle. digitalWrite() can be much, much more). You can manipulate them with the same macros you used to set and clear bits in your function. Looking at the SPI library in the Arduino source code, I'm finally not that sure you will gain from using transfer16() instead of calling transfer() twice... Give it a try, and see! |
Good News, DAC now is working, now i must to add more DAC one for channel, i must to comment the line related OCR2A=OCR2B because if this uncomment is 2 octave down, frequency is a little wrong, if i play C on keyboard, dac play D, now last question, whitch the method to silent a note ? if i put len to 127 note play continally, i would like to silent when key is released, but i don't unenstand ho to do that. |
Good new! How did you solve that? I don't know why your note is two octaves down. I have never verified it on the instruments I made with this library... There must be somewhere something that devides the frequencyby four, or looking for a right bitshift of two (>>2) (but I don't think the one in the ISR is for something in your problem: this one is for diminishing the max aplitude of a channel, so that adding four channels don't cause the final wave value to go beyond limits and cause noise) You can fine tune pitch my modifying the ISR count. In the defines at top of file you have FS, that gives the theorical sampling frequency. This value is used in the begin() function to get a precise value of OCR1A that will give this frequency. By adding or substracting to this value you will tune your real output frequency. The easiest way is to simply add the tunning value on this line, like You can stop a channel before it reach its end by setting its EPCW[channel] value to 0x8000. EPCW is the enveloppe phase accumulator, that's to say it keeps track of the position of the enveloppe table it must read to set the amplitude of the sound. It's set to 0 when the channel is triggered, so the amplitude is at its max (start of the enveloppe), and go crescent until it reaches the end of the enveloppe table, where the amplitde is minimal. And when it reaches 0x8000, the ISR stops incrementing it, and set the amplitude to 0 for this channel (which is sthen silent). I would be curious to see what you are building, if you have a link, video, or something else I'll visit it with pleasure! |
Hi Pierre Regards |
Hi Mirco, |
Hi Pierre Thanks , Regards |
Hi Pierre Thanks |
Hi Pierre |
Hello Mircos, Given that port manipulation has solved the problem of driving the two channels of one DAC, I think the new problem is quite obvious. Supposing you've correctly set the second DAC (with its two channels). Aside note: I don't know how is your global sketch, but as we need the update to be ok on each sound sample, we have to be as efficient as possible. It starts by avoiding use of delay() functions, but maybe track use of digitalWrite and replace it by port manipulation (or use fast sigitalWrite library or similar). You can also check that yu conditionnal structures are organised in an efficient way (e.g. if you have five choices in an if..else structure, you better place first the most common case), and so on. |
Hi Pierre |
Great Pierre, with sample rate to 10000 or 15000 all DAC work fine, now i must to put 3rd DAC and try it, if i undenstud correctly, lower rate means taht i can go to last 1 or 2 octave and quality is same, if difference is this for me is not a problem 7/8 octaves are good. |
With another sample rate, the problem is that you will loose the ability to play high frequencies. The sample rate must be at least double the frequency you want to play. That's the reason why audio CD are sampled at 44100, it's a bit more than double 20000Hz, the max frequency audible for humans. There is one thing you will have to do if you change the sample rate: defining new EFTWS and PITCH in tables.h. You just have to make a prorata, i.e. take the 20000Hz and multiply each value by 2. See the difference between 20000Hz and 16000Hz tables. That way you'll keep the same pitch as before, a do being a do. |
Hi Pierre, great for all info, now i can play 6 notes, in effect all notes are detune and i must to correct the tables, have a right mathematic formulas about that ? now i use a bit rate set to 14000. |
The formula is quite simple: values in both tables I mentionend are directly linked to sample rate (and to other paramaters, but they don't matter for what we want tot do). So you just have to take each value of the table, multiply them by their sample rate, and divide them by the new sample rate you wanna use. |
Great Perre |
You will have to add two values for each arrays that store voices parameters. Look at the variables declaration at the beginning of synth.h |
yes i performed that but it seems didn't work .... i try to attach the synth.h |
On some tables you have added two values, but not set them. That's not a big deal, but it's better to initialize everything. |
Well, i changed the vaule of divider (set to 6) , about if instructions is correctly this code? or i must to change some other parameters ? if(!(divider&=0x05)) Thanks |
No, it won't work that way. That would be too simple!! So, you can set 8 voices, two of them not being used, and use the same compare + loop back to 0 by using 0x07 for compare value. |
Bonjour mes amis français, Im trying to do the same with STM32F103 plus populairement connu sous le nom blue pill.S'il vous plaît.. Je ne sais vraiment pas quoi faire.aidez moi |
Désolé.Je ne suis pas vraiment bon en anglais |
Is it theoretically possible (i.e. possible with the Arduino's processor) to add more voices to the output?
The text was updated successfully, but these errors were encountered: