diff --git a/notes/work-summary-2026-04-15-v2.md b/notes/work-summary-2026-04-15-v2.md new file mode 100644 index 0000000..c7593a9 --- /dev/null +++ b/notes/work-summary-2026-04-15-v2.md @@ -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 \ No newline at end of file diff --git a/src/OSDev_18/CMakeLists.txt b/src/OSDev_18/CMakeLists.txt index 3b3a6cd..4bedf51 100644 --- a/src/OSDev_18/CMakeLists.txt +++ b/src/OSDev_18/CMakeLists.txt @@ -71,6 +71,7 @@ add_executable(uiaos-kernel src/memutils.c src/memory.c src/pit.c + src/songPlayer.c ) diff --git a/src/OSDev_18/include/kernel/pit.h b/src/OSDev_18/include/kernel/pit.h index 3534894..c8e1ea1 100644 --- a/src/OSDev_18/include/kernel/pit.h +++ b/src/OSDev_18/include/kernel/pit.h @@ -1,5 +1,5 @@ -#ifndef PIT_H -#define PIT_H +#ifndef KERNEL_PIT_H +#define KERNEL_PIT_H #include #include diff --git a/src/OSDev_18/include/songApp/frequencies.h b/src/OSDev_18/include/songApp/frequencies.h new file mode 100644 index 0000000..2994c3b --- /dev/null +++ b/src/OSDev_18/include/songApp/frequencies.h @@ -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 diff --git a/src/OSDev_18/include/songApp/song.h b/src/OSDev_18/include/songApp/song.h new file mode 100644 index 0000000..bca21f3 --- /dev/null +++ b/src/OSDev_18/include/songApp/song.h @@ -0,0 +1,89 @@ +#ifndef SONGAPP_SONG_H +#define SONGAPP_SONG_H + +#include +#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 diff --git a/src/OSDev_18/src/kernel.c b/src/OSDev_18/src/kernel.c index 64ba23b..0e2bf77 100644 --- a/src/OSDev_18/src/kernel.c +++ b/src/OSDev_18/src/kernel.c @@ -6,16 +6,38 @@ #include #include #include - - -/*TODO -clean up terminal - enable scrolling? - -*/ - +#include +#include 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"); } diff --git a/src/OSDev_18/src/songPlayer.c b/src/OSDev_18/src/songPlayer.c new file mode 100644 index 0000000..04c4bad --- /dev/null +++ b/src/OSDev_18/src/songPlayer.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +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; +} \ No newline at end of file