Skip to content

Instantly share code, notes, and snippets.

@kinoshita-lab
Last active May 9, 2026 03:38
Show Gist options
  • Select an option

  • Save kinoshita-lab/ae8051db5eb242aa1b4ebd9935e89c5a to your computer and use it in GitHub Desktop.

Select an option

Save kinoshita-lab/ae8051db5eb242aa1b4ebd9935e89c5a to your computer and use it in GitHub Desktop.
16-bit sample playback for Mozzi

A. This software is "Everyone'sWare". It means: Anybody who has this software can use it as if he/she is the author.

A-1. Freeware. No fee is required. A-2. You can freely redistribute this software. A-3. You can freely modify this software. And the source may be used in any software with no limitation. A-4. When you release a modified version to public, you must publish it with your name.

B. The author is not responsible for any kind of damages or loss while using or misusing this software, which is distributed "AS IS". No warranty of any kind is expressed or implied. You use AT YOUR OWN RISK.

C. Copyrighted to Kinoshita Laboratory, which has relinquished.

D. Above three clauses are applied both to source and binary form of this software.

#ifndef SAMPLE16_H_
#define SAMPLE16_H_
#include "MozziHeadersOnly.h"
#include "mozzi_fixmath.h"
#include "mozzi_pgmspace.h"
#define SAMPLE16_F_BITS 16
#define SAMPLE16_F_BITS_AS_MULTIPLIER 65536
enum interpolation16
{
INTERP16_NONE,
INTERP16_LINEAR
};
template <unsigned int NUM_TABLE_CELLS, unsigned int UPDATE_RATE, uint8_t INTERP = INTERP16_NONE>
class Sample16
{
public:
Sample16(const int16_t* TABLE_NAME) : table(TABLE_NAME), endpos_fractional((uint64_t)NUM_TABLE_CELLS << SAMPLE16_F_BITS)
{
setLoopingOff();
}
Sample16() : endpos_fractional((uint64_t)NUM_TABLE_CELLS << SAMPLE16_F_BITS)
{
setLoopingOff();
}
inline void setTable(const int16_t* TABLE_NAME)
{
table = TABLE_NAME;
}
inline void setStart(unsigned int startpos)
{
startpos_fractional = (uint64_t)startpos << SAMPLE16_F_BITS;
}
inline void start()
{
phase_fractional = startpos_fractional;
}
inline void start(unsigned int startpos)
{
setStart(startpos);
start();
}
inline void setEnd(unsigned int end)
{
endpos_fractional = (uint64_t)end << SAMPLE16_F_BITS;
}
inline void rangeWholeSample()
{
startpos_fractional = 0;
endpos_fractional = (uint64_t)NUM_TABLE_CELLS << SAMPLE16_F_BITS;
}
inline void setLoopingOn()
{
looping = true;
}
inline void setLoopingOff()
{
looping = false;
}
inline int16_t next()
{
if (phase_fractional > endpos_fractional) {
if (looping) {
phase_fractional = startpos_fractional + (phase_fractional - endpos_fractional);
} else {
return 0;
}
}
int16_t out;
if (INTERP == INTERP16_LINEAR) {
unsigned int index = phase_fractional >> SAMPLE16_F_BITS;
out = FLASH_OR_RAM_READ<const int16_t>(table + index);
int16_t difference = FLASH_OR_RAM_READ<const int16_t>((table + 1) + index) - out;
int16_t diff_fraction = (int16_t)(((((unsigned int)phase_fractional) >> 8) * difference) >> 8);
out += diff_fraction;
} else {
out = FLASH_OR_RAM_READ<const int16_t>(table + (phase_fractional >> SAMPLE16_F_BITS));
}
incrementPhase();
return out;
}
inline boolean isPlaying()
{
return phase_fractional < endpos_fractional;
}
inline void setFreq(int frequency)
{
phase_increment_fractional = ((((uint64_t)NUM_TABLE_CELLS << ADJUST_FOR_NUM_TABLE_CELLS) * frequency) / UPDATE_RATE) << (SAMPLE16_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS);
}
inline void setFreq(float frequency)
{
phase_increment_fractional = (uint64_t)((((float)NUM_TABLE_CELLS * frequency) / UPDATE_RATE) * SAMPLE16_F_BITS_AS_MULTIPLIER);
}
inline void setFreq_Q24n8(Q24n8 frequency)
{
phase_increment_fractional = (((((uint64_t)NUM_TABLE_CELLS << ADJUST_FOR_NUM_TABLE_CELLS) >> 3) * frequency) / (UPDATE_RATE >> 6)) << (SAMPLE16_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS - (8 - 3 + 6));
}
inline int16_t atIndex(unsigned int index)
{
return FLASH_OR_RAM_READ<const int16_t>(table + index);
}
inline uint64_t phaseIncFromFreq(unsigned int frequency)
{
return (((uint64_t)frequency * NUM_TABLE_CELLS) / UPDATE_RATE) << SAMPLE16_F_BITS;
}
inline void setPhaseInc(uint64_t phaseinc_fractional)
{
phase_increment_fractional = phaseinc_fractional;
}
private:
static const uint8_t ADJUST_FOR_NUM_TABLE_CELLS = (NUM_TABLE_CELLS < 2048) ? 8 : 0;
inline void incrementPhase()
{
phase_fractional += phase_increment_fractional;
}
volatile uint64_t phase_fractional;
volatile uint64_t phase_increment_fractional;
const int16_t* table;
bool looping;
uint64_t startpos_fractional, endpos_fractional;
};
#endif
import wave
import struct
import sys
import os
def convert_wav_to_mozzi16(wav_path, table_name):
try:
with wave.open(wav_path, 'rb') as w:
# フォーマットチェック
num_channels = w.getnchannels()
sampwidth = w.getsampwidth()
framerate = w.getframerate()
nframes = w.getnframes()
if num_channels != 1:
print(f"Error: Mono wav required. Input has {num_channels} channels.", file=sys.stderr)
return
if sampwidth != 2:
print(f"Error: 16-bit wav required. Input is {sampwidth * 8}-bit.", file=sys.stderr)
return
print(f"Processing: {wav_path}", file=sys.stderr)
print(f"Rate: {framerate}Hz, Length: {nframes} samples", file=sys.stderr)
# データの読み込み
raw_data = w.readframes(nframes)
# 16bit Little Endian としてアンパック
# dataには -32768 〜 32767 の整数が入る
data = struct.unpack(f"<{nframes}h", raw_data)
# --- C++コードの出力 ---
header_str = f"""#ifndef {table_name}_H_
#define {table_name}_H_
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "MozziGuts.h"
#define {table_name}_NUM_CELLS {nframes}
#define {table_name}_SAMPLERATE {framerate}
// 16-bit signed integer array
const int16_t {table_name}_DATA[] PROGMEM = {{
"""
print(header_str)
# データをカンマ区切りで出力
for i, sample in enumerate(data):
end_char = ","
if i == len(data) - 1:
end_char = "" # 最後はカンマなし
print(f"{sample}{end_char}", end="")
# 適当な箇所で改行
if (i + 1) % 20 == 0:
print()
else:
print(" ", end="")
footer_str = f"""
}};
#endif /* {table_name}_H_ */
"""
print(footer_str)
except FileNotFoundError:
print(f"Error: File not found: {wav_path}", file=sys.stderr)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python wav2mozzi16.py <input.wav> [table_name]", file=sys.stderr)
sys.exit(1)
input_wav = sys.argv[1]
# テーブル名を決定(引数がなければファイル名から生成)
if len(sys.argv) >= 3:
t_name = sys.argv[2]
else:
base = os.path.basename(input_wav)
t_name = os.path.splitext(base)[0].upper().replace(" ", "_")
# 標準出力をファイルにリダイレクトして使ってください
convert_wav_to_mozzi16(input_wav, t_name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment