Assignment 5 completed

This commit is contained in:
Teodor
2026-04-15 23:01:36 +00:00
parent 1bef0f3bcb
commit 21cc65beb1
7 changed files with 402 additions and 9 deletions

View File

@@ -0,0 +1,81 @@
# Assignment 5
## #Overview
This assignment implements a simple music player using the **PC speaker (PCSPK)** and the **Programmable Interval Timer (PIT)**.
Songs are played by generating frequencies and controlling timing between notes.
---
## #HowItWorks
### #PCSpeaker
The PC speaker is controlled through **port `0x61`**:
- Bits 0 and 1 enable/disable sound output
---
### #PIT
The PIT is used for:
#### #SoundGeneration
- Channel 2 generates square wave audio
- Frequency is set using:
divisor = PIT_BASE_FREQ / frequency
#### #Timing
- Channel 0 runs at ~1000 Hz
- A tick counter is incremented via interrupts
- `SleepInterrupt()` is used for accurate note timing
---
## #Implementation
### #Playback
Each note contains:
- Frequency (Hz)
- Duration (ms)
Playback works by:
1. Setting frequency with `PlaySound()`
2. Waiting using `SleepInterrupt()`
3. Stopping sound with `StopSound()`
Rests are handled using `R = 0`.
---
## #Challenges
### #Timing
Busy-wait delays caused incorrect timing.
This was solved by using interrupt-based sleeping.
### #QEMU
QEMU has limited PC speaker support:
- Fast note changes sound unclear
- Songs degrade at high speed
---
## #Adjustments
To improve playback clarity:
duration = original_duration * 2
This slows down the songs and makes notes more distinguishable.
---
## #Conclusion
The system successfully:
- Generates sound using the PC speaker
- Uses PIT for timing and frequency control
- Plays songs with multiple notes
Limitations in QEMU affect sound quality, but the implementation itself works as i

View File

@@ -71,6 +71,7 @@ add_executable(uiaos-kernel
src/memutils.c
src/memory.c
src/pit.c
src/songPlayer.c
)

View File

@@ -1,5 +1,5 @@
#ifndef PIT_H
#define PIT_H
#ifndef KERNEL_PIT_H
#define KERNEL_PIT_H
#include <libc/stdint.h>
#include <libc/stdbool.h>

View File

@@ -0,0 +1,139 @@
#ifndef SONGAPP_FREQUENCIES_H
#define SONGAPP_FREQUENCIES_H
// Note frequencies (in Hz)
#define C0 16
#define Cs0 17
#define D0 18
#define Ds0 19
#define E0 21
#define F0 22
#define Fs0 23
#define G0 25
#define Gs0 26
#define A0 27
#define As0 29
#define B0 31
#define C1 33
#define Cs1 35
#define D1 37
#define Ds1 39
#define E1 41
#define F1 44
#define Fs1 46
#define G1 49
#define Gs1 52
#define A1 55
#define As1 58
#define B1 62
#define C2 65
#define Cs2 69
#define D2 73
#define Ds2 78
#define E2 82
#define F2 87
#define Fs2 92
#define G2 98
#define Gs2 104
#define A2 110
#define As2 117
#define B2 123
#define C3 131
#define Cs3 139
#define D3 147
#define Ds3 156
#define E3 165
#define F3 175
#define Fs3 185
#define G3 196
#define Gs3 208
#define A3 220
#define As3 233
#define B3 247
#define C4 262
#define Cs4 277
#define D4 294
#define Ds4 311
#define E4 330
#define F4 349
#define Fs4 370
#define G4 392
#define Gs4 415
#define A4 440
#define As4 466
#define B4 494
#define C5 523
#define Cs5 554
#define D5 587
#define Ds5 622
#define E5 659
#define F5 698
#define Fs5 740
#define G5 784
#define Gs5 831
#define A5 880
#define As5 932
#define B5 988
#define C6 1047
#define Cs6 1109
#define D6 1175
#define Ds6 1245
#define E6 1319
#define F6 1397
#define Fs6 1480
#define G6 1568
#define Gs6 1661
#define A6 1760
#define As6 1865
#define B6 1976
#define C7 2093
#define Cs7 2217
#define D7 2349
#define Ds7 2489
#define E7 2637
#define F7 2794
#define Fs7 2960
#define G7 3136
#define Gs7 3322
#define A7 3520
#define As7 3729
#define B7 3951
#define C8 4186
#define Cs8 4435
#define D8 4699
#define Ds8 4978
#define E8 5274
#define F8 5588
#define Fs8 5919
#define G8 6272
#define Gs8 6645
#define A8 7040
#define As8 7459
#define B8 7902
#define C9 8372
#define Cs9 8870
#define D9 9397
#define Ds9 9956
#define E9 10548
#define F9 11175
#define Fs9 11839
#define G9 12543
#define Gs9 13290
#define A9 14080
#define As9 14917
#define B9 15804
#define A_SHARP4 466
#define G_SHARP4 415
#define R 0 // R (no sound)
#endif //UIAOS_FREQUENCIES_H

View File

@@ -0,0 +1,89 @@
#ifndef SONGAPP_SONG_H
#define SONGAPP_SONG_H
#include <libc/stdint.h>
#include "frequencies.h"
// Define a struct to represent a single musical note
typedef struct {
uint32_t frequency; // The frequency of the note in Hz (e.g., A4 = 440 Hz)
uint32_t duration; // The duration of the note in milliseconds
} Note;
// Define a struct to represent a song
typedef struct {
Note* notes; // Pointer to an array of Note structs representing the song
uint32_t length; // The number of notes in the song
} Song;
// Define a struct to represent a song player
typedef struct {
void (*play_song)(Song* song); // Function pointer to a function that plays a song
} SongPlayer;
// Function prototype for creating a new SongPlayer instance
// Returns a pointer to a newly created SongPlayer object
SongPlayer* CreateSongPlayer(void);
static Note music_1[] = {
{E5, 250}, {R, 125}, {E5, 125}, {R, 125}, {E5, 125}, {R, 125},
{C5, 125}, {E5, 125}, {G5, 125}, {R, 125}, {G4, 125}, {R, 250},
{C5, 125}, {R, 250}, {G4, 125}, {R, 125}, {E4, 125}, {R, 125},
{A4, 125}, {B4, 125}, {R, 125}, {A_SHARP4, 125}, {A4, 125}, {R, 125},
{G4, 125}, {E5, 125}, {G5, 125}, {A5, 125}, {F5, 125}, {G5, 125},
{R, 125}, {E5, 125}, {C5, 125}, {D5, 125}, {B4, 125}, {R, 125},
{C5, 125}, {R, 250}, {G4, 125}, {R, 125}, {E4, 125}, {R, 125},
{A4, 125}, {B4, 125}, {R, 125}, {A_SHARP4, 125}, {A4, 125}, {R, 125},
{G4, 125}, {E5, 125}, {G5, 125}, {A5, 125}, {F5, 125}, {G5, 125},
{R, 125}, {E5, 125}, {C5, 125}, {D5, 125}, {B4, 125}, {R, 125},
};
static Note music_2[] = {
{A4, 200}, {E5, 200}, {A5, 200}, {R, 100}, {A5, 200}, {A5, 200}, {Gs5, 200}, {A5, 200},
{R, 100}, {E5, 200}, {R, 100}, {E5, 200}, {R, 100}, {E5, 200}, {R, 100}, {E5, 200},
{A4, 200}, {E5, 200}, {A5, 200}, {R, 100}, {A5, 200}, {A5, 200}, {Gs5, 200}, {A5, 200},
{R, 100}, {E5, 200}, {R, 100}, {E5, 200}, {R, 100}, {E5, 200}, {R, 100}, {E5, 200},
{A4, 200}, {E5, 200}, {A5, 200}, {R, 100}, {A5, 200}, {A5, 200}, {Gs5, 200}, {A5, 200},
{R, 100}, {E5, 200}, {R, 100}, {E5, 200}, {R, 100}, {E5, 200}, {R, 100}, {E5, 200}
};
static Note music_3[] = {
{E4, 200}, {E4, 200}, {F4, 200}, {G4, 200}, {G4, 200}, {F4, 200}, {E4, 200}, {D4, 200},
{C4, 200}, {C4, 200}, {D4, 200}, {E4, 200}, {E4, 400}, {R, 200},
{D4, 200}, {D4, 200}, {E4, 200}, {F4, 200}, {F4, 200}, {E4, 200}, {D4, 200}, {C4, 200},
{A4, 200}, {A4, 200}, {A4, 200}, {G4, 400}
};
static Note music_4[] = {
{C4, 500}, {D4, 500}, {E4, 500}, {C4, 500},
{C4, 500}, {D4, 500}, {E4, 500}, {C4, 500},
{E4, 500}, {F4, 500}, {G4, 1000},
{E4, 500}, {F4, 500}, {G4, 1000},
{G4, 250}, {A4, 250}, {G4, 250}, {F4, 250}, {E4, 500}, {C4, 500},
{G4, 250}, {A4, 250}, {G4, 250}, {F4, 250}, {E4, 500}, {C4, 500},
{C4, 500}, {G3, 500}, {C4, 1000},
{C4, 500}, {G3, 500}, {C4, 1000}
};
static Note music_5[] = {
{E4, 375}, {C4, 375}, {D4, 375}, {A3, 375}, {B3, 375}, {D4, 375}, {C4, 375}, {A3, 375},
{E4, 375}, {C4, 375}, {D4, 375}, {A3, 375}, {B3, 375}, {D4, 375}, {C4, 375}, {A3, 375},
};
static Note music_6[] = {
{F4, 250}, {F4, 250}, {F4, 250}, {C5, 250}, {A_SHARP4, 250}, {G_SHARP4, 250}, {F4, 500},
{F4, 250}, {F4, 250}, {F4, 250}, {C5, 250}, {A_SHARP4, 250}, {G_SHARP4, 250}, {F4, 500},
{A_SHARP4, 250}, {A_SHARP4, 250}, {A_SHARP4, 250}, {F5, 250}, {D5, 250}, {C5, 250}, {A_SHARP4, 500},
{A_SHARP4, 250}, {A_SHARP4, 250}, {A_SHARP4, 250}, {F5, 250}, {D5, 250}, {C5, 250}, {A_SHARP4, 500},
};
void EnableSpeaker(void);
void DisableSpeaker(void);
void PlaySound(uint32_t frequency);
void StopSound(void);
void PlaySongImpl(Song* song);
void PlayMusic(void);
#endif

View File

@@ -6,16 +6,38 @@
#include <kernel/keyboard.h>
#include <kernel/memory.h>
#include <kernel/pit.h>
/*TODO
clean up terminal - enable scrolling?
*/
#include <songApp/song.h>
#include <songApp/frequencies.h>
extern uint32_t end;
void PlayMusic(void) {
Song songs[] = {
{music_1, sizeof(music_1) / sizeof(Note)},
{music_2, sizeof(music_2) / sizeof(Note)},
{music_3, sizeof(music_3) / sizeof(Note)},
{music_4, sizeof(music_4) / sizeof(Note)},
{music_5, sizeof(music_5) / sizeof(Note)},
{music_6, sizeof(music_6) / sizeof(Note)}
};
uint32_t songCount = sizeof(songs) / sizeof(Song);
SongPlayer* player = CreateSongPlayer();
if (!player) {
TerminalWriteString("Failed to create SongPlayer.\n");
return;
}
while(1) {
for(uint32_t i = 0; i < songCount; i++) {
player->play_song(&songs[i]);
SleepInterrupt(1000);
}
}
}
void main(void) {
TerminalInitialize();
GdtInitialize();
@@ -28,6 +50,8 @@ void main(void) {
InitPaging();
PrintMemoryLayout();
/*
void* memory1 = malloc(48261);
void* memory2 = malloc(27261);
void* memory3 = malloc(12617);
@@ -54,6 +78,10 @@ void main(void) {
SleepTest();
*/
PlayMusic();
for (;;) {
__asm__ volatile("hlt");
}

View File

@@ -0,0 +1,55 @@
#include <songApp/song.h>
#include <kernel/pit.h>
#include <kernel/io.h>
#include <kernel/memory.h>
#include <libc/stdint.h>
void EnableSpeaker(void) {
uint8_t speakerState = InPortByte(PC_SPEAKER_PORT);
OutPortByte(PC_SPEAKER_PORT, speakerState | 0x03);
}
void DisableSpeaker(void) {
uint8_t speakerState = InPortByte(PC_SPEAKER_PORT);
OutPortByte(PC_SPEAKER_PORT, speakerState & 0xFC);
}
void PlaySound(uint32_t frequency) {
if (!frequency) return;
uint32_t divisor = PIT_BASE_FREQ / frequency;
OutPortByte(PIT_CMD_PORT, 0xB6);
OutPortByte(PIT_CHANNEL2_PORT, divisor & 0xFF);
OutPortByte(PIT_CHANNEL2_PORT, (divisor >> 8) & 0xFF);
EnableSpeaker();
}
void StopSound(void) {
DisableSpeaker();
}
void PlaySongImpl(Song* song) {
for (uint32_t i = 0; i < song->length; i++) {
Note currentNote = song->notes[i];
if (currentNote.frequency == R) {
StopSound();
SleepInterrupt(currentNote.duration * 2); // *2 is a forced delay, added because QEMU struggles with frequencies switching fast
} else {
PlaySound(currentNote.frequency);
SleepInterrupt(currentNote.duration * 2); // *2 is a forced delay, added because QEMU struggles with frequencies switching fast
StopSound();
}
}
}
SongPlayer* CreateSongPlayer(void) {
SongPlayer* player = (SongPlayer*)malloc(sizeof(SongPlayer));
if (!player) return 0;
player->play_song = PlaySongImpl;
return player;
}