Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
6
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
C
crowbx
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Iterations
Merge Requests
0
Merge Requests
0
Requirements
Requirements
List
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Test Cases
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issue
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacob Vosmaer
crowbx
Commits
75a66c48
Commit
75a66c48
authored
May 25, 2020
by
Jacob Vosmaer
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'fewer-files'
parents
98512076
820ce99a
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
463 additions
and
602 deletions
+463
-602
Makefile
Makefile
+1
-1
clamp_add.c
clamp_add.c
+0
-17
clamp_add.h
clamp_add.h
+0
-8
crowbx.c
crowbx.c
+460
-40
dac.c
dac.c
+0
-90
dac.h
dac.h
+0
-12
gate.c
gate.c
+0
-35
gate.h
gate.h
+0
-11
midi.c
midi.c
+0
-1
midi.h
midi.h
+1
-1
mono.c
mono.c
+0
-84
mono.h
mono.h
+0
-11
notes.h
notes.h
+0
-7
pitch.c
pitch.c
+0
-41
pitch.h
pitch.h
+0
-15
poly.c
poly.c
+0
-80
poly.h
poly.h
+0
-11
poly2.c
poly2.c
+0
-67
poly2.h
poly2.h
+0
-11
uart.h
uart.h
+0
-8
vibrato.c
vibrato.c
+0
-39
vibrato.h
vibrato.h
+0
-11
voice.h
voice.h
+1
-1
No files found.
Makefile
View file @
75a66c48
PRG
=
crowbx
OBJ
=
crowbx.o midi.o
dac.o gate.o poly.o mono.o list.o vibrato.o poly2.o clamp_add.o pitch
.o pitch_env.o vibrato_rate.o voice.o
OBJ
=
crowbx.o midi.o
list
.o pitch_env.o vibrato_rate.o voice.o
MCU_TARGET
=
atmega328p
OPTIMIZE
=
-O3
...
...
clamp_add.c
deleted
100644 → 0
View file @
98512076
#include "clamp_add.h"
uint16_t
clamp_add
(
uint16_t
x
,
int16_t
delta
)
{
if
(
delta
<
0
)
{
uint16_t
sub
=
-
delta
;
if
(
x
<
sub
)
{
return
0
;
}
return
x
-
sub
;
}
uint16_t
add
=
delta
;
if
(
UINT16_MAX
-
x
<
add
)
{
return
UINT16_MAX
;
}
return
x
+
add
;
}
clamp_add.h
deleted
100644 → 0
View file @
98512076
#ifndef CLAMP_ADD_H
#define CLAMP_ADD_H
#include <stdint.h>
uint16_t
clamp_add
(
uint16_t
x
,
int16_t
delta
);
#endif
crowbx.c
View file @
75a66c48
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/atomic.h>
#include <util/delay.h>
#include "array_size.h"
#include "dac.h"
#include "gate.h"
#include "list.h"
#include "midi.h"
#include "mono.h"
#include "pitch.h"
#include "pitch_env.h"
#include "poly.h"
#include "poly2.h"
#include "uart.h"
#include "vibrato.h"
#include "vibrato_rate.h"
#include "voice.h"
static
struct
{
uint8_t
data
[
16
];
uint8_t
head
;
uint8_t
tail
;
}
uart_buffer
;
uint16_t
clamp_add
(
uint16_t
x
,
int16_t
delta
)
{
if
(
delta
<
0
)
{
uint16_t
sub
=
-
delta
;
if
(
x
<
sub
)
{
return
0
;
}
return
x
-
sub
;
}
void
uart_buffer_push
(
uint8_t
x
)
{
ATOMIC_BLOCK
(
ATOMIC_RESTORESTATE
)
{
uart_buffer
.
data
[
uart_buffer
.
tail
]
=
x
;
uart_buffer
.
tail
=
(
uart_buffer
.
tail
+
1
)
%
ARRAY_SIZE
(
uart_buffer
.
data
);
uint16_t
add
=
delta
;
if
(
UINT16_MAX
-
x
<
add
)
{
return
UINT16_MAX
;
}
return
x
+
add
;
}
uint8_t
uart_buffer_pop
(
uint8_t
*
x
)
{
ATOMIC_BLOCK
(
ATOMIC_RESTORESTATE
)
{
if
(
uart_buffer
.
head
==
uart_buffer
.
tail
)
{
return
false
;
// error: uart_buffer is empty
uint16_t
dac_pitch_delta
(
uint8_t
semitones
)
{
if
(
semitones
>=
NUM_SEMITONES
)
{
semitones
=
NUM_SEMITONES
-
1
;
}
return
dac_semitone_offset
(
semitones
);
}
struct
{
volatile
uint8_t
*
port
;
volatile
uint8_t
*
ddr
;
uint8_t
bit
;
}
dac_sync
=
{.
port
=
&
PORTB
,
.
ddr
=
&
DDRB
,
.
bit
=
PORTB1
};
void
dac_sync_enable
(
void
)
{
*
dac_sync
.
port
&=
~
_BV
(
dac_sync
.
bit
);
}
void
dac_sync_disable
(
void
)
{
*
dac_sync
.
port
|=
_BV
(
dac_sync
.
bit
);
}
void
dac_sync_init
(
void
)
{
*
dac_sync
.
ddr
|=
_BV
(
dac_sync
.
bit
);
dac_sync_disable
();
_delay_ms
(
1
);
// let dac boot up
}
uint8_t
dac_command_set
(
uint8_t
v
)
{
struct
dac
*
d
=
dac
(
v
);
if
(
!
d
)
{
return
0
;
};
return
0
b00110000
|
d
->
channel
;
}
void
spi_send
(
uint8_t
data
)
{
SPDR
=
data
;
loop_until_bit_is_set
(
SPSR
,
SPIF
);
}
void
dac_send
(
uint8_t
command
,
uint16_t
data
)
{
dac_sync_enable
();
spi_send
(
command
);
spi_send
(
data
>>
8
);
spi_send
(
data
&
0xff
);
dac_sync_disable
();
}
void
spi_init
(
void
)
{
enum
{
MOSI
=
PORTB3
,
MISO
=
PORTB4
,
SCK
=
PORTB5
,
SS
=
PORTB2
};
// slave select, sck, mosi all as output
DDRB
|=
_BV
(
SS
)
|
_BV
(
MOSI
)
|
_BV
(
SCK
);
PORTB
|=
_BV
(
MISO
);
// MISO pull-up to save power
// spi enable
SPCR
|=
_BV
(
SPE
)
|
_BV
(
MSTR
)
|
_BV
(
CPOL
);
SPSR
|=
_BV
(
SPI2X
);
}
void
dac_init
(
void
)
{
dac_sync_init
();
spi_init
();
}
uint16_t
dac_note_pitch
(
uint8_t
v
,
uint8_t
note
)
{
enum
{
MIDI_LOWEST
=
24
};
struct
dac
*
d
=
dac
(
v
);
if
(
!
d
)
{
return
0
;
};
// Don't go lower than "C0"
if
(
note
<
MIDI_LOWEST
)
{
note
=
MIDI_LOWEST
;
}
if
(
note
>
127
)
{
note
=
127
;
}
uint8_t
octave
=
(
note
-
MIDI_LOWEST
)
/
NUM_SEMITONES
;
if
(
octave
>
7
)
{
return
0
;
}
return
clamp_add
(
d
->
offset
+
octave
*
d
->
scale
,
dac_semitone_offset
((
note
-
MIDI_LOWEST
)
%
NUM_SEMITONES
));
}
void
gate_on_legato
(
uint8_t
v
)
{
struct
gate
*
g
=
gate
(
v
);
if
(
!
g
)
{
return
;
}
*
(
g
->
port
)
|=
g
->
mask
;
}
void
gate_off
(
uint8_t
v
)
{
struct
gate
*
g
=
gate
(
v
);
if
(
!
g
)
{
return
;
}
*
(
g
->
port
)
&=
~
(
g
->
mask
);
}
void
gate_on
(
uint8_t
v
)
{
if
(
v
>=
NUM_VOICES
)
{
return
;
}
gate_off
(
v
);
pitch_env_trigger
(
v
);
gate_on_legato
(
v
);
}
void
gate_init
(
void
)
{
for_each_voice
(
v
)
{
gate_off
(
v
);
}
}
enum
{
NUM_NOTES
=
128
};
struct
{
uint32_t
phase
;
int16_t
amount
;
uint8_t
rate
;
uint8_t
depth
;
}
vibrato
;
void
vibrato_set_rate
(
uint8_t
val
)
{
vibrato
.
rate
=
val
;
}
void
vibrato_set_depth
(
uint8_t
val
)
{
vibrato
.
depth
=
val
;
}
int16_t
vibrato_detune
(
void
)
{
return
vibrato
.
amount
;
}
void
vibrato_update
(
uint8_t
delta
)
{
enum
{
VIB_MID
=
INT16_MAX
>>
1
};
vibrato
.
phase
+=
delta
*
pgm_read_float
(
&
(
vibrato_rate_table
[
vibrato
.
rate
]));
uint16_t
phase16
=
vibrato
.
phase
>>
16
;
if
(
phase16
<
INT16_MAX
)
{
vibrato
.
amount
=
phase16
-
VIB_MID
;
}
else
{
vibrato
.
amount
=
phase16
-
INT16_MAX
;
vibrato
.
amount
=
INT16_MAX
-
vibrato
.
amount
;
vibrato
.
amount
-=
VIB_MID
;
}
float
pitch_factor
=
dac_pitch_delta
(
7
)
/
((
float
)
INT16_MAX
);
vibrato
.
amount
*=
pitch_factor
;
float
depth_factor
=
vibrato
.
depth
/
127
.
0
;
vibrato
.
amount
*=
depth_factor
;
if
(
!
vibrato
.
depth
)
{
vibrato
.
amount
=
0
;
}
}
struct
{
int16_t
bend_data
;
uint16_t
base_pitch
[
NUM_VOICES
];
}
pitch
;
void
pitch_set_note
(
uint8_t
voice
,
uint8_t
note
,
int16_t
detune
)
{
if
((
voice
>=
NUM_VOICES
)
||
(
note
>
127
))
{
return
;
}
pitch
.
base_pitch
[
voice
]
=
clamp_add
(
dac_note_pitch
(
voice
,
note
),
detune
);
}
void
pitch_set_bend
(
uint16_t
bend
)
{
enum
{
BEND_CENTER
=
8192
,
BEND_MAX
=
16383
};
float
bend_factor
=
((
int16_t
)(
bend
&
BEND_MAX
)
-
BEND_CENTER
)
/
((
float
)
BEND_MAX
);
pitch
.
bend_data
=
dac_pitch_delta
(
4
)
*
bend_factor
;
}
void
pitch_update_clock
(
uint8_t
delta
)
{
pitch_env_update_clock
(
delta
);
for_each_voice
(
v
)
{
uint16_t
data
=
clamp_add
(
pitch
.
base_pitch
[
v
],
vibrato_detune
());
data
=
clamp_add
(
data
,
pitch_env_delta
(
v
)
*
(
float
)
dac_pitch_delta
(
5
));
data
=
clamp_add
(
data
,
pitch
.
bend_data
);
dac_send
(
dac_command_set
(
v
),
data
);
}
}
struct
{
uint8_t
avail_array
[
NUM_VOICES
];
uint8_t
in_use_array
[
NUM_VOICES
];
struct
list
avail
;
struct
list
in_use
;
uint8_t
current_note
[
NUM_VOICES
];
}
poly
=
{
.
avail
=
{.
array
=
poly
.
avail_array
,
.
sup
=
ARRAY_SIZE
(
poly
.
avail_array
)},
.
in_use
=
{.
array
=
poly
.
in_use_array
,
.
sup
=
ARRAY_SIZE
(
poly
.
in_use_array
)},
};
uint8_t
poly_acquire
(
void
)
{
if
(
l_empty
(
&
poly
.
avail
))
{
l_pushl
(
&
poly
.
avail
,
l_popr
(
&
poly
.
in_use
));
}
uint8_t
v
=
l_popr
(
&
poly
.
avail
);
l_pushl
(
&
poly
.
in_use
,
v
);
return
v
;
}
void
poly_release
(
uint8_t
v
)
{
if
(
l_contains
(
&
poly
.
in_use
,
v
))
{
l_delete
(
&
poly
.
in_use
,
v
);
l_pushl
(
&
poly
.
avail
,
v
);
}
}
void
poly_init
(
void
)
{
l_flush
(
&
poly
.
in_use
);
l_flush
(
&
poly
.
avail
);
for_each_voice
(
v
)
{
l_pushl
(
&
poly
.
avail
,
v
);
poly
.
current_note
[
v
]
=
NUM_NOTES
;
}
}
void
poly_note_on
(
uint8_t
key
)
{
if
(
key
>=
NUM_NOTES
)
{
return
;
}
uint8_t
v
=
poly_acquire
();
if
(
v
>=
NUM_VOICES
)
{
return
;
}
pitch_set_note
(
v
,
key
,
0
);
gate_on
(
v
);
poly
.
current_note
[
v
]
=
key
;
}
void
poly_note_off
(
uint8_t
key
)
{
if
(
key
>=
NUM_NOTES
)
{
return
;
}
for_each_voice
(
v
)
{
if
(
poly
.
current_note
[
v
]
==
key
)
{
poly
.
current_note
[
v
]
=
NUM_NOTES
;
poly_release
(
v
);
gate_off
(
v
);
}
}
}
*
x
=
uart_buffer
.
data
[
uart_buffer
.
head
];
uart_buffer
.
head
=
(
uart_buffer
.
head
+
1
)
%
ARRAY_SIZE
(
uart_buffer
.
data
);
struct
{
uint8_t
available_voices_array
[
NUM_VOICES
];
uint8_t
note_queue_array
[
NUM_NOTES
];
struct
list
available_voices
;
struct
list
note_queue
;
uint8_t
current_note
[
NUM_VOICES
];
}
poly2
=
{
.
available_voices
=
{.
array
=
poly2
.
available_voices_array
,
.
sup
=
ARRAY_SIZE
(
poly2
.
available_voices_array
)},
.
note_queue
=
{.
array
=
poly2
.
note_queue_array
,
.
sup
=
ARRAY_SIZE
(
poly2
.
note_queue_array
)},
};
void
poly2_init
(
void
)
{
l_flush
(
&
poly2
.
note_queue
);
l_flush
(
&
poly2
.
available_voices
);
for_each_voice
(
v
)
{
poly2
.
current_note
[
v
]
=
poly2
.
note_queue
.
sup
;
l_pushl
(
&
poly2
.
available_voices
,
v
);
}
}
return
true
;
void
poly2_assign_available_voices
(
void
)
{
for
(;
!
l_empty
(
&
poly2
.
available_voices
);)
{
uint8_t
v
=
l_popr
(
&
poly2
.
available_voices
);
uint8_t
note
=
l_popr
(
&
poly2
.
note_queue
);
if
(
note
==
poly2
.
note_queue
.
sup
)
{
l_pushr
(
&
poly2
.
available_voices
,
v
);
return
;
}
gate_on
(
v
);
pitch_set_note
(
v
,
note
,
0
);
poly2
.
current_note
[
v
]
=
note
;
}
}
ISR
(
USART_RX_vect
)
{
uint8_t
uart_status
=
UCSR0A
;
if
(
!
(
uart_status
&
_BV
(
RXC0
)))
{
// No UART data available
void
poly2_note_on
(
uint8_t
note
)
{
if
(
note
>=
poly2
.
note_queue
.
sup
)
{
return
;
}
// Read data to clear all status bits
uint8_t
uart_data
=
UDR0
;
l_pushl
(
&
poly2
.
note_queue
,
note
);
poly2_assign_available_voices
();
}
if
(
uart_status
&
(
_BV
(
FE0
)
|
_BV
(
DOR0
)
|
_BV
(
UPE0
))
)
{
// There was a UART error
void
poly2_note_off
(
uint8_t
note
)
{
if
(
note
>=
poly2
.
note_queue
.
sup
)
{
return
;
}
uart_buffer_push
(
uart_data
);
l_delete
(
&
poly2
.
note_queue
,
note
);
for_each_voice
(
v
)
{
if
(
poly2
.
current_note
[
v
]
==
note
)
{
poly2
.
current_note
[
v
]
=
poly2
.
note_queue
.
sup
;
gate_off
(
v
);
l_pushl
(
&
poly2
.
available_voices
,
v
);
}
}
poly2_assign_available_voices
();
}
uint8_t
uart_read
(
uint8_t
*
data
)
{
return
uart_buffer_pop
(
data
);
}
struct
{
uint8_t
held_notes_array
[
NUM_NOTES
];
struct
list
held_notes
;
const
float
detune_factor
[
MAX_VOICES
];
uint8_t
detune_amount
;
uint8_t
stacked_voices
;
}
mono
=
{
.
held_notes
=
{.
array
=
mono
.
held_notes_array
,
.
sup
=
ARRAY_SIZE
(
mono
.
held_notes_array
)},
.
detune_factor
=
{
1
.
2
,
-
1
.
2
,
0
.
6
,
-
0
.
6
,
0
.
9
,
-
0
.
9
,
0
.
3
,
-
0
.
3
},
};
uint8_t
mono_active_voices
(
void
)
{
if
(
mono
.
stacked_voices
&&
mono
.
stacked_voices
<
NUM_VOICES
)
{
return
mono
.
stacked_voices
;
}
else
{
return
NUM_VOICES
;
}
}
void
mono_init
(
void
)
{
l_flush
(
&
mono
.
held_notes
);
}
void
mono_set_pitch
(
void
)
{
if
(
l_empty
(
&
mono
.
held_notes
))
{
return
;
}
uint8_t
current_note
=
l_last
(
&
mono
.
held_notes
);
for
(
uint8_t
v
=
0
;
v
<
mono_active_voices
();
v
++
)
{
int16_t
detune
=
mono
.
detune_factor
[
v
]
*
mono
.
detune_amount
;
pitch_set_note
(
v
,
current_note
,
detune
);
}
}
void
mono_note_on
(
uint8_t
n
)
{
if
(
n
>=
NUM_NOTES
)
{
return
;
}
l_pushr
(
&
mono
.
held_notes
,
n
);
mono_set_pitch
();
for
(
uint8_t
v
=
0
;
v
<
mono_active_voices
();
v
++
)
{
gate_on
(
v
);
}
}
void
mono_note_off
(
uint8_t
n
)
{
if
(
n
>=
NUM_NOTES
)
{
return
;
}
if
(
l_last
(
&
mono
.
held_notes
)
!=
n
)
{
l_delete
(
&
mono
.
held_notes
,
n
);
return
;
}
l_delete
(
&
mono
.
held_notes
,
n
);
mono_set_pitch
();
void
(
*
f
)(
uint8_t
)
=
l_empty
(
&
mono
.
held_notes
)
?
gate_off
:
gate_on
;
for
(
uint8_t
v
=
0
;
v
<
mono_active_voices
();
v
++
)
{
f
(
v
);
}
}
void
mono_control_change
(
uint8_t
ctl
,
uint8_t
val
)
{
switch
(
ctl
)
{
case
CC_DETUNE
:
mono
.
detune_amount
=
val
;
break
;
case
CC_STACKED_VOICES
:
mono
.
stacked_voices
=
val
;
for
(
uint8_t
v
=
mono_active_voices
();
v
<
NUM_VOICES
;
v
++
)
{
gate_off
(
v
);
}
break
;
}
}
struct
{
void
(
*
init
)(
void
);
...
...
@@ -69,8 +440,8 @@ struct {
void
(
*
note_off
)(
uint8_t
n
);
void
(
*
control_change
)(
uint8_t
ctl
,
uint8_t
val
);
}
programs
[]
=
{
{
poly_init
,
poly_note_on
,
poly_note_off
,
poly_control_change
},
{
poly2_init
,
poly2_note_on
,
poly2_note_off
,
poly2_control_change
},
{
poly_init
,
poly_note_on
,
poly_note_off
,
0
},
{
poly2_init
,
poly2_note_on
,
poly2_note_off
,
0
},
{
mono_init
,
mono_note_on
,
mono_note_off
,
mono_control_change
},
};
...
...
@@ -156,14 +527,15 @@ void handle_cc(uint8_t cc, uint8_t val) {
break
;
}
programs
[
current_program
].
control_change
(
cc
,
val
);
if
(
programs
[
current_program
].
control_change
)
{
programs
[
current_program
].
control_change
(
cc
,
val
);
}
}
int
main
(
void
)
{
io_init
();
dac_init
();
midi_init
();
pitch_init
();
program_change
(
0
);
for
(;;)
{
...
...
@@ -199,3 +571,51 @@ int main(void) {
return
0
;
}
static
struct
{
uint8_t
data
[
16
];
uint8_t
head
;
uint8_t
tail
;
}
uart_buffer
;
void
uart_buffer_push
(
uint8_t
x
)
{
ATOMIC_BLOCK
(
ATOMIC_RESTORESTATE
)
{
uart_buffer
.
data
[
uart_buffer
.
tail
]
=
x
;
uart_buffer
.
tail
=
(
uart_buffer
.
tail
+
1
)
%
ARRAY_SIZE
(
uart_buffer
.
data
);
}
}
uint8_t
uart_buffer_pop
(
uint8_t
*
x
)
{
ATOMIC_BLOCK
(
ATOMIC_RESTORESTATE
)
{
if
(
uart_buffer
.
head
==
uart_buffer
.
tail
)
{
return
false
;
// error: uart_buffer is empty
}
*
x
=
uart_buffer
.
data
[
uart_buffer
.
head
];
uart_buffer
.
head
=
(
uart_buffer
.
head
+
1
)
%
ARRAY_SIZE
(
uart_buffer
.
data
);
}
return
true
;
}
ISR
(
USART_RX_vect
)
{
uint8_t
uart_status
=
UCSR0A
;
if
(
!
(
uart_status
&
_BV
(
RXC0
)))
{
// No UART data available
return
;
}
// Read data to clear all status bits
uint8_t
uart_data
=
UDR0
;
if
(
uart_status
&
(
_BV
(
FE0
)
|
_BV
(
DOR0
)
|
_BV
(
UPE0
)))
{
// There was a UART error
return
;
}
uart_buffer_push
(
uart_data
);
}
uint8_t
uart_read
(
uint8_t
*
data
)
{
return
uart_buffer_pop
(
data
);
}
dac.c
deleted
100644 → 0
View file @
98512076
#include "dac.h"
#include "clamp_add.h"
#include "voice.h"
#include <avr/io.h>
#include <util/delay.h>
uint16_t
dac_pitch_delta
(
uint8_t
semitones
)
{
if
(
semitones
>=
NUM_SEMITONES
)
{
semitones
=
NUM_SEMITONES
-
1
;
}
return
dac_semitone_offset
(
semitones
);
}
uint8_t
spi_io
(
uint8_t
data
)
{
SPDR
=
data
;
loop_until_bit_is_set
(
SPSR
,
SPIF
);
return
SPDR
;
}
#define DAC_SYNC PORTB1
#define MOSI PORTB3
#define MISO PORTB4
#define SCK PORTB5
#define SS PORTB2