C Code problems

I have a problem with a code implementation for SDCC and the C wrapper.

The thing I want to do is a “wind wheel” for Traktor with an encoder. The wind funktion in Traktor is working in the following manner:

* When deck is paused, and aMIDI input of 40Hex (64= 128/2 = half range) there is no wind.

* From 64 down to 0 the deck winds faster and faster in reverse.

* From 64 up to 127 the deck winds faster and faster in forward direction.

So I want my “Wind encoder” to work as follow:

* When I turn it slow clockwise I want to send 64 + “little deviation” i.e 65 or 66.

* The faster I turn the encoder the bigger values are outputet, up to 127.

* Same thing when turning counterclockwise, but from 64 downto 0..

Thougt solution:

* Use a timer to increase a variable every time the timer function is called.

* Then when the ENC_NotifyChange function is called I check the counter variable how many ticks it has gone since the last call of ENC_NotifyChange.

* Then calculate the deviation from 64 and send it to Traktor.

* Finally set the counter variable to zero.

Here is the code :

/*
 * C example
 *
 * ==========================================================================
 *
 * Copyright (C) 2004  Thorsten Klose (tk@midibox.org)
 *
 * ==========================================================================
 *
 * This file is part of a MIOS application
 *
 * This application is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with This application; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * ==========================================================================
 */

#include "cmios.h"

//#include <pic18f452.h>

unsigned int deviation;
unsigned int value;
unsigned int timer_counter;
unsigned int counts;


/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS after startup to initialize the
// application
/////////////////////////////////////////////////////////////////////////////
void Init(void)
{
   timer_counter = 0;

   // If 1 pulse/sec is minimum resolution, timer_counter should have a
   // frequency of 64 Hz = 15,625ms
   // calculate the required number of clocks for this period:
   // clocks = period / 400 nS = 15,625 mS / 400 nS = 39062

       MIOS_TIMER_Init(4,39062);

   // define number of shift registers
   MIOS_SRIO_NumberSet(16);

   MIOS_AIN_Muxed();
   MIOS_AIN_NumberSet(8);
   MIOS_AIN_DeadbandSet(8);

   // update frequency of SRIO chain
   MIOS_SRIO_UpdateFrqSet(1); // ms
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS in the mainloop when nothing else is to do
/////////////////////////////////////////////////////////////////////////////
void Tick(void)
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is periodically called by MIOS. The frequency has to be
// initialized with MIOS_Timer_Set
/////////////////////////////////////////////////////////////////////////////
void Timer(void)
{
      timer_counter++;
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when the display content should be
// initialized. Thats the case during startup and after a temporary message
// has been printed on the screen
/////////////////////////////////////////////////////////////////////////////
void DISPLAY_Init(void)
{
   // clear screen
   MIOS_LCD_Clear();
}

/////////////////////////////////////////////////////////////////////////////
//  This function is called in the mainloop when no temporary message is shown
//  on screen. Print the realtime messages here
/////////////////////////////////////////////////////////////////////////////
void DISPLAY_Tick(void)
{

}

/////////////////////////////////////////////////////////////////////////////
//  This function is called by MIOS when a complete MIDI event has been received
/////////////////////////////////////////////////////////////////////////////
void MPROC_NotifyReceivedEvnt(unsigned char evnt0, unsigned char evnt1, unsigned char evnt2)
{
   // print received MIDI event
   MIOS_LCD_Clear();

   MIOS_LCD_PrintCString("Received:");

   MIOS_LCD_CursorSet(0x40);
   MIOS_LCD_PrintHex2(evnt0);
   MIOS_LCD_PrintHex2(evnt1);
   MIOS_LCD_PrintHex2(evnt2);
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when a MIDI event has been received
// which has been specified in the MIOS_MPROC_EVENT_TABLE
/////////////////////////////////////////////////////////////////////////////
void MPROC_NotifyFoundEvent(unsigned entry, unsigned char evnt0, unsigned char evnt1, unsigned char envt2)
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when a MIDI event has not been completly
// received within 2 seconds
/////////////////////////////////////////////////////////////////////////////
void MPROC_NotifyTimeout(void)
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when a MIDI byte has been received
/////////////////////////////////////////////////////////////////////////////
void MPROC_NotifyReceivedByte(unsigned char byte)
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS before the shift register are loaded
/////////////////////////////////////////////////////////////////////////////
void SR_Service_Prepare(void)
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS after the shift register have been loaded
/////////////////////////////////////////////////////////////////////////////
void SR_Service_Finish(void)
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when an button has been toggled
// pin_value is 1 when button released, and 0 when button pressed
/////////////////////////////////////////////////////////////////////////////
void DIN_NotifyToggle(unsigned char pin, unsigned char pin_value)
{

}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when an encoder has been moved
// incrementer is positive when encoder has been turned clockwise, else
// it is negative
/////////////////////////////////////////////////////////////////////////////
void ENC_NotifyChange(unsigned char encoder, unsigned char incrementer)
{

      counts = timer_counter;

   //Over 1 sec from last function call ?
   if (counts >= 64)
        counts = 64;

   //Calculate diviation
   deviation = 64 - counts;

   //Depending on the direction
   if (incrementer == 1)
        value = 64 + deviation;
   else
        value = 64 - deviation;

    MIOS_LCD_Clear();
    MIOS_LCD_PrintCString("Encoder #: ");
    MIOS_LCD_PrintBCD1(encoder);

    if (incrementer == 1)
          MIOS_LCD_PrintCString(" Pos");
    else if (incrementer == 255)
          MIOS_LCD_PrintCString(" Neg");
    MIOS_LCD_CursorSet(0x40);
    MIOS_LCD_PrintCString("Value: ");
    MIOS_LCD_PrintBCD2(value);

       MIOS_MIDI_TxBufferPut(0xb0);
       MIOS_MIDI_TxBufferPut(encoder);
       MIOS_MIDI_TxBufferPut(value);
    timer_counter = 0;

}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when a pot has been moved
/////////////////////////////////////////////////////////////////////////////
void AIN_NotifyChange(unsigned char pin, unsigned int pin_value)
{
      MIOS_LCD_Clear();
   MIOS_LCD_CursorSet(0x40);
      MIOS_LCD_PrintBCD4(pin);
   MIOS_LCD_PrintBCD4(pin_value);
      MIOS_LCD_PrintBCD4(MIOS_AIN_Pin7bitGet(pin));
   MIOS_MIDI_TxBufferPut(0xb0);
   MIOS_MIDI_TxBufferPut(pin);
   MIOS_MIDI_TxBufferPut(MIOS_AIN_Pin7bitGet(pin));
}

and in mios_tables.inc:

.
.
.
MIOS_ENC_PIN_TABLE
      ;; encoders 1-16
      ;;        SR  Pin  Mode
      ENC_ENTRY      1,      2,      MIOS_ENC_MODE_DETENTED
      ENC_EOT
      ENC_EOT
.
.
.
.

Problem:

When I turn encoder very slow, 64 is sent. When I slowly increase the turnspeed the value is supposed to change from 64 to 65-66-67-68-69 and so on to 127.

But….It’s almost impossible to get stable values between 64 and 80 (which is the most important range to fine seek for a beat). Same counter-clockwise, hard to get values between 64 and 40. Value is jumping from 64 to e.i 74.

Am I way off here or have someone else a better solution?

Anyone have some thoughts about this?

/Wise

Hi Wise,

I haven’t tried this out (currently I don’t have access to my hardware), but I can give you four suggestions:

  1. use “unsigned char” instead of “unsigned int” – 8 bit are enough for your calculations

  2. you should prevent an overrun in the Timer() function to avoid unexpected results. Just write “if( timer_counter < 64) ++timer_counter;”

this obsoletes also the additional check in ENC_NotifyChange()

  1. the MIOS driver already provides some kind of deviation function. It doesn’t help in your case, since you need a higher resolution. But something which is important to know for you is, that the incrementer can be higher than 1 if the encoder is turned fast. So, don’t check "if( incrementer == 1 ), but check “if( incrementer < 128 )”

Maybe I will change the type of the incrementer from “unsigned char” to “char” in order to simplify such checks, because "if( incrementer > 0 ) would be easier to use

  1. if the results are still bad, try to reduce the LCD messages. Especially the MIOS_LCD_Clear() can take some time, depending on the display you are using (sometimes it can take more than 20..40 mS!)

Please let me know if this helps

Best Regards, Thorsten.

Hi TK !

I just had 10 min’s to test your suggestions this morning, unfortunately with no luck. But I was in a hurry and may have overlooked something. And now I don’t have access to my hardware untill thursday night  :(

But it would be strange if it was a “calculation timing” problem, since the problems start appear as the encoder is turned slowly. But how knows, stange things can happen in the MCU world  ;)

I’ll keep you informed when I get the hands on my hardware.

Best.

/Wise

:frowning: :frowning: :frowning:

No luck…

Checked your sugestions again without any luck. Changed the relevant code as follow:

#include "cmios.h"

//#include <pic18f452.h>

unsigned char deviation;
unsigned char value;
unsigned char timer_counter;
unsigned char counts;

unsigned char shift;
unsigned char i;


unsigned char SHIFT_BUTTON1;
unsigned char SHIFT_BUTTON2;

const SB1 = 0;
const SB2 = 1;

const ON = 1;
const OFF = 0;

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS after startup to initialize the
// application
/////////////////////////////////////////////////////////////////////////////
void Init(void)
{
   timer_counter = 0;

   // If 1 pulse/sec is minimum resolution, timer_counter should have a
   // frequency of 64 Hz = 15,625ms
   // calculate the required number of clocks for this period:
   // clocks = period / 400 nS = 15,625 mS / 400 nS = 39062

       MIOS_TIMER_Init(4,39062);

      // define number of shift registers
      MIOS_SRIO_NumberSet(4);
      
      MIOS_AIN_Muxed();
      MIOS_AIN_NumberSet(4);
      MIOS_AIN_DeadbandSet(8);

      // update frequency of SRIO chain
      MIOS_SRIO_UpdateFrqSet(1); // ms
}

void Timer(void)
{
      if (timer_counter < 64)
            timer_counter++;
}

void ENC_NotifyChange(unsigned char encoder, unsigned char incrementer)
{

      counts = timer_counter;
    timer_counter = 0;

   //Over 1 sec from last function call ?
//   if (counts >= 64)
//        counts = 64;

   //Calculate diviation
   deviation = 64 - counts;

   //Depending on the direction
   if (incrementer < 128)
        value = 64 + deviation;
   else
        value = 64 - deviation;
/*
    MIOS_LCD_Clear();
    MIOS_LCD_PrintCString("Encoder #: ");
    MIOS_LCD_PrintBCD1(encoder);

    if (incrementer == 1)
          MIOS_LCD_PrintCString(" Pos");
    else if (incrementer == 255)
          MIOS_LCD_PrintCString(" Neg");

    MIOS_LCD_CursorSet(0x40);
    MIOS_LCD_PrintCString("Value: ");
    MIOS_LCD_PrintBCD2(value);
*/
       MIOS_MIDI_TxBufferPut(0xb0);
       MIOS_MIDI_TxBufferPut(encoder);
       MIOS_MIDI_TxBufferPut(value);

}

This is annoying, sould be a fairly easy thing to do, not to complex…But obviously I can’t see what’s wrong..

If you got any more ideas I would be glad  :)

Another way to get round this is to devide “counts” in intervalls, like from ‘0-10’,‘11-15’,‘16-25’, etc..because I realy dont need the ±64 step resolution. Maybe just 5-10 different values depending on encoder speed from 0 to max.

Well, over and out for this time. Time for some sleep (sure…)

/wise

The resolution shouldn’t be a problem, the timing conditions are very relaxed in your code.

Maybe I will find some time tomorrow to try this out.

Best Regards, Thorsten.

Hi Wise,

I tried your code and think that it works in principle, but needs some finetuning.

One error: “MIOS_TIMER_Init(4,39062)” is wrong, I guess that you want to select a 1:4 prescaler, the appr. number is 2

4 is an invalid value, but I can tell you that it selects mode 0, which means: prescaler 1:1

The program works better when the timer is running faster. I checked it with a detented ALPS STEC11 (24 pulses per revolution) and MIOS_TIMER_Init(0,15000) which is ok, but not the optimum. From my impression the physical force which is required to move the encoder from one to another detent makes a fine resolution of 6bit impossible. I also tried it with the (so called) “chinese low-cost encoders” which were organiced by Ian Hurlock some time ago, they are working much better than ALPS (in your case!!!) with MIOS_TIMER_Init(0,10000)

Yes, maybe it makes sense to reduce the resolution to allow a better (and not so random-like) handling. It could also make sense to use a table which maps the counter values to the output values

Best Regards, Thorsten.

P.S.: debug hint: you can write:

void Tick(void)
{
  MIOS_LCD_CursorSet(0x00);
  MIOS_LCD_PrintHex2(counter);
}
[/code]









to debug the counter.



Tick() is the best function for such debug messages, because it is called when nothing else is to do. It's only important that this function never runs longer than ca. 300 uS to reduce the risk for a MIDI In buffer overrun







Best Regards, Thorsten.

This: http://69.56.171.55/~midibox/forum/index.php?topic=3852.0 is possibly the best solution for high resolutions

Best Regards, Thorsten.

Hi!

Just wanted to bring this up again!

Has anybody had sucess with this code?

Is development still going on?

Regards

Michael

Hi WW !

I have not got the time to work on my controller nor this code from last post in this thread (shame on me..). But I have now taken me time to start working on the project again. Right now I’m redesigning the front layout for traktor 3 new functionality.

I will see if i got some time this weekend to try this code on Traktor 3 and see if i can get it to work.

Anyone else ?

I’ll keep you informed.

/Wise

edit: I’ll think this thread should be moved to the C section..

Hello again

I have looked into the code again and changed the “concept”. Well, first of all i discovered that Traktor 3 seems to have a bug in the wind function. When setting up a deck wind cc, and send 0x40 (64) to traktor att that CC, the deck should stop winding but it dont. It’s still slooooooowly winding in some direction. Traktor 2 dont do this…

So my solution:

One “wind_activate” button to activate the encoder as “wind”. When that button is pressed, the encoder sends CC of 0x01 or 0x7f depending on the direction. When releasing the activate button MIOS sends a “wind reset” CC to traktor.

Traktor wind is setup as Deck wind, direct, rotary encoder (64). And the reset as Deck wind, reset, button. Setup the acceleration and sensetivity as you like and prefer.

This way you can wind slowly in both directions and stop the wind when you release the button. Also you dont need to constantly spinning the encoder if you are winding long times as traktor wont stop winding untill you release the button.

Good luck and let me know is any problem occurs and excuse me for my poor english explanations…

So, here is the code. You have to modify the constants in the program to match your activate button and encoder.

Main.c:

/*
 * MIOS SDCC Wrapper
 *
 * ==========================================================================
 *
 * Copyright (C) 2004  Thorsten Klose (tk@midibox.org)
 * 
 * ==========================================================================
 * 
 * This file is part of a MIOS application
 *
 * This application is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with This application; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * ==========================================================================
 */

#include "cmios.h"
#include "pic18f452.h"

unsigned char deckA_wind_active;
unsigned char deckA_winded;

const deckA_wind_button = 0 ;


/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS after startup to initialize the 
// application
/////////////////////////////////////////////////////////////////////////////
void Init(void) __wparam
{

	MIOS_TIMER_Init(2,12500);

	// define number of shift registers
	MIOS_SRIO_NumberSet(4);

	MIOS_AIN_Muxed();
	MIOS_AIN_NumberSet(4);
	MIOS_AIN_DeadbandSet(8);

	// update frequency of SRIO chain
	MIOS_SRIO_UpdateFrqSet(1); // ms
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS in the mainloop when nothing else is to do
/////////////////////////////////////////////////////////////////////////////
void Tick(void) __wparam
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is periodically called by MIOS. The frequency has to be
// initialized with MIOS_Timer_Set
/////////////////////////////////////////////////////////////////////////////
void Timer(void) __wparam
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when the display content should be 
// initialized. Thats the case during startup and after a temporary message
// has been printed on the screen
/////////////////////////////////////////////////////////////////////////////
void DISPLAY_Init(void) __wparam
{
}

/////////////////////////////////////////////////////////////////////////////
//  This function is called in the mainloop when no temporary message is shown
//  on screen. Print the realtime messages here
/////////////////////////////////////////////////////////////////////////////
void DISPLAY_Tick(void) __wparam
{
}

/////////////////////////////////////////////////////////////////////////////
//  This function is called by MIOS when a complete MIDI event has been received
/////////////////////////////////////////////////////////////////////////////
void MPROC_NotifyReceivedEvnt(unsigned char evnt0, unsigned char evnt1, unsigned char evnt2) __wparam
{
  // print received MIDI event
   MIOS_LCD_Clear();

   MIOS_LCD_PrintCString("Received:");

   MIOS_LCD_CursorSet(0x40);
   MIOS_LCD_PrintHex2(evnt0);
   MIOS_LCD_PrintHex2(evnt1);
   MIOS_LCD_PrintHex2(evnt2);
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when a MIDI event has been received
// which has been specified in the MIOS_MPROC_EVENT_TABLE
/////////////////////////////////////////////////////////////////////////////
void MPROC_NotifyFoundEvent(unsigned entry, unsigned char evnt0, unsigned char evnt1, unsigned char evnt2) __wparam
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when a MIDI event has not been completly
// received within 2 seconds
/////////////////////////////////////////////////////////////////////////////
void MPROC_NotifyTimeout(void) __wparam
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when a MIDI byte has been received
/////////////////////////////////////////////////////////////////////////////
void MPROC_NotifyReceivedByte(unsigned char byte) __wparam
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS before the shift register are loaded
/////////////////////////////////////////////////////////////////////////////
void SR_Service_Prepare(void) __wparam
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS after the shift register have been loaded
/////////////////////////////////////////////////////////////////////////////
void SR_Service_Finish(void) __wparam
{
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when an button has been toggled
// pin_value is 1 when button released, and 0 when button pressed
/////////////////////////////////////////////////////////////////////////////
void DIN_NotifyToggle(unsigned char pin, unsigned char pin_value) __wparam
{


	if (pin == deckA_wind_button)
	{
		if (pin_value == 0 )
		{
			deckA_wind_active = 1;
		}
		else if (pin_value == 1 )
		{
			deckA_wind_active = 0;
			MIOS_MIDI_TxBufferPut(0xb0);
			MIOS_MIDI_TxBufferPut(0x00);
			MIOS_MIDI_TxBufferPut(0x40);
		}
	}

}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when an encoder has been moved
// incrementer is positive when encoder has been turned clockwise, else
// it is negative
/////////////////////////////////////////////////////////////////////////////
void ENC_NotifyChange(unsigned char encoder, char incrementer) __wparam
{
	if (deckA_wind_active == 1)
	{
		MIOS_MIDI_TxBufferPut(0xb1);
		MIOS_MIDI_TxBufferPut(encoder);
		if (incrementer < 0)
		{
			MIOS_MIDI_TxBufferPut(0x7F);
		}
		else
			MIOS_MIDI_TxBufferPut(0x01);
	}
}

/////////////////////////////////////////////////////////////////////////////
// This function is called by MIOS when a pot has been moved
/////////////////////////////////////////////////////////////////////////////
void AIN_NotifyChange(unsigned char pin, unsigned int pin_value) __wparam
{
	MIOS_LCD_Clear();
	MIOS_LCD_CursorSet(0x40);
	MIOS_LCD_PrintBCD4(pin);
	MIOS_LCD_PrintBCD4(pin_value);
	MIOS_LCD_PrintBCD4(MIOS_AIN_Pin7bitGet(pin));
	MIOS_MIDI_TxBufferPut(0xb0);
	MIOS_MIDI_TxBufferPut(pin);
	MIOS_MIDI_TxBufferPut(MIOS_AIN_Pin7bitGet(pin));
}

edit: pleeease pleese move this into the C section of the forum..

Nice one mate, thanks for sharing!

Thanks for your PM and thanks for the effort you put in this.

One question, though.

Do you think that is possible to automate the “wind reset” procedure by sending the reset signal after a certain amount of time when the encoder has not been touched?

Which traktor version do you use. Has Native Instruments been informed about this bug?

[Hmm. I think I need to learn C …]

Thanks a lot.

Regards

Michael

Which traktor version do you use. Has Native Instruments been informed about this bug?

The winding or scrolling bug has been present in Traktor since 2.0. NI has been informed by multiple people and there’s a thread about it on their community forums. Doesn’t mean they’ll fix it though… :slight_smile:

-drin

Yeah, Absynth has a PRG-CH Change Bug since 3.0.

I start a small riot every month or so via NI forum and by emailing the support, but I’m waiting since a year now… so don’t expect that anything happens (except that there will be a new release for 500$ with additional fresh bonus bugs… probably)

grrr  >:(

More good reasons to hate software synths …

The good old days Radium might have fixed it in a day or maybe in a week.

But times change, right Moebius? Sometimes for the better, sometimes for worse… :smiley:

-drin

Why do you think they were under so much pressure? They made everyone else look bad!

But times change, right Moebius? Sometimes for the better, sometimes for worse… :smiley:

-drin

Totally!

Like I’m becoming an old fart, so I really believe:

Trustworthy Computing will bring down the risks in the computing, like make malware non-existant! Only the Axis of Evil would say it’s to eliminate the competition and the last resort approach before actually having <insert company name here> to review its initial design choices and coding practices.

M

Hi!

Just wanted to announce that the Traktor wind bug is gone.

Just tested it with a regular pot. The track doesn’t move any more when the wind is set to 64.

Traktor Version is 3.0.2.098

Regards

Michael

Traktor Version is 3.0.2.098

Strange Michael. I’m running the same version and I still have the problem. So does my buddy the NI Traktor betatester. They’ve told him it’s not fixed.

-drin