Commit b1b08dbb authored by Snorre Nilssen Vestli's avatar Snorre Nilssen Vestli

wipe and add tesla-midi-19

parent 1442109f
/*
* MIDI.c
*
* Created: 19.08.2014 23:32:07
* Author: Snorre
*/
#include <inttypes.h>
#include "MIDI.h"
#include "MIDIdefs.h"
#include "panic.h"
#include "tesladefs.h"
void midi_add_note(uint8_t note, uint8_t velocity);
void midi_clear_note(uint8_t note, uint8_t velocity);
note_t* midi_notes;
midi_channel_t* midi_channel;
char midi_buffer[3];
uint8_t midi_current_byte;
enum {
FREE,
NOTE_ON,
NOTE_OFF,
IGNORE
} midi_state;
void midi_init(uint8_t channel, note_t* notes_struct, midi_channel_t* channel_struct)
{
midi_notes = notes_struct;
midi_channel = channel_struct;
midi_channel->active_notes = 0;
midi_channel->channel = channel;
midi_current_byte = 0;
for(uint8_t i = 0; i << TESLA_MAX_CHANNELS; i++){
midi_notes[i].age = 0;
midi_notes[i].change = 0;
midi_notes[i].update = 0;
midi_notes[i].on = 0;
midi_notes[i].tesla_channel = i;
}
}
uint8_t parse_status( char input ) {
// Status byte?
if( input & MIDI_STATUS_bm ) {
if( input == MIDI_clock_gc ) {
// TODO: Implement midi clock handling
}
// Check that they want to speak to us
else if( ( input & MIDI_CHANNEL_MASK ) == midi_channel->channel ) {
switch ( input & MIDI_STATUS_MASK ) {
case MIDI_note_off_gc:
midi_state = NOTE_OFF;
midi_current_byte = 0;
break;
case MIDI_note_on_gc:
midi_state = NOTE_ON;
midi_current_byte = 0;
break;
default:
// Not supported yet
midi_state = IGNORE;
midi_current_byte = 0;
break;
}
}
else {
// Not to us, ignore
midi_state = IGNORE;
midi_current_byte = 0;
}
// Return 1 cause we intercepted a status byte
return 1;
}
else {
// Not a status byte
return 0;
}
}
void parse_midi(char input)
{
switch (midi_state)
{
case FREE:
case IGNORE:
//check channel
parse_status( input );
break;
case NOTE_ON:
// Check for status bytes
if( parse_status( input ) ) {
break;
}
if (!(input & MIDI_STATUS_bm)){
if (midi_current_byte == 1){
//store note
midi_buffer[midi_current_byte++] = input;
} else if (midi_current_byte == 2) {
//store velocity
midi_buffer[midi_current_byte++] = input;
// Zero-velocity input == NOTE_OFF
if( input == 0 ) {
midi_clear_note( midi_buffer[0], 127 ); // Release with full force
}
else {
//find free channel & update
midi_add_note(midi_buffer[0],midi_buffer[1]);
}
// Get ready for next note
midi_current_byte = 0;
} else {
tesla_panic();
}
} else {
//evaluate if single-byte-interrupting package, else abort
}
break;
case NOTE_OFF:
// Check for status bytes
if( parse_status( input ) ) {
break;
}
if (!(input & MIDI_STATUS_bm)){
if (midi_current_byte == 1){
//store note
midi_buffer[midi_current_byte++] = input;
} else if (midi_current_byte == 2) {
//store velocity
midi_buffer[midi_current_byte++] = input;
midi_clear_note(midi_buffer[0],midi_buffer[1]);
// Get ready for next note
midi_current_byte = 0;
} else {
tesla_panic();
}
} else {
//check for interrupting frame, panic otherwise
}
break;
default:
// WTF? Unknown state
tesla_panic();
}
}
void midi_add_note(uint8_t note, uint8_t velocity){
uint8_t n, result = 0, max_age = 0;
//check if note already is on
for (n = 0; n < TESLA_MAX_CHANNELS; n++){
if ((midi_notes[n].note == note) && (midi_notes[n].on)){
midi_notes[n].velocity = velocity;
midi_notes[n].update = 1;
return;
}
}
//find free channel
for (n = 0; n < TESLA_MAX_CHANNELS; n++){
if (!(midi_notes[n].on)){
midi_notes[n].on = 1;
midi_notes[n].note = note;
midi_notes[n].velocity = velocity;
midi_notes[n].change = 1;
return;
}
}
//if no free channels, find oldest note and overwrite
for (n = 0; n < TESLA_MAX_CHANNELS; n++){
if (midi_notes[n].age > max_age){
result = n;
max_age = midi_notes[n].age;
}
}
midi_notes[result].on = 1;
midi_notes[result].note = note;
midi_notes[result].velocity = velocity;
midi_notes[result].change = 1;
}
void midi_clear_note(uint8_t note, uint8_t velocity){
uint8_t n;
//find note
for (n = 0; n < TESLA_MAX_CHANNELS; n++){
if ((midi_notes[n].note == note) && (midi_notes[n].on)){
midi_notes[n].on = 0;
midi_notes[n].velocity = velocity;
midi_notes[n].change = 1;
return;
}
}
}
/*
* MIDI.h
*
* Created: 19.08.2014 23:34:37
* Author: Snorre
*/
#ifndef MIDI_H_
#define MIDI_H_
typedef struct note_struct{
uint8_t change;
uint8_t update;
uint8_t note;
uint8_t velocity;
uint8_t on;
uint8_t age;
uint8_t tesla_channel;
} note_t;
typedef struct midi_channel_struct{
uint8_t channel;
uint8_t active_notes;
} midi_channel_t;
void midi_init(uint8_t channel, note_t* notes_struct, midi_channel_t* channel_struct);
void parse_midi(char input);
uint8_t parse_status( char input );
#endif /* MIDI_H_ */
\ No newline at end of file
/*
* MIDIdefs.h
*
* Created: 24.08.2014 22:52:23
* Author: Snorre
*/
#ifndef MIDIDEFS_H_
#define MIDIDEFS_H_
#define MIDI_STATUS_bm (1 << 7)
#define MIDI_STATUS_MASK (0b1111 << 4)
#define MIDI_CHANNEL_MASK (0b1111)
#define MIDI_note_on_gc (0b1001 << 4)
#define MIDI_note_off_gc (0b1000 << 4)
#define MIDI_aftertouch_gc ( 0b1010 << 4 )
#define MIDI_clock_gc (0xf8)
#endif /* MIDIDEFS_H_ */
/*
* TeslaMidi.c
*
* Created: 18.08.2014 21:05:01
* Author: SNV
*/
#include <avr/io.h>
#include "MIDI.h"
#include "panel_io.h"
#include "watchdog.h"
#include "clock.h"
#include "uart.h"
#include "tesla_io.h"
#include "tesladefs.h"
int main(void)
{
uint8_t error;
char uartchar;
note_t notes[TESLA_MAX_CHANNELS];
midi_channel_t midi_channel;
//TESLA IO SAFE
tesla_init();
clock_init();
//PANEL IO INIT
panel_init();
//midi UART init
uart_init();
//WATCHDOG INIT
error = watchdog_init();
if (error){
//watchdog tripped, set IO appropriately and hold.
panel_set_led_on(PANEL_ERROR);
}
//MIDI INIT
midi_init(1,notes,&midi_channel);
//WAIT FOR READY
panel_wait_run();
//WATCHDOG_START
watchdog_start();
//CARRIER_START
for (uint8_t i = 0; i < 2 ; i++){
tesla_channel_enable(i);
}
tesla_carrier_enable();
while(1)
{
//READ PANEL IO
panel_update();
if (uart_read(&uartchar)){
//POLL MIDI USART
parse_midi(uartchar);
//HANDLE WAVE IO
for (uint8_t i = 0; i < TESLA_MAX_CHANNELS; i++){
if (notes[i].change){
notes[i].change = 0;
if (notes[i].on){
// turn on relevant channel
tesla_channel_play(notes[i].tesla_channel, notes[i].note, panel_get_level());
} else {
//turn off relevant channel
tesla_channel_stop(notes[i].tesla_channel);
}
} /*else if (notes[i].update){
//TODO: Handle renewed velocity
}*/
}
}
// TODO: handle updated level pot
watchdog_pet();
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProjectVersion>6.2</ProjectVersion>
<ToolchainName>com.Atmel.AVRGCC8.C</ToolchainName>
<ProjectGuid>{e2ecc8a3-d5c6-44f6-bee0-23a8d5846b0a}</ProjectGuid>
<avrdevice>ATxmega64A4U</avrdevice>
<avrdeviceseries>none</avrdeviceseries>
<OutputType>Executable</OutputType>
<Language>C</Language>
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
<AssemblyName>TeslaMidi</AssemblyName>
<Name>TeslaMidi</Name>
<RootNamespace>TeslaMidi</RootNamespace>
<ToolchainFlavour>Native</ToolchainFlavour>
<KeepTimersRunning>true</KeepTimersRunning>
<OverrideVtor>false</OverrideVtor>
<OverrideVtorValue>exception_table</OverrideVtorValue>
<eraseonlaunchrule>0</eraseonlaunchrule>
<AsfVersion>3.1.3</AsfVersion>
<CacheFlash>true</CacheFlash>
<ProgFlashFromRam>true</ProgFlashFromRam>
<RamSnippetAddress>0x20000000</RamSnippetAddress>
<UncachedRange />
<BootSegment>2</BootSegment>
<AsfFrameworkConfig>
<framework-data>
<options />
<configurations />
<files />
<documentation help="" />
<offline-documentation help="" />
<dependencies>
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.6.0" />
</dependencies>
</framework-data>
</AsfFrameworkConfig>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<ToolchainSettings>
<AvrGcc xmlns="">
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcc.linker.libraries.Libraries>
</AvrGcc>
</ToolchainSettings>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<ToolchainSettings>
<AvrGcc xmlns="">
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcc.linker.libraries.Libraries>
<avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel>
</AvrGcc>
</ToolchainSettings>
</PropertyGroup>
<ItemGroup>
<Compile Include="clock.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="clock.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="MIDI.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="MIDI.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="MIDIdefs.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="miditable.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="panel_io.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="panel_io.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="panic.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="tesladefs.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="TeslaMidi.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="teslaarrays.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="tesla_io.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="tesla_io.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="uart.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="watchdog.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="watchdog.h">
<SubType>compile</SubType>
</Compile>
</ItemGroup>
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
</Project>
\ No newline at end of file
/*
* clock.c
*
* Created: 25.08.2014 21:40:20
* Author: Snorre
*/
#include <avr/io.h>
void clock_init()
{
OSC.CTRL |= OSC_RC32MEN_bm;
while (!(OSC.STATUS & OSC_RC32MRDY_bm));
CCP = CCP_IOREG_gc;
CLK.CTRL = CLK_SCLKSEL_RC32M_gc;
}
\ No newline at end of file
/*
* clock.h
*
* Created: 25.08.2014 21:40:28
* Author: Snorre
*/
#ifndef CLOCK_H_
#define CLOCK_H_
void clock_init();
#endif /* CLOCK_H_ */
\ No newline at end of file
/*
* miditable.h
*
* Created: 24.08.2014 15:35:37
* Author: Snorre
*/
#ifndef MIDITABLE_H_
#define MIDITABLE_H_
const uint16_t midi_per[128] = {
61156,
57724,
54484,
51426,
48540,
45815,
43244,
40817,
38526,
36364,
34323,
32396,
30578,
28862,
27242,
25713,
24270,
22908,
21622,
20408,
19263,
18182,
17161,
16198,
15289,
14431,
13621,
12856,
12135,
11454,
10811,
10204,
9631,
9091,
8581,
8099,
7645,
7215,
6810,
6428,
6067,
5727,
5405,
5102,
4816,
4545,
4290,
4050,
3822,
3608,
3405,
3214,
3034,
2863,
2703,
2551,
2408,
2273,
2145,
2025,
1911,
1804,
1703,
1607,
1517,
1432,
1351,
1276,
1204,
1136,
1073,
1012,
956,
902,
851,
804,
758,
716,
676,
638,
602,
568,
536,
506,
478,
451,
426,
402,
379,
358,
338,
319,
301,
284,
268,
253,
239,
225,
213,
201,
190,
179,
169,
159,
150,
142,
134,
127,
119,
113,
106,
100,
95,
89,
84,
80,
75,
71,
67,
63,
60,
56,
53,
50,
47,
45,
42,
40
};
/* Table giving max CC values for note channels given (T_on < 700us) and (T_on < per/2),
* and given 500khz timer clock
*
*/
const uint16_t midi_note_max[128]= {
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,
350,