Upload files to "/"

This commit is contained in:
dev32std 2026-03-04 04:20:01 +00:00
commit cdddbfe92b
5 changed files with 390 additions and 18 deletions

34
LICENSE
View file

@ -1,16 +1,24 @@
MIT No Attribution
BSD 2-Clause License
Copyright <YEAR> <COPYRIGHT HOLDER>
Copyright (c) 2023, Trevor Hall
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,3 +1,63 @@
# CS9236
# CS9236 Library
A library for operating the Crystal CS9236 Wavetable Synthesizer chip.
The CS9236 library provides an interface for working with the Crystal CS9236 Wavetable Synthesizer Chip. It allows you to control and communicate with the chip using an Arduino or compatible microcontroller. This library provides an easy-to-use API for sending MIDI messages and commands to the Crystal CS9236.
## Installation
To use this library in your Arduino project, follow these steps:
1. In the Arduino IDE, click on "Sketch" -> "Include Library" -> "Add .ZIP Library" and select the downloaded ZIP file of the CS9236 library.
2. Now, you can include the library in your Arduino sketch by adding the following line at the top of your sketch:
#include <CS9236.h>
## Usage
To use the CS9236 library, you'll need to create an instance of the `CS9236` class and initialize it with the appropriate parameters. I've included an example project to help demonstrate this.
## API Reference
### Constructor
- `CS9236(uint8_t rs, Stream *comm)`: Initializes an instance of the CS9236 class with the specified reset pin (`rs`) and communication stream (`comm`).
### Basic MIDI Messages
- `NoteOn(uint8_t channel, uint8_t note_number, uint8_t velocity)`: Sends a Note On message.
- `NoteOff(uint8_t channel, uint8_t note_number)`: Sends a Note Off message.
- `ProgramChange(uint8_t channel, uint8_t program)`: Sends a Program Change message.
- `SetChannelPressure(uint8_t channel, uint8_t pressure)`: Sets the channel pressure.
- `SetPitchBend(uint8_t channel, uint16_t pitchbend)`: Sets the pitch bend.
- `SetModWheel(uint8_t channel, uint8_t depth)`: Sets the modulation wheel.
- `SetVolume(uint8_t channel, uint8_t volume)`: Sets the channel volume.
- `SetPan(uint8_t channel, uint8_t value)`: Sets the pan position.
- `SetExpression(uint8_t channel, uint8_t value)`: Sets the expression.
- `SetPedal(uint8_t channel, bool pedaldown)`: Sets the pedal.
- `SetReverb(uint8_t channel, uint8_t value)`: Sets the reverb level.
- `SetChorus(uint8_t channel, uint8_t value)`: Sets the chorus level.
### RPN Messages
- `SelectRPN(uint8_t channel, uint8_t rpn)`: Selects the RPN (Registered Parameter Number).
- `SetPitchBendSensitivity(uint8_t channel, uint16_t value)`: Sets the pitch bend sensitivity.
- `SetFineTuning(uint8_t channel, uint16_t value)`: Sets the fine tuning.
- `SetCoarseTuning(uint8_t channel, uint16_t value)`: Sets the coarse tuning.
### Channel Mode Messages
- `AllSoundsOff(uint8_t channel)`: Sends an All Sounds Off message.
- `ResetAll(uint8_t channel)`: Sends a Reset All Controllers message.
- `AllNotesOff(uint8_t channel)`: Sends an All Notes Off message.
### Special Functions
- `EnablePressureRecognition()`: Enables pressure recognition.
- `DisablePressureRecognition()`: Disables pressure recognition.
- `EnableTestTone()`: Enables the test tone.
- `DisableTestTone()`: Disables the test tone.
For detailed information on the usage of each function, please refer to the header file `CS9236.h`.
## License
This library is released under the BSD License. Please see the LICENSE file for more details.

153
cs9236.cpp Normal file
View file

@ -0,0 +1,153 @@
/*
* Copyright (C) 2023 Trevor Hall
* All rights reserved.
*
* This software may be modified and distributed under the terms
* of the BSD license. See the LICENSE file for details.
*/
#include <Arduino.h>
#include "cs9236.h"
CS9236::CS9236(uint8_t rs, Stream * comm) {
//constructor
_rs = rs;
_comm = comm;
}
void CS9236::Init(){
digitalWrite(_rs, LOW);
delay(1);
digitalWrite(_rs, HIGH);
}
void CS9236::Shutdown(){
digitalWrite(_rs, LOW);
}
void CS9236::SystemReset(){
_comm->write(CS_SYSTEM_RESET);
}
void CS9236::Poll(){ //For active sensing, if Active Sensing is on, either a note/message, or this polling message must be sent no later than 372 ms, or else the All sounds off and reset all controllers function will be executed
_comm->write(CS_ACTIVE_SENSE);
}
void CS9236::NoteOn(uint8_t channel, uint8_t note_number, uint8_t velocity){
if(channel > 15) return; //only channels 0 through 15 are allowed
uint8_t midipacket[3] = {CS_NOTE_ON | channel, note_number, velocity};;
_comm->write(midipacket, 4);
}
void CS9236::NoteOff(uint8_t channel, uint8_t note_number){
if(channel > 15) return;
uint8_t midipacket[3] = {CS_NOTE_OFF | channel, note_number, 0x00};
_comm->write(midipacket, 3);
}
void CS9236::ProgramChange(uint8_t channel, uint8_t program){
if(channel > 15) return;
uint8_t midipacket[2] = {CS_PROGRAM_CHANGE | channel, program};
_comm->write(midipacket, 2);
}
void CS9236::SetChannelPressure(uint8_t channel, uint8_t pressure){
if(channel > 15) return;
uint8_t midipacket[2] = {CS_CHANNEL_PRESSURE | channel, pressure};
_comm->write(midipacket, 2);
}
void CS9236::SetPitchBend(uint8_t channel, uint16_t pitchbend){
if(channel > 15) return;
uint8_t midipacket[3] = {CS_PITCH_BEND | channel, (pitchbend >> 8) & 0xff, pitchbend & 0xff};
_comm->write(midipacket, 3);
}
void CS9236::SetModWheel(uint8_t channel, uint8_t depth){
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_CC_MODWHEEL, depth};
_comm->write(midipacket, 3);
}
void CS9236::SetVolume(uint8_t channel, uint8_t volume){
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_CC_VOLUME, volume};
_comm->write(midipacket, 3);
}
void CS9236::SetPan(uint8_t channel, uint8_t value) {//0 - left, 64 - center, 127 - right
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_CC_PAN, value};
_comm->write(midipacket, 3);
}
void CS9236::SetExpression(uint8_t channel, uint8_t value){
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_CC_EXPRESSION, value};
_comm->write(midipacket, 3);
}
void CS9236::SetPedal(uint8_t channel, bool pedaldown){ // 0 through 63 = off, 64 through 127 =on | Sostenuto
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_CC_PEDAL, pedaldown ? 0x7f : 0x00};
_comm->write(midipacket, 3);
}
void CS9236::SetReverb(uint8_t channel, uint8_t value){
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_CC_REVERB, value};
_comm->write(midipacket, 3);
}
void CS9236::SetChorus(uint8_t channel, uint8_t value){
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_CC_CHORUS, value};
_comm->write(midipacket, 3);
}
//RPN messages
void CS9236::SelectRPN(uint8_t channel, uint8_t rpn){
if(channel > 15) return;
uint8_t msbpacket[3] = {0xB0 | channel, 0x65, 0x00};
_comm->write(msbpacket, 3);
uint8_t lsbpacket[3] = {0xB0 | channel, 0x64, rpn};
_comm->write(lsbpacket, 3);
}
void CS9236::SetPitchBendSensitivity(uint8_t channel, uint16_t value){
SelectRPN(channel, CS_RPN_PITCHBEND_SENSITIVITY);
uint8_t msbpacket[3] = {0xB0 | channel, CS_CC_RPN_MSB, (value >> 8)};
_comm->write(msbpacket, 3);
uint8_t lsbpacket[3] = {0xB0 | channel, CS_CC_RPN_LSB, value & 0xff};
_comm->write(lsbpacket, 3);
}
void CS9236::SetFineTuning(uint8_t channel, uint16_t value){
SelectRPN(channel, CS_RPN_FINE_TUNING);
uint8_t msbpacket[3] = {0xB0 | channel, CS_CC_RPN_MSB, (value >> 8)};
_comm->write(msbpacket, 3);
uint8_t lsbpacket[3] = {0xB0 | channel, CS_CC_RPN_LSB, value & 0xff};
_comm->write(lsbpacket, 3);
}
void CS9236::SetCoarseTuning(uint8_t channel, uint16_t value){
SelectRPN(channel, CS_RPN_COARSE_TUNING);
uint8_t msbpacket[3] = {0xB0 | channel, CS_CC_RPN_MSB, (value >> 8)};
_comm->write(msbpacket, 3);
uint8_t lsbpacket[3] = {0xB0 | channel, CS_CC_RPN_LSB, value & 0xff};
_comm->write(lsbpacket, 3);
}
//channel mode messages
void CS9236::AllSoundsOff(uint8_t channel){
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_ALL_SOUNDS_OFF, 0x00};
_comm->write(midipacket, 3);
}
void CS9236::ResetAll(uint8_t channel){
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_RESET_ALL, 0x00};
_comm->write(midipacket, 3);
}
void CS9236::AllNotesOff(uint8_t channel){
if(channel > 15) return;
uint8_t midipacket[3] = {0xB0 | channel, CS_ALL_NOTES_OFF, 0x00};
_comm->write(midipacket, 3);
}
void CS9236::EnablePressureRecognition(){
uint8_t midipacket[8] = {0xF0, 0x00, 0x01, 0x02, 0x01, 0x01, 0x01, 0xF7};
_comm->write(midipacket, 8);
}
void CS9236::DisablePressureRecognition(){
uint8_t midipacket[8] = {0xF0, 0x00, 0x01, 0x02, 0x01, 0x01, 0x02, 0xF7};
_comm->write(midipacket, 8);
}
void CS9236::EnableTestTone(){
uint8_t midipacket[8] = {0xF0, 0x00, 0x01, 0x02, 0x01, 0x01, 0x03, 0xF7};
_comm->write(midipacket, 8);
}
void CS9236::DisableTestTone(){
uint8_t midipacket[8] = {0xF0, 0x00, 0x01, 0x02, 0x01, 0x01, 0x04, 0xF7};
_comm->write(midipacket, 8);
}

86
cs9236.h Normal file
View file

@ -0,0 +1,86 @@
/*
* Copyright (C) 2023 Trevor Hall
* All rights reserved.
*
* This software may be modified and distributed under the terms
* of the BSD license. See the LICENSE file for details.
*/
#include "Stream.h"
#ifndef CS9236_H_
#define CS9236_H_
#define CS_ACTIVE_SENSE 0xFE
#define CS_SYSTEM_RESET 0xFF
#define CS_NOTE_ON 0x90
#define CS_NOTE_OFF 0x80
#define CS_VOLUME 0xB0
#define CS_PROGRAM_CHANGE 0xC0
#define CS_CHANNEL_PRESSURE 0xD0
#define CS_PITCH_BEND 0xE0
#define CS_CC_MODWHEEL 0x01
#define CS_CC_RPN_MSB 0x06
#define CS_CC_RPN_LSB 0x26
#define CS_CC_VOLUME 0x07
#define CS_CC_PAN 0x0A
#define CS_CC_EXPRESSION 0x0B
#define CS_CC_PEDAL 0x40
#define CS_CC_REVERB 0x5B
#define CS_CC_CHORUS 0x5D
#define CS_RPN_PITCHBEND_SENSITIVITY 0x00
#define CS_RPN_FINE_TUNING 0x01
#define CS_RPN_COARSE_TUNING 0x02
#define CS_ALL_SOUNDS_OFF 0x78
#define CS_RESET_ALL 0x79
#define CS_ALL_NOTES_OFF 0x7B
#define CS_OMNI_MODE_OFF 0x7C
#define CS_OMNI_MODE_ON 0x7D
#define CS_MONO_MODE_ON 0x7E
#define CS_POLY_MODE_ON 0x7F
class CS9236 {
public:
CS9236(uint8_t rs, Stream * comm);
void Init();
void Shutdown();
void SystemReset();
void Poll();
void NoteOn(uint8_t channel, uint8_t note_number, uint8_t velocity);
void NoteOff(uint8_t channel, uint8_t note_number);
void ProgramChange(uint8_t channel, uint8_t program);
void SetChannelPressure(uint8_t channel, uint8_t pressure);
void SetPitchBend(uint8_t channel, uint16_t pitchbend);
void SetModWheel(uint8_t channel, uint8_t depth);
void SetVolume(uint8_t channel, uint8_t volume);
void SetPan(uint8_t channel, uint8_t value); //0 - left, 64 - center, 127 - right
void SetExpression(uint8_t channel, uint8_t value);
void SetPedal(uint8_t channel, bool pedaldown); // 0 through 63 = off, 64 through 127 =on | Sostenuto
void SetReverb(uint8_t channel, uint8_t value);
void SetChorus(uint8_t channel, uint8_t value);
void SelectRPN(uint8_t channel, uint8_t rpn);
void SetPitchBendSensitivity(uint8_t channel, uint16_t value); //RPN messages
void SetFineTuning(uint8_t channel, uint16_t value);
void SetCoarseTuning(uint8_t channel, uint16_t value);
//channel mode messages
void AllSoundsOff(uint8_t channel);
void ResetAll(uint8_t channel);
void AllNotesOff(uint8_t channel);
void EnablePressureRecognition();//F0H 00H 01H 02H 01H 01H 01H F7H
void DisablePressureRecognition();//F0H 00H 01H 02H 01H 01H 02H F7H
void EnableTestTone();//F0H 00H 01H 02H 01H 01H 03H F7H
void DisableTestTone();//F0H 00H 01H 02H 01H 01H 04H F7H
private:
uint8_t _rs;
Stream * _comm;
};
#endif

View file

@ -0,0 +1,65 @@
#include <Arduino.h>
#include <cs9236.h>
//The reset pin for the Wavetable Chip. I use pin 7 in my projects
#define wavetable_rs 7
#define rhythm_width 16
CS9236 wavetableChip(wavetable_rs, &Serial1);
//For the simple drum machine loop.
const uint32_t rhythm_frames[rhythm_width] = {0x3c000030, 0x00000000, 0x00000010, 0x00000000, 0x00e00030, 0x00000000, 0x00000020, 0x0000002c, 0x00000034, 0x00000000, 0x34000024, 0x00000000, 0x00f00034, 0x00000000, 0x00000024, 0x2c000000};
byte master_volume = 127;
byte rhythm_ticks = 0;
uint32_t rhythm_waitmicros = 150000;
uint32_t last_sequencer_update = 0;
byte note_values[5] = {42,51,47,38,36};
//Updates the sequencer/drum machine, if percussion events are triggered for the current from it will send that information to the Wavetable chip
void updateSequencer(uint32_t microseconds){
//Only increment the rhythm frame if enough time has passed
if(microseconds - last_sequencer_update > rhythm_waitmicros){
last_sequencer_update = microseconds;
//Establish which frame we are on in the drum groove
byte frameindex = rhythm_ticks++ % rhythm_width;
uint32_t current_frame = rhythm_frames[frameindex] & 0x3fffffff; //clear top 2 bits
// if a frame is 0, that means nothing happens at all, so we can skip it
if(current_frame > 0){
//I store my frames as 5 values with 6 bits each, this fits into a 32 bit unsigned integer leaving the most significant 2 bits blank, I currently do not use those bits for any purpose
for(int i = 0; i < 5; i++){
//extract the 6 bit value for this instrument, processing only non-zero values
byte val6 = (current_frame >> (i * 6)) & 0x3f;
if(val6 != 0) {
float velo = ((float)val6) / 63.0;
byte midivelo = constrain(127.0 * (velo), 0, 127);
//Trigger the percussion instrument with the specified velocity. We do not need a corresponding "NoteOff" call since this is percussion
wavetableChip.NoteOn(0x09, note_values[i], midivelo);
}
}
}
}
}
void setup() {
//Set the appropriate pinmode for the reset pin
pinMode(wavetable_rs, OUTPUT);
//Begin communication through Serial, I use Serial1 assuming we want Serial to be free and clear for debugging or other uses
Serial1.begin(31250);
while(!Serial1) delayMicroseconds(10);
//Set up the wavetable chip
wavetableChip.Init();
//Reset all values on Midi Channel 10, please note that while this value is "9", since Midi Channel 1 is "0", Midi Channel 10 would be "9"
wavetableChip.ResetAll(0x09);
//Sets the volume of Channel 10 to master_volume, 0 is silent and 127 is maximum loudness
wavetableChip.SetVolume(0x09, constrain(master_volume, 0, 127));
}
void loop() {
//pumps the drum-machine loop
updateSequencer(micros());
}