This commit is contained in:
y4my4my4m 2023-09-04 00:13:36 +09:00
parent d05b7dbb7e
commit 77bcef3667
7 changed files with 250 additions and 36 deletions

20
src/Home/Tracker/Classes.ZC Normal file → Executable file
View file

@ -1,3 +1,10 @@
#define INSTRUMENT_NONE 0
#define PULSE1 1
#define PULSE2 2
#define TRIANGLE 3
#define NOISE 4
#define SAMPLE 5
class NoteCell { class NoteCell {
U8 note; // MIDI note number U8 note; // MIDI note number
U8 velocity; // Volume/Intensity U8 velocity; // Volume/Intensity
@ -5,7 +12,7 @@ class NoteCell {
U8 effect; // Future expansion for effects U8 effect; // Future expansion for effects
}; };
#define TRACK_LENGTH 64 // Length of each pattern in rows #define TRACK_LENGTH 10 // Length of each pattern in rows
class Pattern { class Pattern {
NoteCell cells[TRACK_LENGTH]; NoteCell cells[TRACK_LENGTH];
}; };
@ -19,3 +26,14 @@ class Song {
#define SAMPLE_RATE 44100 #define SAMPLE_RATE 44100
#define PI 3.1415926535897932 // i know i can just use the pi symbol #define PI 3.1415926535897932 // i know i can just use the pi symbol
// globals
U8 *gSampleData = NULL; // Global sample buffer
I64 gSampleSize = 0; // Size of the loaded sample
U0 CleanupWaveformGen() {
if (gSampleData) {
Free(gSampleData);
gSampleData = NULL; // Reset the pointer
}
}

View file

@ -27,6 +27,4 @@ include_noreindex "Lib/ELF64";
AutoComplete(0); AutoComplete(0);
MusicTracker; MusicTracker;
//InitPlayer;

0
src/Home/Tracker/MIDIHandling.ZC Normal file → Executable file
View file

View file

@ -1,26 +1,37 @@
#include "MIDIHandling" #include "MIDIHandling"
#include "WaveformGen" #include "WaveformGen"
U0 ApplyEnvelope(U32 *buffer, I64 length) { U0 AudioPlayNote(U8 note, U8 velocity, U8 instrument) {
I64 fadeLength = length * 0.1; // 10% fade in and fade out
I64 i;
for (i = 0; i < fadeLength; i++) {
F64 factor = ToI64(i / fadeLength);
buffer[i] = ToI64(buffer[i] * factor);
buffer[length - i - 1] = ToI64(buffer[length - i - 1] * factor);
}
}
U0 AudioPlayNote(U8 note, U8 velocity) {
U32 buffer[SAMPLE_RATE]; U32 buffer[SAMPLE_RATE];
F64 freq = MidiToFreq(note); F64 freq = MidiToFreq(note);
// GenerateSineWave(buffer, SAMPLE_RATE, freq, velocity);
switch(instrument) {
case PULSE1:
GeneratePulse1Wave(buffer, SAMPLE_RATE, freq, velocity);
break;
case PULSE2:
GeneratePulse2Wave(buffer, SAMPLE_RATE, freq, velocity);
break;
case TRIANGLE:
GenerateTriangleWave(buffer, SAMPLE_RATE, freq, velocity);
break;
case NOISE:
GenerateNoiseWave(buffer, SAMPLE_RATE, freq, velocity);
break;
case SAMPLE:
PlaySample(buffer, SAMPLE_RATE, velocity);
break;
default:
GenerateSineWave(buffer, SAMPLE_RATE, freq, velocity); GenerateSineWave(buffer, SAMPLE_RATE, freq, velocity);
ApplyEnvelope(buffer, SAMPLE_RATE); // TODO: Apply fade in and fade out break;
}
//ApplyEnvelope(buffer, SAMPLE_RATE); // TODO: Apply fade in and fade out
// Play the buffer using the AC97 driver: // Play the buffer using the AC97 driver:
AudioSFXPlay(buffer, SAMPLE_RATE); AudioSFXPlay(buffer, SAMPLE_RATE);
"$$YELLOW$$%d$$FG$$ ", note; "Instrument: $$LTGREEN$$ %d $$DKGRAY$$|$$FG$$ Note: $$YELLOW$$%d$$FG$$\n", instrument, note;
} }
U0 EnterPattern(Pattern *pattern) { U0 EnterPattern(Pattern *pattern) {
@ -43,53 +54,60 @@ U0 PlayPattern(Pattern *pattern) {
for (row = 0; row < TRACK_LENGTH; row++) { for (row = 0; row < TRACK_LENGTH; row++) {
cell = &pattern->cells[row]; cell = &pattern->cells[row];
if (cell->note) { if (cell->note) {
AudioPlayNote(cell->note, cell->velocity); AudioPlayNote(cell->note, cell->velocity, cell->instrument);
} }
Sleep(900); // Adjust for tempo Sleep(2900); // Adjust for tempo
} }
} }
U0 MusicTracker() { U0 MusicTracker() {
LoadSample("~/Tracker/Samples/Sample.WAV");
Song song; Song song;
song.patterns[0].cells[0].note = 60; // C3 song.patterns[0].cells[0].note = 60; // C3
song.patterns[0].cells[0].velocity = 100; song.patterns[0].cells[0].velocity = 100;
song.patterns[0].cells[0].instrument = SAMPLE;
song.patterns[0].cells[1].note = 62; // D song.patterns[0].cells[1].note = 60; // C3
song.patterns[0].cells[1].velocity = 100; song.patterns[0].cells[1].velocity = 100;
song.patterns[0].cells[1].instrument = PULSE1;
song.patterns[0].cells[2].note = 64; // E song.patterns[0].cells[2].note = 62; // D
song.patterns[0].cells[2].velocity = 100; song.patterns[0].cells[2].velocity = 100;
song.patterns[0].cells[2].instrument = PULSE1;
song.patterns[0].cells[3].note = 60; // C song.patterns[0].cells[3].note = 64; // E
song.patterns[0].cells[3].velocity = 100; song.patterns[0].cells[3].velocity = 100;
song.patterns[0].cells[3].instrument = PULSE1;
song.patterns[0].cells[4].note = 62; // D song.patterns[0].cells[4].note = 60; // C
song.patterns[0].cells[4].velocity = 100; song.patterns[0].cells[4].velocity = 100;
song.patterns[0].cells[4].instrument = PULSE2;
song.patterns[0].cells[5].note = 64; // E song.patterns[0].cells[5].note = 62; // D
song.patterns[0].cells[5].velocity = 100; song.patterns[0].cells[5].velocity = 100;
song.patterns[0].cells[5].instrument = PULSE2;
song.patterns[0].cells[6].note = 60; // C song.patterns[0].cells[6].note = 60; // C
song.patterns[0].cells[6].velocity = 100; song.patterns[0].cells[6].velocity = 100;
song.patterns[0].cells[6].instrument = TRIANGLE;
song.patterns[0].cells[7].note = 62; // D song.patterns[0].cells[7].note = 62; // D
song.patterns[0].cells[7].velocity = 100; song.patterns[0].cells[7].velocity = 100;
song.patterns[0].cells[7].instrument = TRIANGLE;
song.patterns[0].cells[8].note = 64; // E song.patterns[0].cells[8].note = 64; // E
song.patterns[0].cells[8].velocity = 100; song.patterns[0].cells[8].velocity = 100;
song.patterns[0].cells[8].instrument = TRIANGLE;
song.patterns[0].cells[9].note = 65; // F song.patterns[0].cells[9].note = 102;
song.patterns[0].cells[9].velocity = 100; song.patterns[0].cells[9].velocity = 100;
song.patterns[0].cells[9].instrument = NOISE;
song.patterns[0].cells[10].note = 72; // C4
song.patterns[0].cells[10].velocity = 100;
song.patterns[0].cells[11].note = 76; // E4
song.patterns[0].cells[11].velocity = 100;
song.patterns[0].cells[12].note = 36; // C1
song.patterns[0].cells[12].velocity = 100;
// Clear(&song); // Clear(&song);
I64 sc; I64 sc;
@ -116,4 +134,5 @@ U0 MusicTracker() {
Print("Invalid choice.\n"); Print("Invalid choice.\n");
} }
} }
CleanupWaveformGen(); // Free the sample buffer
} }

Binary file not shown.

0
src/Home/Tracker/UITracker.ZC Normal file → Executable file
View file

181
src/Home/Tracker/WaveformGen.ZC Normal file → Executable file
View file

@ -1,7 +1,7 @@
U0 GenerateSineWave(U32 *buffer, I64 length, F64 freq, U8 velocity) { U0 GenerateSineWave(U32 *buffer, I64 length, F64 freq, U8 velocity) {
I64 i; I64 i;
F64 amplitude = (ToF64(velocity) / 127.0) * 0x7FFF; // Adjust the amplitude based on velocity F64 amplitude = (ToF64(velocity) / 127.0) * 0x7FFF; // Adjust the amplitude based on velocity
F64 phase_increment = (2.0 * PI * freq) / 44100.0; // Assuming a sample rate of 44.1kHz F64 phase_increment = (2.0 * PI * freq) / SAMPLE_RATE; // Assuming a sample rate of 44.1kHz
F64 phase = 0.0; F64 phase = 0.0;
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
@ -12,6 +12,185 @@ U0 GenerateSineWave(U32 *buffer, I64 length, F64 freq, U8 velocity) {
} }
} }
U0 GeneratePulse1Wave(U32 *buffer, I64 length, F64 freq, U8 velocity) {
I64 i;
F64 amplitude = (ToF64(velocity) / 127.0) * 0x7FFF;
F64 phase_increment = (2.0 * PI * freq) / SAMPLE_RATE;
F64 phase = 0.0;
for (i = 0; i < length; i++) {
I16 sample_value;
if (phase < PI) {
sample_value = ToI64(amplitude);
} else {
sample_value = -ToI64(amplitude);
}
buffer[i] = (sample_value << 16) | (sample_value & 0xFFFF);
phase += phase_increment;
if (phase >= 2.0 * PI) phase -= 2.0 * PI;
}
}
U0 GeneratePulse2Wave(U32 *buffer, I64 length, F64 freq, U8 velocity) {
I64 i;
F64 amplitude = (ToF64(velocity) / 127.0) * 0x7FFF;
F64 phase_increment = (2.0 * PI * freq) / SAMPLE_RATE;
F64 phase = 0.0;
for (i = 0; i < length; i++) {
I16 sample_value;
if (phase < 0.5 * PI) {
sample_value = -ToI64(amplitude);
} else {
sample_value = ToI64(amplitude);
}
buffer[i] = (sample_value << 16) | (sample_value & 0xFFFF);
phase += phase_increment;
if (phase >= 2.0 * PI) phase -= 2.0 * PI;
}
}
U0 GenerateTriangleWave(U32 *buffer, I64 length, F64 freq, U8 velocity) {
I64 i;
F64 amplitude = (ToF64(velocity) / 127.0) * 0x7FFF;
F64 phase_increment = (2.0 * PI * freq) / SAMPLE_RATE;
F64 phase = 0.0;
for (i = 0; i < length; i++) {
I16 sample_value;
if (phase < PI) {
sample_value = ToI64((phase / PI) * amplitude);
} else {
sample_value = ToI64((2.0 - (phase / PI)) * amplitude);
}
buffer[i] = (sample_value << 16) | (sample_value & 0xFFFF);
phase += phase_increment;
if (phase >= 2.0 * PI) phase -= 2.0 * PI;
}
}
U0 GenerateNoiseWave(U32 *buffer, I64 length, F64 freq, U8 velocity) {
I64 i;
F64 amplitude = (ToF64(velocity) / 127.0) * 0x7FFF;
F64 phase_increment = (2.0 * PI * freq) / SAMPLE_RATE;
F64 phase = 0.0;
for (i = 0; i < length; i++) {
I16 sample_value = ToI64((RandI64 / 0x7FFFFFFF) * amplitude);
buffer[i] = (sample_value << 16) | (sample_value & 0xFFFF);
phase += phase_increment;
if (phase >= 2.0 * PI) phase -= 2.0 * PI;
}
}
U0 ApplyEnvelope(U32 *buffer, I64 length) {
I64 i;
for (i = 0; i < length; i++) {
I64 sample = buffer[i];
I64 left = sample >> 16;
I64 right = sample & 0xFFFF;
left = (left * i) / length;
right = (right * (length - i)) / length;
buffer[i] = (left << 16) | (right & 0xFFFF);
}
}
// U0 ApplyEnvelope(U32 *buffer, I64 length) {
// I64 fadeLength = length * 0.1; // 10% fade in and fade out
// I64 i;
// for (i = 0; i < fadeLength; i++) {
// F64 factor = ToI64(i / fadeLength);
// buffer[i] = ToI64(buffer[i] * factor);
// buffer[length - i - 1] = ToI64(buffer[length - i - 1] * factor);
// }
// }
class WAVHeader {
U8 riff[4]; // RIFF string
U32 overall_size; // overall size of file in bytes
U8 wave[4]; // WAVE string
U8 fmt_chunk[4]; // fmt string with trailing null char
U32 length; // length of format data. Should be 16 for PCM
U16 format_type; // format type. 1 for PCM
U16 channels; // number of channels
U32 sample_rate; // sampling rate (blocks per second)
U32 byterate; // SampleRate * NumChannels * BitsPerSample/8
U16 block_align; // channels * bits/sample / 8
U16 bits_per_sample;// bits per sample, 8- 8bits, 16- 16 bits etc
};
U0 LoadSample(U8 *filename) {
//WAVHeader header;
I64 fileSize;
// Free any previously loaded sample
if (gSampleData) {
Free(gSampleData);
}
gSampleSize = fileSize - sizeof(WAVHeader);
//gSampleData = MAlloc(gSampleSize);
//U8 *buffer = FileRead(filename, &fileSize);
//if (buffer)
//{
// MemCopy(&header, buffer, sizeof(WAVHeader));
// Print("Got: %c %c %c %c\n", header.riff[0], header.riff[1], header.riff[2], header.riff[3]);
// Basic validation - check the RIFF and WAVE tags
// if (!StrCompare("RIFF", header.riff) || !StrCompare("WAVE", header.wave)) {
// Print("Invalid WAV file.\n");
// Free(buffer);
// return;
// }
//} else {
// Print("Failed to read the file.\n");
//}
//U8 *fileContent = FileRead(filename, &fileSize);
//U8 *audioDataStart = fileContent + sizeof(WAVHeader); // Move pointer after header
//MemCopy(gSampleData, audioDataStart, gSampleSize);
// DEBUG
//gSampleSize = sizeof(fileContent) - 36;
//U8 *audioDataStart = fileContent + 36; // Move pointer after header MANUAL 36 bytes
//MemCopy(gSampleData, audioDataStart, 36);
//Free(fileContent);
// test without skipping header
gSampleData = FileRead(filename, &gSampleSize);
// Print some info about the sample
//Print("Sample rate: %d\n", header.sample_rate);
//Print("Channels: %d\n", header.channels);
//Print("Bits per sample: %d\n", header.bits_per_sample);
}
U0 PlaySample(U32 *buffer, I64 duration, U8 velocity) {
if (!gSampleData || !gSampleSize) {
Print("Sample not loaded.\n");
return;
}
// Just as an example: copy loaded sample to buffer
// This step will depend on how you are processing and playing the audio
I64 samplesToCopy = Min(gSampleSize, duration); // don't overflow the buffer
MemCopy(buffer, gSampleData, samplesToCopy);
// Here you'd typically send the buffer to your audio playing routine
}
// I64 sample_rate = SAMPLE_RATE // whatever your sample rate is // I64 sample_rate = SAMPLE_RATE // whatever your sample rate is
// for (I64 i = 0; i < sample_duration * sample_rate; i++) { // for (I64 i = 0; i < sample_duration * sample_rate; i++) {