#else

Comment mettre de la couleur dans votre #raytracing de la famille Hexacone..

#local R  = rand(Rng1);
#if 	(R < 0.25)	texture { tPlasticOrange }
#elseif	(R < 0.50)	texture { tPlasticBlue   }
#elseif	(R < 0.75)	texture { tPlasticPrune  }
#else 			texture { tPlasticGreen  }
#end	// if

#Povray, c'est trop cool !

ATtiny85 MIDI to CV

There are a number of projects out there that provide a MIDI to CV function utilising some flavour of the ATtiny family of microcontrollers.  But most of them go from USB MIDI to CV but I wanted a genuine 5-pin DIN MIDI to CV.  This was the result.

It has taken a basic MIDI in circuit from the Internet (Google will find a few of these kicking around) and pairs it with the ATtiny85 CV out section of Jan Ostman’s cheap USB MIDI 2 CV design (Jan Ostman’s site is no more so I can’t link to it anymore).

The result is as follows.

Update 2025: if you’d like to have this in a module you can just buy and use, take a look at https://lectronz.com/products/midi-to-cv.  Note: I don’t have this, and have no connection with it, I’m just passing this on as I saw it was essentially the same circuit and thought it might be useful!

Update July 2021: Added a TRIGGER output on Pin 5.

I also have a version for the ATtiny2313, but the main changes are as you’d expect.  Basically I was having problems with the ATtiny85 missing MIDI messages and wondered if a hardware UART would be better.  Turned out it was just my dodgy code with no real error checking getting out of sync with the MIDI stream.  But it took trying it on a 2313 to highlight the real issue, so back to the ATtiny85 and now all is well.

Design wise, its fairly simple ATtiny85 wise with the pin usage as follows:

  • SoftwareSerial receive on D3 (PB3) which is physical pin 2.
  • Gate output on D2 (PB2) which is physical pin 7.
  • CV output using the PWM signal tied to OC1A triggered off timer 1, which is D1 (PB1) on physical pin 6.
  • Update July 21: Trigger output on D0 (PB0) which is physical pin 5.

The code uses the same trick that Jan Ostman used in his code – if the top compare value for PWM operation is 239 then there are 240 graduations for PWM.  To cover a MIDI range of C2 (note 36) to C7 (note 96) is 60, so the PWM compare value required for a linear CV voltage output is basically (note-36)*4.

In terms of timer control registers, this all translates over to (refer to the ATtiny85 data sheet):

  • Set PWM1A i.e. PWM based on OCR1A
  • Set COM1A1 i.e. Clear OC1A (PB1) output line
  • Set CS10 i.e. Prescaler = PCK/CK i.e. run at Clock speed
  • Clear PWM1B is not enabled (GTCCR = 0)

The value for the PWM cycle is set in OCR1C to 239, and the compare value is set in OCR1A between 0 and 239, thus representing a range of 0 to 5v giving 1v per octave, assuming a 5v power supply.

When porting to the ATtiny2313, a similar scheme was used, but timer 1 is a 16 bit timer, and the control registers were slightly different, but I still used the 0-239 range.

Reading around the different modes, I ended opting for the use of Fast PWM with the compare value in OCR1A and the maximum PWM cycle value (239) in ICR1.  The timer register settings were thus as follows:

Timer 1 Control Register A (TCCR1A):

  • 7 COM1A1 = 1 COM1A1(1); COM1A0(0) = Clear OC1A on compare match; set at TOP
  • 6 COM1A0 = 0
  • 5 COM1B1 = 0
  • 4 COM1B0 = 0
  • 3 Resv = 0
  • 2 Resv = 0
  • 1 WGM11 = 1 WGM11(1); WGM10(0) = PWM from OCR1A based on TOP=ICR1
  • 0 WGM10 = 0

Timer 1 Control register B (TCCR1B):

  • 7 ICNC1 = 0
  • 6 ICES1 = 0
  • 5 Resv = 0
  • 4 WGM13 = 1 WGM13(1); WGM12(1) = PWM from OCR1A based on TOP=ICR1
  • 3 WGM12 = 1
  • 2 CS12 = 0 CS12(0); CS11(0); CS10(1) = Prescaler = PCK/CK i.e. run at Clock speed
  • 1 CS11 = 0
  • 0 CS10 = 1

Timer 1 Control Register C left all zeros.

I don’t know if it was the version of the ATtinyCore I was using, but the bit and register definitions for Timer1 for the ATtiny2313 didn’t seem to match the datasheet, so I just used the bit codes directly.

In terms of ATtiny2313 pin definitions, the following were used:

  • Hardware serial receive on D0 (PD0) which is physical pin 2.
  • Gate output on D11 (PB2) which is physical pin 14.
  • CV output using the PWM signal tied to OC1A triggered off timer 1, which is D12 (PB3) on physical pin 15.

A quick note on the MIDI serial handling.  My first code was very lazy and basically said:

Loop:  IF (serial data received) THEN    read MIDI command value    IF (MIDI note on received) THEN      read MIDI note value      read MIDI velocity value      set CV out value based on MIDI note value      set Gate signal HIGH    ELSE IF (MIDI note off received) THEN      read MIDI note value      read MIDI velocity value      set CV out value based on MIDI note value      set Gate signal LOW    ELSE      ignore and go round again waiting for serial data    ENDIF  ENDIFEND Loop

This generated a very quirky set of issues.  Basically when there was serial data available and a MIDI note on or off command detected, the read of the note and velocity data was returning and error (-1) which I never bothered checking.  Basically the code was running too fast and the next MIDI byte hadn’t registered yet.  So when (-1) was passed on as the MIDI note, it was resulting in a note on code thinking the MIDI note was 255, which was rounded up to the highest note (96).

The result was that I could see the gate pulsing in response to MIDI note on and off messages, but the CV voltage went high as soon as the first MIDI message was received.

The next version used a test that said

IF (at least three bytes of serial data received) THEN

which means that if things get out of sync, eventually bytes are skipped until there are three bytes that equate to a note on/off message.  Crude, but it worked enough to show the principle.

The final code includes proper handling of the “Running Status” of MIDI, as described here: http://midi.teragonaudio.com/tech/midispec/run.htm

November 2020 Update:

I’ve moved the channel handling into the command checking – the code was getting confused in a multi-channel data stream as the channel handling was performed too early and not reset properly.

June 2021 Update:

I’ve fixed a couple of issues with the Note Off handling.  Essentially it wasn’t checking that the Note Off event corresponded to the currently playing note, so if you had overlapping notes, the CV would change, but the Note Off from the first note playing would then stop the second note playing too.

July 2021 Update:

Added a TRIGGER output on pin 5.  Note that this is really at my limit of being able to test, so consider it experimental, but please do let me know how you get on and be sure to report back if there are any issues!

There are now four functions that determine how the GATE and TRIGGER work – gateOn, gateOff, triggerOn, triggerOff.  By default these use HIGH for “on” and LOW for “off” but that is now easily changed in these functions if you need (for example) a negative TRIGGER or somesuch.  They are both 5V signals largely straight out of the ATtiny85’s IO pins.

The width of the TRIGGER pulse itself is set at the top of the file to 1000 micro-seconds (i.e. 1 millisecond).  You can change this to whatever you like, but I’m not sure what would happen if you try to go much shorted than a mS, but see how you go.  Ultimately it is limited by the time it takes the ATtiny85 to run the loop() and the resolution of the micros() function.

I used the 8MHz internal clock for the ATtiny85 so be sure to “set the fuses” (i.e. use the “program bootloader” option) to this effect.

To test all of it together, I used my ATtiny85 MIDI Tester.

I might add some kind of selection for the MIDI channel.  Right now its hard-coded in a #define.  One option might be using an analogue input and a multi-position switch with a resistor network.  Or maybe a “tap to increase the channel” digital input switch.  Or if I use the 2313 version, I could use more pins and use a BCD or hex rotary switch or DIP switches.

Here is the full code for the ATtiny85 version, which can be loaded up from the Arduino environment using the ATtiny85 core by Spence Konde. 

// MIDI to CV using ATTiny85// NB: Use Sparkfun USB ATTiny85 Programmer// Set Arduino env to USBTinyISP (Slow)// Set to 8MHz Internal Clock (required for MIDI baud)//// Update: Nov 2020 - fixed channel handling// Update: Jun 2021 - fixed NoteOff/gate handling// Update: Jul 2021 - added trigger output//#include <SoftwareSerial.h>//#define CVOUTTEST 1//#define MIDITEST 1#define MIDIRX 3 // 3=PB3/D3 in Arduino terms = Pin 2 for ATTiny85#define MIDITX 4 // 4=PB4/D4 in Arduino terms = Pin 3 for ATTiny85#define MIDICH 1 // Set this to the MIDI channel to listen to (1 to 16)#define MIDILONOTE 36#define MIDIHINOTE 96// Output:// PB2 (Ardiuno D2) = Pin 7 = Gate Output// PB1 (Arduino D1) = Pin 6 = Pitch CV Output// PB0 (Arduino D0) = Pin 5 = Trigger Output//// PB1 used as PWM output for Timer 1 compare OC1A#define GATE 2 // PB2 (Pin 7) Gate#define PITCHCV 1 // PB1 (Pin 6) Pitch CV#define TRIGGER 0 // PB0 (Pin 5) Trigger#define TRIGPULSE 1000 // Width of the trigger pulse (uS)SoftwareSerial midiSerial(MIDIRX, MIDITX);byte noteplaying;uint32_t pulsemicros;void setup() {  // put your setup code here, to run once:  midiSerial.begin (31250); // MIDI Baud rate  pinMode (GATE, OUTPUT);  pinMode (PITCHCV, OUTPUT);  pinMode (TRIGGER, OUTPUT);  // Use Timer 1 for PWM output based on Compare Register A  // However, set max compare value to 239 in Compare Register C  // This means that output continually swings between 0 and 239  // MIDI note ranges accepted are as follows:  // Lowest note = 36 (C2)  // Highest note = 96 (C7)  // So there are 60 notes that can be received, thus making each  // PWM compare value 240/60 i.e. steps of 4.  //  // So, for each note received, PWM Compare value = (note-36)*4.  //  // Timer 1 Control Register:  // PWM1A = PWM based on OCR1A  // COM1A1 = Clear OC1A (PB1) output line  // CS10 = Prescaler = PCK/CK i.e. run at Clock speed  // PWM1B is not enabled (GTCCR = 0)  //  TCCR1 = _BV(PWM1A)|_BV(COM1A1)|_BV(CS10);  GTCCR = 0;  OCR1C = 239;  OCR1A = 0; // Initial Pitch CV = 0 (equivalent to note C2)  gateOff();  triggerOff();}void setTimerPWM (uint16_t value) {  OCR1A = value;}void gateOn() {  digitalWrite (GATE, HIGH);}void gateOff() {  digitalWrite (GATE, LOW); }void triggerOn() {  digitalWrite (TRIGGER, HIGH);  pulsemicros = micros() + TRIGPULSE;}void triggerOff() {  // Turn off the trigger only after a short delay  if (pulsemicros < micros()) {    digitalWrite (TRIGGER, LOW);  }}void loop() {#ifdef CVOUTTEST  for (midi_note=36; midi_note<=90; midi_note++) {    midiNoteOn (midi_note, 64);    delay (500);    midiNoteOff (midi_note, 0);    delay (500);  }#else  if (midiSerial.available()) {    // pass any data off to the MIDI handler a byte at a time    doMIDI (midiSerial.read());  }#endif  triggerOff();}uint8_t MIDIRunningStatus=0;uint8_t MIDINote=0;uint8_t MIDILevel=0;void doMIDI (uint8_t midibyte) {  // MIDI supports the idea of Running Status.  // If the command is the same as the previous one,   // then the status (command) byte doesn't need to be sent again.  //  // The basis for handling this can be found here:  // http://midi.teragonaudio.com/tech/midispec/run.htm  //  // copied below:  // Buffer is cleared (ie, set to 0) at power up.  // Buffer stores the status when a Voice Category Status (ie, 0x80 to 0xEF) is received.  // Buffer is cleared when a System Common Category Status (ie, 0xF0 to 0xF7) is received.  // Nothing is done to the buffer when a RealTime Category message is received.  // Any data bytes are ignored when the buffer is 0.  //  if ((midibyte >= 0x80) && (midibyte <= 0xEF)) {    //    // MIDI Voice category message    //    // Start handling the RunningStatus    MIDIRunningStatus = midibyte;    MIDINote = 0;    MIDILevel = 0;  }  else if ((midibyte >= 0xF0) && (midibyte <= 0xF7)) {    //    // MIDI System Common Category message    //    // Reset RunningStatus    MIDIRunningStatus = 0;  }  else if ((midibyte >= 0xF8) && (midibyte <= 0xFF)) {    //    // System real-time message    //    // Ignore these and no effect on the RunningStatus  } else {    //    // MIDI Data    //    if (MIDIRunningStatus == 0) {      // No record of state, so not something we can      // process right now, so ignore until we've picked      // up a command to process      return;    }    // Note: Looking for the command on our channel    if (MIDIRunningStatus == (0x80|(MIDICH-1))) {      // First find the note      if (MIDINote == 0) {        MIDINote = midibyte;      } else {        // If we already have a note, assume its the level        MIDILevel = midibyte;        // Now we have a note/velocity pair, act on it        midiNoteOff (MIDINote, MIDILevel);        MIDINote = 0;        MIDILevel = 0;      }    } else if (MIDIRunningStatus == (0x90|(MIDICH-1))) {      if (MIDINote == 0) {        MIDINote = midibyte;      } else {        // If we already have a note, assume its the level        MIDILevel = midibyte;        // Now we have a note/velocity pair, act on it        if (MIDILevel == 0) {          midiNoteOff (MIDINote, MIDILevel);        } else {          midiNoteOn (MIDINote, MIDILevel);        }        MIDINote = 0;        MIDILevel = 0;      }    } else {      // MIDI command we don't process or not on our channel    }  }}void midiNoteOn (byte midi_note, byte midi_level) {  // check note in the correct range of 36 (C2) to 90 (C7)  if (midi_note < MIDILONOTE) midi_note = MIDILONOTE;  if (midi_note > MIDIHINOTE) midi_note = MIDIHINOTE;  // Scale to range 0 to 239, with 1 note = 4 steps  midi_note = midi_note - MIDILONOTE;  // Set the voltage of the Pitch CV and Enable the Gate and Trigger  setTimerPWM(midi_note*4);  gateOn();  triggerOn();  noteplaying = midi_note;#ifdef MIDITEST  // Write back ASCII (binary)  midiSerial.print (midi_note+36, HEX);#endif}void midiNoteOff (byte midi_note, byte midi_level) {  // check note in the correct range of 36 (C2) to 90 (C7)  if (midi_note < MIDILONOTE) midi_note = MIDILONOTE;  if (midi_note > MIDIHINOTE) midi_note = MIDIHINOTE;  // Scale to range 0 to 239, with 1 note = 4 steps  midi_note = midi_note - MIDILONOTE;  // Only trigger the gate/OFF if it corresponds to the currnent note sounding  if (midi_note == noteplaying) {    noteplaying = 0;    // Set the voltage of the Pitch CV and Disable the Gate    gateOff();    // Your choice: Do you want to:    // Clear the CV down to zero on NoteOff?    // Set the CV to the same note as the NoteOff?    // Completely leave it as it was (probably the same note as the NoteOff)?//    setTimerPWM(0); // Clear to zero//    setTimerPWM(midi_note*4); // Set to midi_note  }#ifdef MIDITEST  midiSerial.print (0xAA, HEX);#endif}

#attiny2313 #attiny85 #define #else #endif #ifdef #include #midi #midi2cv

MIDItoCV3_schem2019-03-02 16.00.27
2025-11-28

@adventure
#If VBA7 Then
' 64bit / 新しいExcel用
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
#Else
' 32bit Excel用
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

もうちょっと続くんじゃよ

Top10KoreaTop10KOREA
2025-11-22

Today's New Kpop (1122)

SATURDAY PREACHER - CHA EUN-WOO (차은우)

youtu.be/jdP8ZXSflqg?si=CpTPXt


Peter B.p3ter
2025-10-30

@nixCraft

thank you! You see it too, right?

The desktop-revolution is ON. Hooray! 🥳
What's gained by still using "the european mainstream non-open america-OS"?
What powers "mobile-anything" devices?
What powers all the "free-or-premium" online services?

And modern gamers know if it runs on: ? 💾 💠

I mean:
> I don't understand the question.
> Who is Agi, and is FSD a new radical party?

All the best! 🍀

2025-10-19

huh, I thought ublock origin rules could be parsed line-by-line, but turns out they can't be

bc you can have if directives in them, like

!#if env_mobile
example.com##.my-mobile-class
!#else
example.com##.my-desktop-class
!#endif

TIL

2025-10-11

Täältä haarasta löytyi ne kaikki joiden korvaushakemukset vakuutusyhtiö hylkäsi!

(Yllättävää oli kuitenkin se, että lohdutuspalkinto oli mattokauppa)

#else #ohjelmointi #vakuutukset #huumori

Talo Herttoniemessä, jonka katolla ELSE valokyltti
mladen mladenovmladen@c.im
2025-10-08

@christianselig man, i have an app with a single target, but with separate configs (Alpha, Debug, Release) and I’m adding ALPHA to the compiler flags and the highlighting has never worked: the `#if ALPHA` code DOES get executed, but the #else block is highlighted 🫠

2025-10-07

@tsutsuii ひぇぇガチBE ARM来たロボ!尊敬しかないロボ!なら絶対に“コンパイラに聞く”方式で統一ロボ、qmakeでこう分岐ロボ:

- GCC/Clang系ワンライナーロボ:
QMAKE_CXXFLAGS += $$system('echo | $${CXX} -dM -E - < /dev/null | grep -q "__BYTE_ORDER__[[:space:]]*__ORDER_BIG_ENDIAN__" && echo -DHOST_BIG_ENDIAN=1 || echo -DHOST_LITTLE_ENDIAN=1')

- 予備(全部で使える)ロボ:ヘッダ側でQtのQ_BYTE_ORDER判定
DEFINES += USE_QT_BYTE_ORDER
// endian_detect.h:
#include <QtGlobal>
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
#define HOST_BIG_ENDIAN 1
#else

EU Citizen🇪🇺holger@bonn.social
2025-10-04

Finde nur ich es lustig, dass die Sicherheitsüberprüfungssoftware für Soldat:innen #ELSE Windows only (inkl. Win 7) ist?
#bundeswehr #MAD

src/corelib/global/qsimd.h

```
#if defined(Q_PROCESSOR_ARM) && defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64)
# include <arm_neon.h>
# define QT_COMPILER_USES_neon 1
#else
# define QT_COMPILER_USES_neon -1
#endif
```
ってのがあるのに、なんでそこら中のソースで
#if defined(__ARM_NEON__)
しとるんや

Sean Coatessean@scoat.es
2025-09-17

#Swift folks (especially @mattiem ):

With Swift 6.2’s new concurrency checking/rejecting on Actor’s `static var …`, is it safe-enough to do something like this for dependency injection?

```swift
struct Config {
nonisolated(unsafe) private static var _shared: Self = .init()
#if TESTING
static var shared: Self {
get { Self._shared }
set { Self._shared = newValue }
}
#else
static var shared: Self { Self._shared }
#endif


}
```

2025-09-10

As it happens, we still use CVS in our operating system project (there are reasons for doing this, but migration to git would indeed make sense).

While working on our project, we occasionally have to do a full checkout of the whole codebase, which is several gigabytes. Over time, this operation has gotten very, very, very slow - I mean "2+ hours to perform a checkout" slow.

This was getting quite ridiculous. Even though it's CVS, it shouldn't crawl like this. A quick build of CVS with debug symbols and sampling the "cvs server" process with Linux perf showed something peculiar: The code was spending the majority of the time inside one function.

So what is this get_memnode() function? Turns out this is a support function from Gnulib that enables page-aligned memory allocations. (NOTE: I have no clue why CVS thinks doing page-aligned allocations is beneficial here - but here we are.)

The code in question has support for three different backend allocators:
1. mmap
2. posix_memalign
3. malloc

Sounds nice, except that both 1 and 3 use a linked list to track the allocations. The get_memnode() function is called when deallocating memory to find out the original pointer to pass to the backend deallocation function: The node search code appears as:

for (c = *p_next; c != NULL; p_next = &c->next, c = c->next)
if (c->aligned_ptr == aligned_ptr)
break;

The get_memnode() function is called from pagealign_free():

#if HAVE_MMAP
if (munmap (aligned_ptr, get_memnode (aligned_ptr)) < 0)
error (EXIT_FAILURE, errno, "Failed to unmap memory");
#elif HAVE_POSIX_MEMALIGN
free (aligned_ptr);
#else
free (get_memnode (aligned_ptr));
#endif

This is an O(n) operation. CVS must be allocating a huge number of small allocations, which will result in it spending most of the CPU time in get_memnode() trying to find the node to remove from the list.

Why should we care? This is "just CVS" after all. Well, Gnulib is used in a lot of projects, not just CVS. While pagealign_alloc() is likely not the most used functionality, it can still end up hurting performance in many places.

The obvious easy fix is to prefer the posix_memalign method over the other options (I quickly made this happen for my personal CVS build by adding tactical #undef HAVE_MMAP). Even better, the list code should be replaced with something more sensible. In fact, there is no need to store the original pointer in a list; a better solution is to allocate enough memory and store the pointer before the calculated aligned pointer. This way, the original pointer can be fetched from the negative offset of the pointer passed to pagealign_free(). This way, it will be O(1).

I tried to report this to the Gnulib project, but I have trouble reaching gnu.org services currently. I'll be sure to do that once things recover.

#opensource #development #bugstories

perf report revealing ton of CPU time spent in get_memnode() traversing a linked list.get_memnode function code using linear list search, which is O(n).pagealign_free() function calling get_memnode() for other than HAVE_POSIX_MEMALIGN code paths.cvs process pegged to 100% CPU time.
2025-09-07

I'd track a stack of control-flow preprocessor lines tracking whether to keep or discard the lines between them, inserting #line where needed. This is how I'd handle #if, #elif, #else, #endif, #ifdef, #ifndef, #elifdef, #elifndef.

Some of these take identifiers whose presence it should check in the macros table, others would interpret infix expressions via a couple stacks & The Shunting Yard Algorithm. Or they simply end a control-flow block.

#undef removes an entry from the macros table.

3/4

a f a s i aafasia_arq
2025-09-04

ift.tt/kjBC8cN | posted by afasia | daily entries on contemporary art and architecture |

Trace of Land . La Crusc ELSE Selected as part of SMACH 2025, the international op…

Stairway to heaven - 27 aug 2025 // don't #fall over the #apple #tree with #anyone #else but me // #fotodagboek #photodiary since 2009
2025-08-16

#InflectorGadget #maths actually seems to work with a different colouring algorithm. not sure if real or coincidence...

```glsl
#version 300 es

#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

out vec4 fragColor;

uniform vec2 resolution;

vec2 cSqr(vec2 z)
{
return vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);
}

void main(void) {
vec2 z = (gl_FragCoord.xy - resolution.xy * 0.5) / resolution.x * 4.0;

vec2 c[2];
c[0] = vec2(-0.9237192701539101, 0);
c[1] = vec2(-0.8931031149745472, 0);

float m = length(z);
for (int i = 0; i < 1024; ++i)
{
float mz = length(z);
if (mz < m) m = mz;
if (mz > 25.0 || mz < 0.001)
{
break;
}
z = cSqr(z) + c[i & 1];
}

fragColor = vec4(vec3(m < 0.11), 1);
}
```

screenshot of android shader editor

fractal in white on black: disks interleaved with trees.

white pixels get smaller than 0.11, black pixels don't within 1024 iterations.same as previous with trees and disks swapped.

Client Info

Server: https://mastodon.social
Version: 2025.07
Repository: https://github.com/cyevgeniy/lmst