Compare commits

...

24 commits

Author SHA1 Message Date
Solomon Peachy
0e7718ed4e voice: Add Norwegian Nynorsk voicefile synthesis
(Enabled, as it now has >98% coverage)

Change-Id: Ie14c28cf279cf8bb2215deb8f1ec597cb980b3ab
2026-04-01 11:04:53 -04:00
Solomon Peachy
a2f652cf1f disktidy: Add '__MACOSX' directories to the list
Change-Id: If25ccbeaf1ada8ae4bcc9a1853302c297f7a3e08
2026-04-01 10:32:40 -04:00
mojyack
6be230275e plugins: pdbox: remove HW_SAMPR_CAPS usage
Change-Id: I4e37213a38ac5d1260bc2fa208828abd55fc2647
2026-04-01 08:48:12 -04:00
Solomon Peachy
c5b171da41 FS#13847 - Updated Norwegian Nynorsk translation (Eivind Ødegård)
Change-Id: Iccb88dcb80b4c316ad1701fada1d0aac54958e51
2026-04-01 08:25:23 -04:00
Solomon Peachy
65f2105445 Revert "sdlapp: Enable compilation of most apps/games"
This reverts commit 96b5642c93.

Reason for revert: Accidentlly merged this before it was ready.

Change-Id: I9344988f6556b774479dd4fd209f30e450ad0858
2026-03-30 20:52:42 -04:00
Solomon Peachy
78ba3211be FS13846: Russian translation update (Sergey Puskov)
Change-Id: Ifa9dff84380f20637e39900409bacbb690d40634
2026-03-30 20:50:13 -04:00
Erin of Yukis
96b5642c93 sdlapp: Enable compilation of most apps/games
Removes `#ifdef`s in many places to just have `HAVE_TOUCHSCREEN` be the
input fall back, as is already tentatively the case in the code.

Solitaire was the only app excluded since there aren’t enough buttons
for it mapped in the SDL layer.

Change-Id: I62450b7110b86c8037a121e96cd2e46754be79a3
2026-03-29 20:32:58 -04:00
Solomon Peachy
e677895e29 xrick: Fix a race condition that could result in audio stopping
get_more() can disable audio when there's nothing more to genreate
but that can get called (and "fail") _before_ we set the flag
that audio is turned on.  If this occurs, we will incorrecly
set the "audio on" flag after the failure which will prevent
the mixer from being re-started.

(We're at the mercy of thread scheduling)

Change-Id: I4f6c50a71c7ad685ff45f775e7e4b1c61b8b7777
2026-03-29 18:07:00 -04:00
Solomon Peachy
f235443ba1 FS13829 - Add Android-specific entries to disktidy plugin
Notably, the 'Android' and 'LOST.DIR' top-level directories.

Change-Id: Idee411e518a8b73e4f0a4283e368e35c8a2f0938
2026-03-29 09:25:28 -04:00
mojyack
03060090c9 usb: arc: implement batched request api
Change-Id: I9d677286589a336d7258cf2c9d3c7d2847243dfa
2026-03-29 09:04:10 -04:00
mojyack
f84003fa40 usb: add fallback implementation of emulated batch api
some targets can process requests fast enough without dedicated
batch api implementation.
provide generic implementationn for such targets.

Change-Id: I152681441e70e0e98396274d9305d371d2bbfbe3
2026-03-29 09:03:56 -04:00
mojyack
d2b76a99f3 usb: introduce batched request api
in order to implement iap digital audio interface, we have to send
a pcm packet every usb frame i.e. 1000 packets per second.
this rate can't be achieved with current standard request-response design,
even with fast_completion_handler.
this is why this new api is needed.

Change-Id: Id3d7dd037660871e2bdb69656f63ff13280c3804
2026-03-29 09:03:29 -04:00
Solomon Peachy
3f4d5a527b bootloader: Don't compile sound.c in bootloader builds
(a) Nothing uses it, and (b) stuff it needs isn't built either

Change-Id: Ib8b46e00d4306f7d05b47883bfaa98497a075db5
2026-03-28 12:43:59 -04:00
Solomon Peachy
42ad3c75d2 Fix red in 36f34089d6
Can't reference settings in bootloader builds

Change-Id: I471c3ca61ec4c93d38317278b79b973192aa28bb
2026-03-28 12:41:43 -04:00
Solomon Peachy
36f34089d6 volume: Apply limits inside sound_set_volume()
This way all paths to setting the volume respect the limits, instead
of only callers of setvol(), which only applies to (some?) interactive
paths.

Change-Id: Ia8ece22aafcb04df33021071cb933eaeb1146502
2026-03-28 08:55:57 -04:00
Solomon Peachy
5ce96b84d8 pcm_mixer: Explicitly initialize the mixer frequency in the pcm init path
Change-Id: I5b79dbb0608ef182fd5592797c6211ce5299999c
2026-03-28 08:55:07 -04:00
Solomon Peachy
a84a38dd87 pcm: Fix up some compile warnings that show up under some sim builds
firmware/pcm_mixer.c:36:35: warning: conversion from ‘long unsigned int’ to ‘unsigned int’ changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow]
   36 | static unsigned int mixer_sampr = -1UL;
      |
firmware/pcm_mixer.c:269:21: warning: comparison is always false due to limited range of data type [-Wtype-limits]
  269 |     if (mixer_sampr == -1UL)
      |                     ^~

So drop the 'L' and everything is peachy.

Change-Id: I9440d3300931be229c6c4b01bdc1d09b55c34d88
2026-03-28 08:54:33 -04:00
Solomon Peachy
daef425c59 pcm_mixer: Get default sample rate from pcm layer.
Change-Id: I6eccb37556e70f23112bbb0e26f4a45ac0930245
2026-03-28 08:16:04 -04:00
Solomon Peachy
759ef2767a pcm: Make pcm_is_ready a per-sink property instead of a global.
All that matters is that the _current_ sink is ready, not all possible
sinks.  When switching sinks we will need to ensure tehy are initialized
before switching over.

Change-Id: I341f7e9dcb4e2add4d0b292b68b69eb08dec0194
2026-03-28 07:09:54 -04:00
Solomon Peachy
ae724ba5f3 as3525: PCM driver init references the sink directly
Change-Id: I42e8f04214790676d6f177669cdeda58fa8e8c28
2026-03-25 10:13:36 -04:00
mojyack
9b207bfb91 plugins: pacbox: remove hw_freq_sampr usage
Change-Id: I365b4f901dd2fbcc22bf1a8009a9d9b58a1da556
2026-03-25 07:35:52 -04:00
Aidan MacDonald
da53cc0f14 echoplayer: pcm: increase SAI FIFO buffer depth
Change the SAI FIFO threshold so that it is always
kept topped up at maximum; DMA needs to do single
transfers to the peripheral now. Ignore FEIF errors
since they seem to occur constantly with this setup
(though it's not exactly clear why this happens).

FEIF only indicates that the SAI made a DMA request
while the DMA FIFO is empty, which isn't a fatal error.
The DMA channel will simply service the request when
it is ready.

Keeping the SAI FIFO constantly full increases the
overall timing margin before underrun so losing the
FEIF info isn't a big deal in practice.

Change-Id: I16eb1cbb17039db76938bd86c4921b8060c83556
2026-03-25 07:33:19 -04:00
Solomon Peachy
2578411f00 Fix red in 9c708e3876
Test build didn't catch this due to a dependency failure.  (WTF?)

Change-Id: I0487a02e9fad8b6bec9b93066619aef04c838d3b
2026-03-25 07:30:27 -04:00
mojyack
9c708e3876 plugins: midi: remove HW_SAMPR_CAPS usage
Change-Id: Ie4962cf6d4f2f9078fa348f8f6f638cfbc0047a5
2026-03-25 07:02:49 -04:00
27 changed files with 8145 additions and 1436 deletions

File diff suppressed because it is too large Load diff

View file

@ -13142,20 +13142,6 @@
*: "в сторону ферзя"
</voice>
</phrase>
<phrase>
id: LANG_PLAYTIME_ERROR
desc: playing time screen
user: core
<source>
*: "Error while gathering info"
</source>
<dest>
*: "Сбой при сборе информации"
</dest>
<voice>
*: "Сбой при сборе информации"
</voice>
</phrase>
<phrase>
id: LANG_CLEAR_ALL_RESUMES
desc: in the mpegplayer settings menu
@ -14389,7 +14375,7 @@
user: core
<source>
*: "Press LEFT to cancel."
android,hifietma*,zenvision: "Press BACK to cancel."
android,hifietma*: "Press BACK to cancel."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
ihifi760,ihifi960: "Double tap RETURN to cancel."
ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
@ -14404,7 +14390,7 @@
</source>
<dest>
*: "Нажмите ВЛЕВО для отмены."
android,hifietma*,zenvision: "Нажмите НАЗАД для отмены."
android,hifietma*: "Нажмите НАЗАД для отмены."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Нажмите ВКЛ. для отмены."
ihifi760,ihifi960: "Дважды нажмите ВОЗВРАТ для отмены."
ihifi770,ihifi770c,ihifi800: "Нажмите ДОМОЙ для отмены."
@ -14419,7 +14405,7 @@
</dest>
<voice>
*: "Нажмите кнопку Влево для отмены."
android,hifietma*,zenvision: "Нажмите кнопку Назад для отмены."
android,hifietma*: "Нажмите кнопку Назад для отмены."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Нажмите кнопку включения для отмены."
ihifi760,ihifi960: "Дважды нажмите кнопку Назад для отмены."
ihifi770,ihifi770c,ihifi800: "Нажмите кнопку Домой для отмены."
@ -15790,13 +15776,13 @@
</phrase>
<phrase>
id: LANG_VOICED_DATE_FORMAT
desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
desc: format string for how dates will be read back. Y == 4-digit year (grouped), y == 4-digit year (numeric), A == month name, m == numeric month, d == numeric day. For example, for 2021-01-05, "AdY" will be voiced as "January 5 twenty twenty-one" and "dmy" will be voiced as "5 1 two thousand twenty one
user: core
<source>
*: "dAY"
</source>
<dest>
*: "~dAY"
*: "dAy"
</dest>
<voice>
*: ""
@ -15872,20 +15858,6 @@
*: "Браузер по умолчанию"
</voice>
</phrase>
<phrase>
id: LANG_AMAZE_MENU
desc: Amaze game
user: core
<source>
*: "Amaze Main Menu"
</source>
<dest>
*: "Главное меню Amaze"
</dest>
<voice>
*: "Главное меню Amaze"
</voice>
</phrase>
<phrase>
id: LANG_SET_MAZE_SIZE
desc: Maze size in Amaze game
@ -16138,34 +16110,6 @@
*: "Настройки Mik mod"
</voice>
</phrase>
<phrase>
id: LANG_MIKMOD_MENU
desc: mikmod plugin
user: core
<source>
*: "Mikmod Menu"
</source>
<dest>
*: "Меню Mikmod"
</dest>
<voice>
*: "Меню Mik mod"
</voice>
</phrase>
<phrase>
id: LANG_CHESSBOX_MENU
desc: chessbox plugin
user: core
<source>
*: "Chessbox Menu"
</source>
<dest>
*: "Меню Chessbox"
</dest>
<voice>
*: "Меню Chessbox"
</voice>
</phrase>
<phrase>
id: VOICE_INVALID_VOICE_FILE
desc: played if the voice file fails to load
@ -16999,3 +16943,48 @@
*: "Все трэки сортируются по альбому"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "Объявлять статус"
</dest>
<voice>
*: "Объявлять статус"
</voice>
</phrase>
<phrase>
id: LANG_USE_LED_INDICATORS
desc: LED indicators setting
user: core
<source>
*: none
general_purpose_led: "Use LED indicators"
</source>
<dest>
*: none
general_purpose_led: "Использовать LED индикаторы"
</dest>
<voice>
*: none
general_purpose_led: "Использовать LED индикаторы"
</voice>
</phrase>
<phrase>
id: LANG_KEEP_DIRECTORY
desc: file browser setting
user: core
<source>
*: "Always remember last folder"
</source>
<dest>
*: "Всегда запоминать последнюю папку"
</dest>
<voice>
*: "Всегда запоминать последнюю папку"
</voice>
</phrase>

View file

@ -866,20 +866,10 @@ void check_bootfile(bool do_rolo)
#endif
#endif
/* check range, set volume and save settings */
/* set volume and save settings */
void setvol(void)
{
const int min_vol = sound_min(SOUND_VOLUME);
const int max_vol = sound_max(SOUND_VOLUME);
int volume = global_status.volume;
if (volume < min_vol)
volume = min_vol;
if (volume > max_vol)
volume = max_vol;
if (volume > global_settings.volume_limit)
volume = global_settings.volume_limit;
sound_set_volume(volume);
sound_set_volume(global_status.volume);
global_status.last_volume_change = current_tick;
status_save(false);
}

View file

@ -19,9 +19,13 @@
.Trashes/: no
.Spotlight-V100/: no
.DS_Store: no
__MACOSX/: no
._*: no
< Linux/BSD >: no
.Trash-*/: no
.dolphinview: no
.d3lphinview: no
< Android >: no
Android/: no
LOST.DIR/: no
< Other >: no

View file

@ -381,6 +381,8 @@
struct MIDIfile * mf IBSS_ATTR;
int sample_rate IBSS_ATTR;
int max_voices IBSS_ATTR;
int number_of_samples IBSS_ATTR; /* the number of samples in the current tick */
int playing_time IBSS_ATTR; /* How many seconds into the file have we been playing? */
int samples_this_second IBSS_ATTR; /* How many samples produced during this second so far? */
@ -490,11 +492,56 @@ static void get_more(const void** start, size_t* size)
*size = samples_in_buf*sizeof(int32_t);
}
UNUSED_ATTR static int find_min_sampr_ge_22(void)
{
const struct pcm_sink_caps* caps = rb->pcm_current_sink_caps();
int ret = caps->samprs[0];
for (size_t i = 1; i < caps->num_samprs; i += 1)
{
/* caps->samprs is in descending order */
if (caps->samprs[i] >= SAMPR_22)
ret = caps->samprs[i];
else
break;
}
return ret;
}
static int midimain(const void * filename)
{
int a, notes_used, vol;
bool is_playing = true; /* false = paused */
/* decide sample_rate and max_voices */
#if defined(SIMULATOR) /* Simulator requires 44100Hz, and we can afford to use more voices */ || \
(CONFIG_PLATFORM & PLATFORM_HOSTED) /* All hosted targets have CPU to spare */ || \
defined(CPU_MIPS) /* All MIPS targets are pretty fast */
sample_rate = SAMPR_44;
max_voices = 48;
#elif defined(CPU_PP)
/* Some of the pp based targets can't handle too many voices
mainly because they have to use 44100Hz sample rate, this could be
improved to increase max_voices for targets that can do 22kHz */
sample_rate = find_min_sampr_ge_22();
max_voices = sample_rate == SAMPR_22 ? 24 : 16;
#elif defined(CPU_ARM)
/* ARMv4 targets are slow, but treat everything else as fast */
#if (ARM_ARCH >= 6)
sample_rate = SAMPR_44;
max_voices = 32;
#elif (ARM_ARCH >= 5)
sample_rate = find_min_sampr_ge_22();
max_voices = 32;
#else /* ie v4 */
sample_rate = find_min_sampr_ge_22();
max_voices = sample_rate == SAMPR_22 ? 24 : 16;
#endif
#else /* !CPU_ARM */
/* Treat everything else as slow */
sample_rate = find_min_sampr_ge_22();
max_voices = sample_rate == SAMPR_22 ? 24 : 16;
#endif
#if defined(HAVE_ADJUSTABLE_CPU_FREQ)
rb->cpu_boost(true);
#endif
@ -537,7 +584,7 @@ static int midimain(const void * filename)
rb->dsp_set_timestretch(PITCH_SPEED_100);
#endif
rb->dsp_configure(dsp, DSP_SET_SAMPLE_DEPTH, 22);
rb->dsp_configure(dsp, DSP_SET_FREQUENCY, SAMPLE_RATE); /* 44100 22050 11025 */
rb->dsp_configure(dsp, DSP_SET_FREQUENCY, sample_rate); /* 44100 22050 11025 */
rb->dsp_configure(dsp, DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
/*
@ -553,14 +600,14 @@ static int midimain(const void * filename)
midi_debug("Okay, starting sequencing");
bpm = mf->div*1000000/tempo;
number_of_samples = SAMPLE_RATE/bpm;
number_of_samples = sample_rate/bpm;
/* Skip over any junk in the beginning of the file, so start playing */
/* after the first note event */
do
{
notes_used = 0;
for (a = 0; a < MAX_VOICES; a++)
for (a = 0; a < max_voices; a++)
if (voices[a].isUsed)
notes_used++;
tick();

View file

@ -35,7 +35,7 @@ struct GPatch * gusload(char *);
struct GPatch * patchSet[128];
struct GPatch * drumSet[128];
struct SynthObject voices[MAX_VOICES] IBSS_ATTR;
struct SynthObject voices[48] IBSS_ATTR;
static void *alloc(int size)
{

View file

@ -27,67 +27,6 @@
#define NBUF 2
#define MAX_SAMPLES 512
#ifdef SIMULATOR
/* Simulator requires 44100Hz, and we can afford to use more voices */
#define SAMPLE_RATE SAMPR_44
#define MAX_VOICES 48
#elif (CONFIG_PLATFORM & PLATFORM_HOSTED)
/* All hosted targets have CPU to spare */
#define MAX_VOICES 48
#define SAMPLE_RATE SAMPR_44
#elif defined(CPU_PP)
/* Some of the pp based targets can't handle too many voices
mainly because they have to use 44100Hz sample rate, this could be
improved to increase MAX_VOICES for targets that can do 22kHz */
#define SAMPLE_RATE HW_SAMPR_MIN_GE_22
#if HW_SAMPR_CAPS & SAMPR_CAP_22
#define MAX_VOICES 24 /* General MIDI minimum */
#else
#define MAX_VOICES 16
#endif
#elif defined(CPU_MIPS)
/* All MIPS targets are pretty fast */
#define MAX_VOICES 48
#define SAMPLE_RATE SAMPR_44
#elif defined(CPU_ARM)
/* ARMv4 targets are slow, but treat everything else as fast */
#if (ARM_ARCH >= 6)
#define MAX_VOICES 32
#define SAMPLE_RATE SAMPR_44
#elif (ARM_ARCH >= 5)
#define MAX_VOICES 32
#define SAMPLE_RATE HW_SAMPR_MIN_GE_22
#else /* ie v4 */
#define SAMPLE_RATE HW_SAMPR_MIN_GE_22
#if HW_SAMPR_CAPS & SAMPR_CAP_22
#define MAX_VOICES 24 /* General MIDI minimum */
#else
#define MAX_VOICES 16
#endif
#endif /* ARM_ARCH < 5*/
#else /* !CPU_ARM */
/* Treat everything else as slow */
#define SAMPLE_RATE HW_SAMPR_MIN_GE_22
#if HW_SAMPR_CAPS & SAMPR_CAP_22
#define MAX_VOICES 24 /* General MIDI minimum */
#else
#define MAX_VOICES 16
#endif
#endif /* Wrap it up. */
#define BYTE unsigned char
/* Data chunk ID types, returned by readID() */
@ -189,7 +128,7 @@ void * readData(int file, int len);
#define malloc(n) my_malloc(n)
void * my_malloc(int size);
extern struct SynthObject voices[MAX_VOICES];
extern struct SynthObject voices[48]; /* 48: maximum possible max_voices */
extern int chVol[16]; /* Channel volume */
extern int chPan[16]; /* Channel panning */
@ -207,6 +146,8 @@ extern struct GPatch * drumSet[128];
extern struct MIDIfile * mf;
extern int sample_rate;
extern int max_voices;
extern int number_of_samples;
extern int playing_time IBSS_ATTR;
extern int samples_this_second IBSS_ATTR;

View file

@ -65,7 +65,7 @@ static inline void setVol(int ch, int vol)
/* If channel volume changes, we need to recalculate the volume scale */
/* factor for all voices active on this channel */
for (a = 0; a < MAX_VOICES; a++)
for (a = 0; a < max_voices; a++)
if (voices[a].ch == ch)
setVolScale(a);
}
@ -156,7 +156,7 @@ static void findDelta(struct SynthObject * so, int ch, int note)
delta = (((freqtable[note+chPBNoteOffset[ch]]))); /* anywhere from 8000 to 8000000 */
delta = delta * wf->sampRate; /* approx 20000 - 44000 but can vary with tuning */
delta = (delta * chPBFractBend[ch]); /* approx 60000 - 70000 */
delta = delta / (SAMPLE_RATE); /* 44100 or 22050 */
delta = delta / (sample_rate); /* 44100 or 22050 */
delta = delta / (wf->rootFreq); /* anywhere from 8000 to 8000000 */
/* Pitch bend is encoded as a fractional of 16 bits, hence the 16 */
@ -167,7 +167,7 @@ static void findDelta(struct SynthObject * so, int ch, int note)
static inline void computeDeltas(int ch)
{
int a;
for (a = 0; a < MAX_VOICES; a++)
for (a = 0; a < max_voices; a++)
{
if (voices[a].isUsed && voices[a].ch == ch)
{
@ -210,7 +210,7 @@ static inline void pressNote(int ch, int note, int vol)
if(ch == 15) return;
*/
int a;
for (a = 0; a < MAX_VOICES; a++)
for (a = 0; a < max_voices; a++)
{
if (voices[a].ch == ch && voices[a].note == note)
break;
@ -218,7 +218,7 @@ static inline void pressNote(int ch, int note, int vol)
if (!voices[a].isUsed)
break;
}
if (a == MAX_VOICES)
if (a == max_voices)
{
// midi_debug("\nVoice kill");
// midi_debug("\nToo many voices playing at once. No more left");
@ -226,7 +226,7 @@ static inline void pressNote(int ch, int note, int vol)
// for(a=0; a<48; a++)
// midi_debug("\n#%d Ch=%d Note=%d curRate=%d curOffset=%d curPoint=%d targetOffset=%d", a, voices[a].ch, voices[a].note, voices[a].curRate, voices[a].curOffset, voices[a].curPoint, voices[a].targetOffset);
lastKill++;
if (lastKill == MAX_VOICES)
if (lastKill == max_voices)
lastKill = 0;
a = lastKill;
// return; /* None available */
@ -263,7 +263,7 @@ static inline void pressNote(int ch, int note, int vol)
struct GWaveform * wf = drumSet[note]->waveforms[0];
voices[a].wf = wf;
voices[a].delta = (((freqtable[note]<<FRACTSIZE) / wf->rootFreq) * wf->sampRate / SAMPLE_RATE);
voices[a].delta = (((freqtable[note]<<FRACTSIZE) / wf->rootFreq) * wf->sampRate / sample_rate);
if (wf->mode & 28)
// midi_debug("\nWoah, a drum patch has a loop. Stripping the loop...");
wf->mode = wf->mode & (255-28);
@ -285,7 +285,7 @@ static void releaseNote(int ch, int note)
return;
int a;
for (a = 0; a < MAX_VOICES; a++)
for (a = 0; a < max_voices; a++)
{
if (voices[a].ch == ch && voices[a].note == note)
{
@ -432,7 +432,7 @@ int tick(void)
tempo = (((short)e->evData[0])<<16)|(((short)e->evData[1])<<8)|(e->evData[2]);
/* midi_debug("\nMeta-Event: Tempo Set = %d", tempo); */
bpm=mf->div*1000000/tempo;
number_of_samples=SAMPLE_RATE/bpm;
number_of_samples=sample_rate/bpm;
}
}
@ -446,9 +446,9 @@ int tick(void)
samples_this_second += number_of_samples;
while (samples_this_second >= SAMPLE_RATE)
while (samples_this_second >= sample_rate)
{
samples_this_second -= SAMPLE_RATE;
samples_this_second -= sample_rate;
playing_time++;
}
@ -471,7 +471,7 @@ void seekBackward(int nsec)
/* Set the tempo to defalt */
bpm = mf->div*1000000/tempo;
number_of_samples = SAMPLE_RATE/bpm;
number_of_samples = sample_rate/bpm;
/* Reset the tracks to start */
rewindFile();
@ -484,7 +484,7 @@ void seekBackward(int nsec)
do
{
notes_used = 0;
for (a = 0; a < MAX_VOICES; a++)
for (a = 0; a < max_voices; a++)
if (voices[a].isUsed)
notes_used++;
tick();

View file

@ -46,7 +46,7 @@ static void readTextBlock(int file, char * buf)
void resetControllers()
{
int a=0;
for(a=0; a<MAX_VOICES; a++)
for(a=0; a<max_voices; a++)
{
voices[a].cp=0;
voices[a].vol=0;
@ -448,13 +448,13 @@ static inline void synthVoice(struct SynthObject * so, int32_t * out, unsigned i
size_t synthSamples(int32_t *buf_ptr, size_t num_samples) ICODE_ATTR;
size_t synthSamples(int32_t *buf_ptr, size_t num_samples)
{
unsigned int i;
int i;
struct SynthObject *voicept;
size_t nsamples = MIN(num_samples, MAX_SAMPLES);
rb->memset(buf_ptr, 0, nsamples * 2 * sizeof(int32_t));
for(i=0; i < MAX_VOICES; i++)
for(i=0; i < max_voices; i++)
{
voicept=&voices[i];
if(voicept->isUsed)

View file

@ -395,19 +395,19 @@ static void start_sound(void)
#endif
/* Get the closest rate >= to what is preferred */
const struct pcm_sink_caps* caps = rb->pcm_current_sink_caps();
sr_index = rb->round_value_to_list32(PREFERRED_SAMPLING_RATE,
rb->hw_freq_sampr, HW_NUM_FREQ, false);
caps->samprs, caps->num_samprs, false);
if (rb->hw_freq_sampr[sr_index] < PREFERRED_SAMPLING_RATE
&& sr_index > 0)
if (caps->samprs[sr_index] < PREFERRED_SAMPLING_RATE && sr_index > 0)
{
/* Round up */
sr_index--;
}
wsg3_set_sampling_rate(rb->hw_freq_sampr[sr_index]);
wsg3_set_sampling_rate(caps->samprs[sr_index]);
rb->mixer_set_frequency(rb->hw_freq_sampr[sr_index]);
rb->mixer_set_frequency(caps->samprs[sr_index]);
rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, get_more, NULL, 0);
sound_playing = true;

View file

@ -188,6 +188,26 @@ enum plugin_status plugin_start(const void* parameter)
/* Set audio API. */
sys_set_audio_api(API_ROCKBOX);
/* Determine sample rate. */
#if defined(SIMULATOR)
#define PREFERRED_SAMPR SAMPR_44
#else
#define PREFERRED_SAMPR SAMPR_22
#endif
int samplerate;
{
int i;
const struct pcm_sink_caps* caps = rb->pcm_current_sink_caps();
for (i = 0; i < caps->num_samprs; i++) {
if (caps->samprs[i] == PREFERRED_SAMPR)
break;
}
if (i == caps->num_samprs)
samplerate = SAMPR_44;
else
samplerate = caps->samprs[i];
}
/* Initialize audio subsystem. */
sys_open_audio(0, /* No sound input yet */
sys_soundindevlist,
@ -197,7 +217,7 @@ enum plugin_status plugin_start(const void* parameter)
sys_soundoutdevlist,
-1, /* Use the default amount (2) of channels */
sys_choutlist,
PD_SAMPLERATE, /* Sample rate */
samplerate, /* Sample rate */
DEFAULTADVANCE, /* Scheduler advance */
1 /* Enable */);

View file

@ -40,17 +40,9 @@
#define calloc(elements, elem_size) tlsf_calloc(elements, elem_size)
/* Audio declarations. */
#ifdef SIMULATOR
#define PD_SAMPLERATE 44100
#elif (HW_SAMPR_CAPS & SAMPR_CAP_22)
#define PD_SAMPLERATE 22050
#elif (HW_SAMPR_CAPS & SAMPR_CAP_32)
#define PD_SAMPLERATE 32000
#elif (HW_SAMPR_CAPS & SAMPR_CAP_44)
#define PD_SAMPLERATE 44100
#else
#error No sufficient sample rate available!
#endif
/* Maximum sample rate; used for buffer sizing.
* Actual rate is determined at runtime from pcm sink capabilities. */
#define PD_SAMPLERATE 44100
#define PD_SAMPLES_PER_HZ ((PD_SAMPLERATE / HZ) + \
(PD_SAMPLERATE % HZ > 0 ? 1 : 0))
#define PD_OUT_CHANNELS 2

View file

@ -222,8 +222,8 @@ void syssnd_update(void)
if (!isAudioPlaying && fillCount > 0)
{
rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, get_more, NULL, 0);
isAudioPlaying = true;
rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, get_more, NULL, 0);
}
}
}

View file

@ -758,6 +758,8 @@ Ingmar Steen
Lourenço Soares
Yegor Chernyshov
Javier Gutiérrez Gertrúdix
Sergey Puskov
Eivind Ødegård
The libmad team
The wavpack team

View file

@ -527,12 +527,11 @@ drivers/rds.c
#endif /* HAVE_RDS_CAP */
#endif /* PLATFORM_NATIVE */
#endif /* CONFIG_TUNER */
#endif /* BOOTLOADER */
#endif /* !BOOTLOADER */
/* Sound */
sound.c
#ifndef BOOTLOADER
sound.c
pcm_sampr.c
pcm.c
pcm_mixer.c
@ -544,7 +543,7 @@ enc_base.c
#endif /* HAVE_RECORDING */
drivers/audio/audiohw-swcodec.c
#endif /* BOOTLOADER */
#endif /* !BOOTLOADER */
/* Audio codec */
#if !defined(BOOTLOADER)

View file

@ -1362,6 +1362,13 @@ Lyre prototype 1 */
//#define USB_HAS_INTERRUPT -- seems to be broken
#endif /* CONFIG_USBOTG */
#if CONFIG_USBOTG == USBOTG_ARC
#define USB_BATCH_SLOTS 16
#else
#define USB_BATCH_NON_NATIVE
#define USB_BATCH_SLOTS 1
#endif
/* define the class drivers to enable */
#ifdef BOOTLOADER

View file

@ -47,6 +47,7 @@ struct pcm_sink {
/* runtime states */
unsigned long pending_freq;
unsigned long configured_freq;
unsigned long pcm_is_ready;
};
enum pcm_sink_ids {

View file

@ -109,6 +109,23 @@ int usb_drv_deinit_endpoint(int endpoint);
int usb_drv_get_frame_number(void);
#endif
/* batched request api is for workloads that perform very high-frequency,
* performance-sensitive isochronous transactions continuously.
* this api allows multiple transaction requests to be buffered in udc driver.
* so that it can maximize the controller's performance. */
/* called when usb system requires more buffer */
typedef void(*usb_drv_batch_get_more)(const void** ptr, size_t* len);
/* prepare request queue */
int usb_drv_batch_init(int ep, usb_drv_batch_get_more get_more);
/* destroy request queue */
int usb_drv_batch_deinit(void);
/* start processing */
int usb_drv_batch_start(void);
/* stop processing */
int usb_drv_batch_stop(void);
/* USB_STRING_INITIALIZER(u"Example String") */
#define USB_STRING_INITIALIZER(S) { \
sizeof(struct usb_string_descriptor) + sizeof(S) - sizeof(*S), \

View file

@ -76,9 +76,6 @@
*
*/
/* 'true' when all stages of pcm initialization have completed */
static bool pcm_is_ready = false;
static struct pcm_sink* sinks[1] = {
[PCM_SINK_BUILTIN] = &builtin_pcm_sink,
};
@ -142,7 +139,7 @@ void pcm_play_stop_int(void)
static void pcm_wait_for_init(void)
{
while (!pcm_is_ready)
while (!sinks[cur_sink]->pcm_is_ready)
sleep(0);
}
@ -205,7 +202,7 @@ void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active,
if (active)
{
struct pcm_sink* sink = sinks[cur_sink];
if (sink->configured_freq == -1UL)
if (sink->configured_freq == -1U)
{
logf("not configured yet");
return;
@ -251,9 +248,11 @@ void pcm_init(void)
logf("pcm_init");
for(size_t i = 0; i < ARRAYLEN(sinks); i += 1) {
sinks[i]->pending_freq = sinks[i]->caps.default_freq;
sinks[i]->configured_freq = -1UL;
sinks[i]->ops.init();
struct pcm_sink* sink = sinks[i];
sink->pending_freq = sink->caps.default_freq;
sink->configured_freq = -1U;
sink->pcm_is_ready = false;
sink->ops.init();
}
}
@ -263,15 +262,18 @@ void pcm_postinit(void)
logf("pcm_postinit");
for(size_t i = 0; i < ARRAYLEN(sinks); i += 1) {
sinks[i]->ops.postinit();
struct pcm_sink* sink = sinks[i];
sink->ops.postinit();
sink->pcm_is_ready = true;
}
pcm_is_ready = true;
/* Ensure mixer is in a sane state */
mixer_set_frequency(pcm_get_frequency());
}
bool pcm_is_initialized(void)
{
return pcm_is_ready;
return sinks[cur_sink]->pcm_is_ready;
}
enum pcm_sink_ids pcm_current_sink(void)

View file

@ -33,7 +33,7 @@
before the last samples are sent to the codec and so things are done in
parallel (as much as possible) with sending-out data. */
static unsigned int mixer_sampr = HW_SAMPR_DEFAULT;
static unsigned int mixer_sampr = -1U;
static unsigned int mix_frame_size = MIX_FRAME_SAMPLES*4;
/* Define this to nonzero to add a marker pulse at each frame start */
@ -266,7 +266,10 @@ static void mixer_start_pcm(void)
#endif
/* Requires a shared global sample rate for all channels */
pcm_set_frequency(mixer_sampr);
if (mixer_sampr == -1U)
mixer_sampr = pcm_get_frequency();
else
pcm_set_frequency(mixer_sampr);
/* Prepare initial frames and set up the double buffer */
mixer_buffer_callback(PCM_DMAST_STARTED);

View file

@ -313,6 +313,16 @@ void sound_set_volume(int value)
return;
#ifndef BOOTLOADER
/* Apply limits */
const int min_vol = sound_min(SOUND_VOLUME);
const int max_vol = sound_max(SOUND_VOLUME);
if (value < min_vol)
value = min_vol;
if (value > max_vol)
value = max_vol;
if (value > global_settings.volume_limit)
value = global_settings.volume_limit;
global_status.volume = value;
#endif

View file

@ -189,7 +189,7 @@ static void sink_dma_init(void)
I2SOUT_CONTROL = (1<<6) | (1<<3); /* enable dma, stereo */
audiohw_preinit();
sink_set_freq(HW_SAMPR_DEFAULT);
sink_set_freq(builtin_pcm_sink.caps.default_freq);
}
#ifdef HAVE_PCM_DMA_ADDRESS

View file

@ -89,8 +89,6 @@ static bool swd_menu(void)
#if defined(ECHO_R1)
extern volatile int pcm_sai_xrun_count;
extern volatile int pcm_dma_teif_count;
extern volatile int pcm_dma_dmeif_count;
extern volatile int pcm_dma_feif_count;
struct pcmdebug_counter
{
@ -102,8 +100,6 @@ static const struct pcmdebug_counter pcmdebug_counters[] =
{
{"SAI xrun count", &pcm_sai_xrun_count},
{"DMA TEIF count", &pcm_dma_teif_count},
{"DMA DMEIF count", &pcm_dma_dmeif_count},
{"DMA FEIF count", &pcm_dma_feif_count},
};
static int pcmdebug_menu_action_cb(int action, struct gui_synclist *lists)

View file

@ -36,19 +36,16 @@
# define PCM_NATIVE_SAMPLE_SIZE 4
# define DMA_PSIZE_VAL BV_DMA_CHN_CR_PSIZE_32BIT
# define SAI_DSIZE_VAL BV_SAI_SUBBLOCK_CR1_DS_32BIT
# define SAI_FIFO_THRESH BV_SAI_SUBBLOCK_CR2_FTH_QUARTER
# define SAI_FRL_VAL 64
#elif PCM_NATIVE_BITDEPTH == 24
# define PCM_NATIVE_SAMPLE_SIZE 4
# define DMA_PSIZE_VAL BV_DMA_CHN_CR_PSIZE_32BIT
# define SAI_DSIZE_VAL BV_SAI_SUBBLOCK_CR1_DS_24BIT
# define SAI_FIFO_THRESH BV_SAI_SUBBLOCK_CR2_FTH_QUARTER
# define SAI_FRL_VAL 64
#elif PCM_NATIVE_BITDEPTH == 16
# define PCM_NATIVE_SAMPLE_SIZE 2
# define DMA_PSIZE_VAL BV_DMA_CHN_CR_PSIZE_16BIT
# define SAI_DSIZE_VAL BV_SAI_SUBBLOCK_CR1_DS_16BIT
# define SAI_FIFO_THRESH BV_SAI_SUBBLOCK_CR2_FTH_HALF
# define SAI_FRL_VAL 32
#else
# error "Unsupported PCM bit depth"
@ -67,8 +64,6 @@ static int pcm_last_freq = -1;
volatile int pcm_sai_xrun_count;
volatile int pcm_dma_teif_count;
volatile int pcm_dma_dmeif_count;
volatile int pcm_dma_feif_count;
static void play_dma_start(const void *addr, size_t size)
{
@ -105,7 +100,7 @@ static void sai_init(void)
/* Configure DMA1 channel 0 */
reg_writelf(dma1_ch0, DMA_CHN_CR,
MBURST_V(INCR4), /* 32-bit x 4 burst = 16 bytes */
PBURST_V(INCR4), /* (16|32)-bit x 4 burst = 8-16 bytes */
PBURST_V(SINGLE), /* (16|32)-bit x 4 burst = 8-16 bytes */
TRBUFF(0), /* bufferable mode not used for SAI */
DBM(0), /* double buffer mode off */
PL_V(VERYHIGH), /* highest priority */
@ -117,10 +112,8 @@ static void sai_init(void)
DIR_V(MEM_TO_PERIPH), /* read from memory, write to SAI */
PFCTRL_V(DMA), /* DMA is flow controller */
TCIE(1), /* transfer complete interrupt */
TEIE(1), /* transfer error interrupt */
DMEIE(1)); /* direct mode error interrupt */
TEIE(1)); /* transfer error interrupt */
reg_writelf(dma1_ch0, DMA_CHN_FCR,
FEIE(1), /* fifo error interrupt */
DMDIS(1), /* enable fifo mode */
FTH_V(FULL)); /* fifo threshold = 4 words */
@ -146,7 +139,7 @@ static void sai_init(void)
MUTEVAL_V(ZERO_SAMPLE), /* send zero sample on mute */
MUTE(1), /* mute output initially */
TRIS(0), /* don't tri-state outputs */
FTH(SAI_FIFO_THRESH)); /* fifo threshold (2 or 4 samples) */
FTH_V(FULL)); /* fifo threshold (2 or 4 samples) */
reg_writelf(sai1a, SAI_SUBBLOCK_FRCR,
FSOFF(1), FSPOL(0), FSDEF(1), /* I2S format */
FSALL(SAI_FRL_VAL/2 - 1), /* FS active for half the frame */
@ -336,22 +329,12 @@ void dma1_ch0_irq_handler(void)
size_t size;
if (reg_vreadf(lisr, DMA_LISR, TEIF0))
pcm_dma_teif_count++;
if (reg_vreadf(lisr, DMA_LISR, DMEIF0))
pcm_dma_dmeif_count++;
if (reg_vreadf(lisr, DMA_LISR, FEIF0))
pcm_dma_feif_count++;
if (reg_vreadf(lisr, DMA_LISR, TEIF0) ||
reg_vreadf(lisr, DMA_LISR, DMEIF0) ||
reg_vreadf(lisr, DMA_LISR, FEIF0))
{
reg_assignlf(dma1, DMA_LIFCR, TEIF0(1), DMEIF0(1), FEIF0(1));
reg_writelf(dma1_ch0, DMA_CHN_CR, EN(0));
pcm_dma_teif_count++;
reg_assignlf(dma1, DMA_LIFCR, TEIF0(1));
pcm_play_dma_status_callback(PCM_DMAST_ERR_DMA);
/* TODO: this case may need additional handling */
pcm_play_dma_complete_callback(PCM_DMAST_ERR_DMA, NULL, NULL);
}
else if (reg_vreadf(lisr, DMA_LISR, TCIF0))
{

View file

@ -362,6 +362,7 @@ static const unsigned int pipe2mask[USB_NUM_ENDPOINTS*2] = {
/*-------------------------------------------------------------------------*/
static void transfer_completed(void);
static void control_received(void);
static void sof_received(void);
static int prime_transfer(int ep_num, void* ptr, int len, bool send, bool wait);
static void prepare_td(struct transfer_descriptor* td,
struct transfer_descriptor* previous_td, void *ptr, int len,int pipe);
@ -415,6 +416,14 @@ static void usb_drv_reset(void)
#endif
}
static void td_set_buf_ptr(struct transfer_descriptor* td, const void* ptr){
td->buff_ptr0 = (unsigned int)ptr;
td->buff_ptr1 = ((unsigned int)ptr & 0xfffff000) + 0x1000;
td->buff_ptr2 = ((unsigned int)ptr & 0xfffff000) + 0x2000;
td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000;
td->buff_ptr4 = ((unsigned int)ptr & 0xfffff000) + 0x4000;
}
/* One-time driver startup init */
void usb_drv_startup(void)
{
@ -544,6 +553,12 @@ void usb_drv_int(void)
if (status & USBSTS_PORT_CHANGE) {
REG_USBSTS = USBSTS_PORT_CHANGE;
}
/* sof */
if (status & USBSTS_SOF) {
REG_USBSTS = USBSTS_SOF;
sof_received();
}
}
bool usb_drv_stalled(int endpoint,bool in)
@ -664,6 +679,183 @@ void usb_drv_set_test_mode(int mode)
REG_USBCMD |= USBCMD_RUN;
}
/* batched request api */
static struct transfer_descriptor batch_td_array[USB_BATCH_SLOTS] USB_DEVBSS_ATTR __attribute__((aligned(32)));
static uint8_t batch_ep = 0;
static uint8_t batch_write_cursor;
static bool batch_stopped;
static usb_drv_batch_get_more batch_get_more;
static int ep_to_pipe_index(uint8_t ep) {
const int ep_num = EP_NUM(ep);
const int ep_dir = EP_DIR(ep);
const int pipe = ep_num * 2 + (ep_dir == DIR_IN ? 1 : 0);
return pipe;
}
int usb_drv_batch_init(int ep, usb_drv_batch_get_more get_more) {
logf("batch init");
if(batch_ep != 0) {
logf("batch function not available");
return -1;
}
batch_ep = ep;
batch_get_more = get_more;
return 0;
}
int usb_drv_batch_deinit() {
logf("batch deinit");
usb_drv_batch_stop();
batch_ep = 0;
return 0;
}
/* returns whether priming is needed */
static bool batch_fill_tds(void) {
while(!(batch_td_array[batch_write_cursor].size_ioc_sts & DTD_STATUS_ACTIVE)) {
/* batch_get_more may call batch_stop() through:
* - batch_get_more()
* - pcm_play_dma_complete_callback()
* - sink_stop()
* - usb_drv_batch_stop() */
const void* ptr;
size_t len;
batch_get_more(&ptr, &len);
if(len == 0 || batch_stopped) {
return false;
}
struct transfer_descriptor* td = &batch_td_array[batch_write_cursor];
td_set_buf_ptr(td, ptr);
td->size_ioc_sts = (len << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE;
batch_write_cursor = (batch_write_cursor + 1) % USB_BATCH_SLOTS;
}
return true;
}
int usb_drv_batch_start(void) {
logf("batch start");
/* reset variables */
batch_write_cursor = 0;
batch_stopped = false;
/* chain tds */
memset(batch_td_array, 0, sizeof(struct transfer_descriptor) * USB_BATCH_SLOTS);
for(int i = 0; i < USB_BATCH_SLOTS; i += 1) {
struct transfer_descriptor* td = &batch_td_array[i];
td->next_td_ptr = (unsigned int)&batch_td_array[(i + 1) % USB_BATCH_SLOTS];
}
/* configure queue head */
const int pipe = ep_to_pipe_index(batch_ep);
struct queue_head* const qh = &qh_array[pipe];
qh->curr_dtd_ptr = (unsigned int)&batch_td_array[0]; /* or error check in sof_received may read random location */
qh->dtd.next_td_ptr = (unsigned int)&batch_td_array[0];
qh->dtd.size_ioc_sts = 0;
/* pull initial buffers */
batch_fill_tds();
/* monitor sof */
REG_USBINTR |= USBINTR_SOF_EN;
return 0;
}
int usb_drv_batch_stop(void) {
batch_stopped = true;
/* disable sof interrupt */
REG_USBINTR &= ~USBINTR_SOF_EN;
/* break the chain */
/* terminating qh->dtd.next_td_ptr is not reliable */
for(int i = 0; i < USB_BATCH_SLOTS; i += 1) {
struct transfer_descriptor* td = &batch_td_array[i];
td->next_td_ptr = DTD_NEXT_TERMINATE;
}
/* flush endpoint */
const int pipe = ep_to_pipe_index(batch_ep);
const unsigned int mask = pipe2mask[pipe];
REG_ENDPTFLUSH = mask;
while (REG_ENDPTFLUSH & mask);
return 0;
}
#if defined(LOGF_ENABLE) && defined(ROCKBOX_HAS_LOGF)
void usb_drv_dump_regs(void) {
logf("==== register dump %ld ====", current_tick);
logf("USBSTS 0x%08X", REG_USBSTS);
logf("ENDPTSETUPSTAT 0x%08X", REG_ENDPTSETUPSTAT);
logf("ENDPTPRIME 0x%08X", REG_ENDPTPRIME);
logf("ENDPTFLUSH 0x%08X", REG_ENDPTFLUSH);
logf("ENDPTSTATUS 0x%08X", REG_ENDPTSTATUS);
logf("ENDPTCOMPLETE 0x%08X", REG_ENDPTCOMPLETE);
logf("ENDPTCTRL0 0x%08X", REG_ENDPTCTRL0);
logf("ENDPTCTRL1 0x%08X", REG_ENDPTCTRL1);
logf("ENDPTCTRL2 0x%08X", REG_ENDPTCTRL2);
}
static void dump_td_array(struct queue_head* qh, struct transfer_descriptor* tds, size_t size) {
void* current = (void*)qh->curr_dtd_ptr;
void* next = (void*)qh->dtd.next_td_ptr;
logf("==== td dump %ld n=%p c=%p ====", current_tick, next, current);
for(int i = 0; i < size; i += 1) {
int len = (tds[i].size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS;
char sts[] = {
&tds[i] == current ? 'c' : ' ',
&tds[i] == next ? 'n' : ' ',
i == batch_write_cursor ? 'w' : ' ',
'\0',
};
logf("%s td[%02d] status=0x%08X len=%d", sts, i, tds[i].size_ioc_sts, len);
}
}
void usb_drv_dump_tds(int ep) {
const int pipe = ep_to_pipe_index(ep);
struct queue_head* const qh = &qh_array[pipe];
dump_td_array(qh, &td_array[pipe * NUM_TDS_PER_EP], NUM_TDS_PER_EP);
}
void usb_drv_batch_dump_tds(void) {
const int pipe = ep_to_pipe_index(batch_ep);
struct queue_head* const qh = &qh_array[pipe];
dump_td_array(qh, batch_td_array, USB_BATCH_SLOTS);
}
#endif
static void sof_received(void) {
if(batch_ep == 0) {
/* should not happen */
return;
}
/* error recovery */
const int pipe = ep_to_pipe_index(batch_ep);
const unsigned int mask = pipe2mask[pipe];
struct queue_head* const qh = &qh_array[pipe];
struct transfer_descriptor* const td = (void*)qh->curr_dtd_ptr;
if(td->size_ioc_sts & DTD_ERROR_MASK) {
logf("td error status=0x%08X", td->size_ioc_sts);
REG_ENDPTPRIME |= mask;
}
/* do refill */
if(batch_fill_tds()) {
const int pipe = ep_to_pipe_index(batch_ep);
const unsigned int mask = pipe2mask[pipe];
REG_ENDPTPRIME |= mask;
}
}
/*-------------------------------------------------------------------------*/
/* manual: 32.14.5.2 */
@ -854,11 +1046,7 @@ static void prepare_td(struct transfer_descriptor* td,
td->next_td_ptr = DTD_NEXT_TERMINATE;
td->size_ioc_sts = (len<< DTD_LENGTH_BIT_POS) |
DTD_STATUS_ACTIVE | DTD_IOC;
td->buff_ptr0 = (unsigned int)ptr;
td->buff_ptr1 = ((unsigned int)ptr & 0xfffff000) + 0x1000;
td->buff_ptr2 = ((unsigned int)ptr & 0xfffff000) + 0x2000;
td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000;
td->buff_ptr4 = ((unsigned int)ptr & 0xfffff000) + 0x4000;
td_set_buf_ptr(td, ptr);
td->reserved |= DTD_RESERVED_LENGTH_MASK & len;
td->reserved |= DTD_RESERVED_IN_USE;
td->reserved |= (pipe << DTD_RESERVED_PIPE_OFFSET);

View file

@ -554,6 +554,64 @@ void usb_core_hotswap_event(int volume, bool inserted)
}
#endif
#ifdef USB_BATCH_NON_NATIVE
static uint8_t batch_ep = 0;
static bool batch_stopped;
static usb_drv_batch_get_more batch_get_more;
int usb_drv_batch_init(int ep, usb_drv_batch_get_more get_more)
{
if(batch_ep != 0) {
logf("usb_core: batch api in use user=0x%02X", batch_ep);
return -1;
}
batch_ep = ep;
batch_get_more = get_more;
batch_stopped = false;
return 0;
}
int usb_drv_batch_deinit(void)
{
logf("usb_core: batch deinit");
usb_drv_batch_stop();
batch_ep = 0;
return 0;
}
static int batch_get_and_send(void)
{
const void* ptr;
size_t len;
batch_get_more(&ptr, &len);
if(len == 0 || batch_stopped) {
return 0;
}
return usb_drv_send_nonblocking(EP_NUM(batch_ep), (void*)ptr, len);
}
int usb_drv_batch_start(void)
{
logf("usb_core: batch start");
batch_stopped = false;
return batch_get_and_send();
}
int usb_drv_batch_stop(void)
{
batch_stopped = true;
return 0;
}
static void batch_xfer_complete(void) {
if(batch_stopped) {
return;
}
batch_get_and_send();
}
#endif
static void usb_core_set_serial_function_id(void)
{
int i, id = 0;
@ -1130,8 +1188,14 @@ void usb_core_bus_reset(void)
/* called by usb_drv_transfer_completed() */
void usb_core_transfer_complete(int endpoint, int dir, int status, int length)
{
struct usb_transfer_completion_event_data* completion_event =
&ep_data[endpoint].completion_event[EP_DIR(dir)];
#ifdef USB_BATCH_NON_NATIVE
/* batch api */
if(batch_ep != 0 && (endpoint | dir) == batch_ep) {
batch_xfer_complete();
return;
}
#endif
/* Fast notification */
fast_completion_handler_t handler = ep_data[endpoint].fast_completion_handler[EP_DIR(dir)];
if(handler != NULL && handler(endpoint, dir, status, length))
@ -1154,6 +1218,9 @@ void usb_core_transfer_complete(int endpoint, int dir, int status, int length)
}
#endif
struct usb_transfer_completion_event_data* completion_event =
&ep_data[endpoint].completion_event[EP_DIR(dir)];
completion_event->endpoint = endpoint;
completion_event->dir = dir;
completion_event->data[0] = data0;

View file

@ -782,7 +782,7 @@ sub simbuilds {
'norsk' => {
'lang' => 'norsk',
'name' => 'Norsk (Norwegian)',
'short' => 'no',
'short' => 'nb',
'defengine' => 'piper',
'engines' => {
'espeak' => '-vno',
@ -791,6 +791,18 @@ sub simbuilds {
},
'enabled' => 0,
},
'norsk-nynorsk' => {
'lang' => 'norsk-nynorsk',
'name' => 'Nynorsk (Norwegian Nynorsk)',
'short' => 'nn',
'defengine' => 'piper',
'engines' => {
'espeak' => '-vno',
'gtts' => '-l no',
'piper' => 'no_NO-talesyntese-medium.onnx',
},
'enabled' => 1,
},
'moldoveneste' => {
'lang' => 'moldoveneste',
'name' => 'Moldovenească (Moldavian)',