Assignment 5 completed
This commit is contained in:
81
notes/work-summary-2026-04-15-v2.md
Normal file
81
notes/work-summary-2026-04-15-v2.md
Normal 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
|
||||
@@ -71,6 +71,7 @@ add_executable(uiaos-kernel
|
||||
src/memutils.c
|
||||
src/memory.c
|
||||
src/pit.c
|
||||
src/songPlayer.c
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
139
src/OSDev_18/include/songApp/frequencies.h
Normal file
139
src/OSDev_18/include/songApp/frequencies.h
Normal 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
|
||||
89
src/OSDev_18/include/songApp/song.h
Normal file
89
src/OSDev_18/include/songApp/song.h
Normal 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
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
55
src/OSDev_18/src/songPlayer.c
Normal file
55
src/OSDev_18/src/songPlayer.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user