Playing music

open-source

#21

That sounds surprisingly good! Do you have an own mixer of 4 channels? It sounds much better than Pokitto’s own mixer. What is the sample rate?

If you can get rid of those extra"pops" when changing the sample (supposing?) it would make it perfect.
We need somehow boost the lower frequencies when playing with the internal speaker. Using headphones the sound is more balanced.


#22

Anyone wondering what it sounds like but can’t test it out… Check out @Spinal_Cord’s Tweet: https://twitter.com/Spinal_Cord/status/1093867050630565890?s=09


#23

Yup, had to mix the channels myself, i didn’t know if there was another way. The only affects supported right now are volume changes and the playing speed. I wouldn’t know what many of the other mod effects do nevermind replicate them.
The music is only semi-automatically converted ATM.


#24

Amazing stuff @spinal !

Edit: Neil, just thought of something

That popping noise may be because you might turn the sample value to zero between samples

In PCM samples, silence is typically 127


#25

I don’t think that’s what the issue is, the samples are converted to signed variables, mixed, then converted back.


#26

That regular popping can only be the result of a systematic big “jump” in the output value. I am pretty sure it is a calculation error of some kind, such as a sudden change from 0->127 or overflow from 255->low


#27

I understand, but I can’t see anywhere in code that might be doing that.

I can’t even tell right now, if this version sounds better or worse…

music_player.LPC11U68 (10).zip (48.7 KB)


#28

I’ve made a couple of changes to how this works, nothing drastically different. It does seem to sound better. I think thiugh, that the samples being 8bit is the biggest issue when it comes to playback quality when mixing.

Expect an update soon™…


#29

Here is the latest test of my music engine. Most some of you are old enough to recognise the game intro I ripped off. I chose it because it was one of the first games that impressed me with the sampled audio and the fact that it fit onto a single 880kb floppy disk. This version fits into a single binary for pokitto, so please excuse the poor sound quality, I have to reduce the sample size by about 33% to make the 2:30 demo fit.

cannon.bin (191.9 KB)

[Edit] I must admit, I expected a comment or two. Did anyone test this? Does it need more work maybe? Should I think about making a module tracker so other people can use this ? Etc.


#30

I sure would like to see some tools to be able to add music to games more easily if that was the question. And yes I tried it. Sounds promising to me.


#31

musicplayer.bin (191.9 KB)

Slight tweak, I noticed that I was playing the samples 4x volume, fixed that, not sure it it sounds better or not.


#32

Here’s an update of Sensitive using my latest version of the music player, you can hear that the looping samples are going a but weird, I don’t know if there’s a trick to that or not.

sensitive.bin (208.7 KB)

I’ve cut the tune format down a little, resulting in a 10kb smaller file with the above tune, and the sample format now includes loop data, which is read directly from a converted .wav file.

What do you guys thing of the sound quality?


#33

Sounds good :slight_smile: Amazing that there is such a long soundtrack in ROM!


#34

I just need to create some sort of tracker so other people can use it :stuck_out_tongue:
There’s 27 samples and 50 patterns in that one, here’s a look at the track array…

const char my_pattern[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,45,46,48,49};

const uint8_t tune[50][64][4][5]={
{
// Pattern:0
//      .----------------------[Note] Upper nibble is the note value.
//      |.---------------------[Octave] Lower nibble is the octave.
//      ||   .-----------------[Volume + 128] If first bit is set then volume is used.
//      ||   |    .------------[Instrument] Can use up to 255 instruments I suppose.
//      ||   |    |    .-------[Effect] Effects are based on standard MOD effects.
//      ||   |    |    |    .--[Effect Value] Values for above effects.
//      ||   |    |    |    |
    {{0x15,0xC0,0x02,0x0C,0x01},{0x00,0x00,0x00,0x0F,0x03},{0x00,0x00,0x00,0x0C,0x00},{0x00,0x00,0x00,0x0C,0x00}},
    {{0x00,0x00,0x00,0x0C,0x02},{0x00,0x00,0x00,0x0C,0x00},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00}},
    {{0x00,0x00,0x00,0x0C,0x03},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00}},
    {{0x00,0x00,0x00,0x0C,0x04},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00}},
    {{0x15,0xC0,0x03,0x0C,0x05},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00}},
    {{0x00,0x00,0x00,0x0C,0x06},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00}},
    {{0x00,0x00,0x00,0x0C,0x07},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00}},
    {{0x00,0x00,0x00,0x0C,0x08},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00}},
    {{0x15,0xC0,0x04,0x0C,0x09},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00}},
    {{0x00,0x00,0x00,0x0C,0x0A},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00}},
// etc.

I think if I limit the number of samples to 15, I can probably merge the instrument and effect bytes and save a little more space…


#35

Instead of just using raw uint8_ts,
you could make a struct to pack the data into so it’s easier to understand and manipulate.

E.g.

enum class Instrument : std::uint8_t
{
	Trumpet,
	Drum,
	Fish,
};

enum class MusicEffect : std::uint8_t
{
	None,
	SineWave,
	Tremolo,
};

enum class Note : std::uint8_t
{
	A, B, C, D, E, F, G,
};

class NoteAndOctave
{
private:
	static constexpr std::uint8_t noteMask = 0x0F;
	static constexpr std::uint8_t noteShift = 4;
	
	static constexpr std::uint8_t octaveMask = 0x0F;
	static constexpr std::uint8_t octaveShift = 0;
	
	constexpr std::uint8_t createValue(Note note, std::uint8_t octave)
	{	
		return static_cast<std::uint8_t>(((static_cast<std::uint8_t>(note) & noteMask) << noteShift) | ((octave & octaveMask) << octaveShift));
	}

private:
	std::uint8_t value;
	
public:
	NoteAndOctave() = default;
	
	constexpr NoteAndOctave(std::uint8_t value) :
		value(value)
	{
	}
	
	constexpr NoteAndOctave(Note note, std::uint8_t octave) :
		value(createValue(note, octave))
	{
	}
	
	constexpr Note getNote() const
	{
		return static_cast<Note>((this->value >> noteShift) & noteMask);
	}
	
	constexpr std::uint8_t getOctave() const
	{
		return static_cast<std::uint8_t>((this->value >> octaveShift) & octaveMask);
	}
};

class MusicThing
{
private:
	NoteAndOctave noteAndOctave;
	std::uint8_t volume;
	Instrument instrument;
	MusicEffect effect;
	std::uint8_t effectValue;
	
public:
	constexpr MusicThing(NoteAndOctave noteAndOctave, std::uint8_t volume, Instrument instrument, MusicEffect effect, std::uint8_t effectValue) :
		noteAndOctave(noteAndOctave), volume(volume), instrument(instrument), effect(effect), effectValue(effectValue)
	{
	}
	
	constexpr Note getNote() const
	{
		return this->noteAndOctave.getNote();
	}
	
	constexpr std::uint8_t getOctave() const
	{
		return this->noteAndOctave.getOctave();
	}
	
	constexpr Instrument getInstrument() const
	{
		return this->instrument;
	}
	
	constexpr MusicEffect getEffect() const
	{
		return this->effect;
	}
	
	constexpr std::uint8_t getEffectValue() const
	{
		return this->effectValue;
	}
};

Then you could declare a sound part like:

{ { Note::A, 5 }, 192, Instrument::Drum, MusicEffect::Tremolo, 0x01 }

#36


#38

You know, if I strip some of the unfinished features out from my music player, I bet it could work well. If say I forget trying to add any of the special effect etc. and simple have notes volumes and a fixed set of instruments, it could work well.


Online Pokitto Python Editor improvement suggestions
#39

Yeah, the audio quality sounds very promising in your system. How much it would increase the rom size (the code and the default instruments)? How much is affects to cpu performance?

If there was the ability to add own instrument samples, how much would the rom cost be per instrument (minimum)?


#40

The way it stands now, if my most recent PR got added (I haven’t checked) then some of the code is already in pokittolib. Its effect on speed seems to me very little. I’ve been using Sensitive to test the changes (which used direct bitmaps so is already running quite slow) doesn’t seem to slow down at all when the audio quality is set to 44100.
As for size, that depends entirely on the samples. Currently I’m using samples taken directly from old protracker mod files without compression, so some of the sample sets are up to 150+kb, but with a handful of fixed instruments, piano, guitar, drum etc. I bet we can keep the size very small, it can handle ANY sample rate, even non-standard rates and looping works well now, so it’s possible that an instrument can be really short.
For the tune data, 1byte per note (with octave) + 1 byte per instrument (+ volume?) for maybe 4 channels, we can probably keep that size down also.

sensitive.bin (215.8 KB)
Above is my latest build of sensitive, which is as far as the music player is right now. it’s updating the buffer in the playback interrupt with no ill effect on the gameplay.


#41

The linker is able to drop out the code that is not used. So it probably will take ROM space only after it is taken into use in a game.

That’s impressive!