QA1 | Why does the tempo change when I change the playback rate? |
QA2 | Playback rate settings, sequence data resolution and tempo settings. |
QA3 | Can't get rid of small noises. |
QA4 | Can't get rid of small noises Part II. |
Q1 This is a question regarding the n_audio sample demo "playseq_naudio." I understand why the pitch changes when samplerate in playseq.h is changed from 33k to 11k, but why does the tempo change?
A1 This is not a problem with the tempo of this song. The number of sample data which are processed per frame is calculated in playseq_naudio in the following segment.
fsize = (f32) NUM_FIELDS * c.outputRate / (f32) 60; frameSize = (s32) fsize; if (frameSize < fsize) frameSize++; frameSize = ((frameSize / SAMPLES) + 1) * SAMPLES; minFrameSize = frameSize - SAMPLES;
With a playback frequency of 11kHzm,
frameSize = 368 minFrameSize = 184
Meanwhile, when the playback frequency is 11kHz, the number of data necessary for one frame is
11000 / 60 = 183.33333
Therefore, excess samples are created in each frame due to the combination of the above frameSize and minFrameSize. As a result, there are data which have been created but which are not played back (audio buffer). The sequence data which are played back at this time will sound as though the tempo has been sped up. The sounds will also be erratically cut off.
There is a countermeasure to this, but, in the case of playseq_naudio, first, the segment in which the number of sample data is calculated is changed as follows.
fsize = (f32) NUM_FIELDS * c.outputRate / (f32) 60; frameSize = (s32) fsize; if (frameSize < fsize) frameSize++; minFrameSize = frameSize - SAMPLES;
At this time, at a playback frequency of 11kHz,
frameSize = 184 minFrameSize = 0
Since the application will in some cases stop if minFrameSize=0, make the following changes inside the main loop (do{...}while()).
(Before changes)
/*######### n_audio : begin #########*/ /* * Samples in this frame */ if((samplesLeft >= (SAMPLES + EXTRA_SAMPLES)) && min_only_one) { audioSamples[buf] = minFrameSize; min_only_one = 0; } else { audioSamples[buf] = frameSize; min_only_one = 1; } /*########## n_audio : end ##########*/ . . . osWritebackDCacheAll(); osSpTaskStart(tlistp); CleanDMABuffs(); . .
(After changes)
/*######### n_audio : begin #########*/ /* * Samples in this frame */ if((samplesLeft >= (SAMPLES + EXTRA_SAMPLES)) && min_only_one) { osSendMesg(&taskMessageQ, NULL, OS_MESG_BLOCK); min_only_one = 0; } else { audioSamples[buf] = frameSize; min_only_one = 1; } /*########## n_audio : end ##########*/ . . . osWritebackDCacheAll(); if(min_only_one) osSpTaskStart(tlistp); CleanDMABuffs(); . .
Thus the tempo of the song is restored.
You can use, e.g., osSyncPrintf(), to check the value of samplesLeft (the number of excess samples per frame), and here you can see that the value for samplesLeft is 0 (zero).
This could be said to be the ideal state, but, on the other hand, there is a possibility that sounds will be cut off. EXTRA_SAMPLES is available essentially for subdividing this condition, but EXTRA_SAMPLES has no significance at a playback frequency of 11kHz.
As a countermeasure, consider the method of creating sound data once every two frames.
The tempo was restored in the above revision, but noise will sometimes randomly pop into the song. This is caused by various things. Limiting the discussion to n_audio, if the envelope length (particularly the release length) is not set to a multiple of the time per 184 samples of sound data, playback will stop before the sound has been sufficiently damped, and noise will occur.
In addition, expanding the discussion beyond n_audio, if the DMA length of one iteration of sound data is too long, the DMA transfer from ROM to RAM may not get completed in time. In this case as well, noise will occur during the song. In this case, in playseq_naudio, change the following values as appropriate.
<playseq.h>
#define MAX_BUFFER_LENGTH 1024 <- One DMA length #define DMA_QUEUE_SIZE 50 <- Number of DMA message buffers
<playseq.c>
The above values are the values already in playseq_naudio.
Q2
Regarding specifying of the playback rate and setting the sequence data resolution and tempo when using n_Audio, I would like to use N_Audio with the playback rate at 22.05kHz and the sequence data resolution at 480 quarter notes, but is this possible? When I actually tried to get the sequence tempo with the above settings, I got a tempo of approximately 15bpm.
In addition, when trying to get this kind of tempo, it seems that I can't actually use the tempo which was specified in the sequence data, but that I can only use the tempo calculated by this equation. Is this true?
A2
To answer your first question, it is possible.
Since n_audio was used, in order to minimize the shift in the sounds produced, it is necessary to align the timing at which the envelope is changed, and the timing of sound generation and damping with the smallest units (184 samples) in which sounds are processed. The former is accomplished by revising the inst file, and the latter is accomplished by adjusting the tempo of the MIDI sequence.
Now, since 22,050 samples are processed in one second, there are (22050/184=119.83695(=120)) units (sub-frames) in which 184 samples are processed per second. In other words, the smallest unit for sound processing time is the inverse of this (1/120) second. The shift in the sounds produced by n_audio can be kept to a minimum if a tempo is selected that processes the sound generation time in units of integeral multiples of this time. When considering a timebase 480 MIDI sequence, if quarter notes are processed divided exactly by 480, the tempo would be ((1/120)*480=4, 60/4=15 bpm), as you have said. The tempo is no more than the smallest unit in which the inverse of this integer can be processed. (This could be thought of as the smallest unit of processing time having become an integer multiple of (1/120).) However, upon actually listening, the fine resolution used up until now may seem no longer necessary for the sequence data. Consequently, assuming that 10 units of resolution are thought of as one, and sound is produced only in those units, when 48 steps are matched to the smallest unit for sound processing of (1/120) second, the shift in sound resulting from using n_audio can be minimized by using a tempo such as 150 bpm, 75 bpm, ...
The critical thing here is that the shift in the very first sound be minimized when generating or damping sounds at the position of the integer multiple of this smallest processing unit. This can, of course, be accomplished by adjusting the tempo, but the tempo can also be corrected by playing with the Timebase in the MIDI file. (In this case, the MIDI data are directly corrected with a binary editor, or the like.) Considering, e.g., eighth notes in 24 steps, at the same tempo, processing eighth notes in 26 steps could be thought of as slowing the tempo. In any case, if you do not wish to change the sound by the sound generation timing, try making corrections around this area.
Q4
n_audio Test Environment (Can't get rid of small noises..)
The program is based on a simple system, and the routines of the n_audio related sample program are transplanted unaltered.
The data used in testing are the sequence data and other test data used by the sample program.
Test Results
The symptoms changed as follows according to the adjustment value for the extra samples specified in the program.
A4
We were also able to reproduce these popping noises. We are considering ways to quickly find the cause and implement a countermeasure. We currently believe that the noise can be avoided to some extent by adjusting the length of the envelope release time.
Example
Release time of the sin wave in sfx.inst included with the development environment : 5000 (us)
Release time set on the long side : 18000 (us)
The following source code is the main segment in this revised diagnostic version of audiomgr.c in simple.
Q4
When I played back a single sound with the sound player in the N_audio library, irregular "popping" noises occurred in the sustain portion of sounds in which the envelope did not change. (Can't get rid of small noises, part II.)
A4
Run osAiGetStatus() before executing osAiSetNextBuffer(), and try making it so that processing is not performed if the return value is AI_STATUS_FIFO_FULL (both the audio DMA registers were set when they were programmed). This conditional equation will prevent the DMABuffer value from being skipped because the DMA setting register is not empty, even if the DMA has been set.
#define NBUFFERS 124 <- Number of DMA buffers
Specifying the extra sample adjustment value as 0 is the value specified in the sample program, but when run with this value, the compatibility with the sound player deteriorated and operation became unstable. In particular, the following phenomena were observed.
If a value equal to or greater than a multiple of the maximum number of samples (= 184) were specified as the extra sample adjustment value, the problems when the extra sample adjustment value = 0 were eliminated. Instead, random popping noises occurred. It appears that the noises were more or less likely to occur depending on the waveform, but their timing was also random. They seemed to be most apparent in simple loop waveforms, such as sin waves, etc.static u32 __amHandleFrameMsg(AudioInfo *info, AudioInfo *lastInfo)
{
s16 *audioPtr;
Acmd *cmdp;
s32 cmdLen;
int samplesLeft = 0;
OSScTask *t;
/* ##### for n_audio ##### */
static u8
min_only_one = 1;
/* ##### for n_audio ##### */
/* ##### for n_audio ##### */
samplesLeft = osAiGetLength() >> 2;
/* ##### for n_audio ##### */
__clearAudioDMA(); /* call once a frame, before doing alAudioFrame */
audioPtr = (s16 *) osVirtualToPhysical(info->data);
if (lastInfo)
osAiSetNextBuffer(lastInfo->data, lastInfo->frameSamples<<2);
/* ##### for n_audio ##### */
/*
samplesLeft = osAiGetLength() >> 2;
info->frameSamples = 16 + (frameSize - samplesLeft + EXTRA_SAMPLES)&
~0xf;
if(info->frameSamples < minFrameSize)
info->frameSamples = minFrameSize;
*/
if((samplesLeft > (SAMPLES + EXTRA_SAMPLES)) && min_only_one){
info->frameSamples = minFrameSize;
min_only_one = 0;
} else {
info->frameSamples = frameSize;
min_only_one = 1;
}
/* ##### for n_audio ##### */
cmdp = alAudioFrame(__am.ACMDList[curAcmdList], &cmdLen, audioPtr,
info->frameSamples);
assert(cmdLen <= maxRSPCmds);
if(cmdLen == 0) /* no task produced, return zero to show no valid task */
return 0;
t = &info->task;
t->next = 0; /* paranoia */
t->msgQ = &__am.audioReplyMsgQ; /* reply to when finished */
t->msg = (OSMesg)&info->msg; /* reply with this message */
t->flags = OS_SC_NEEDS_RSP;
t->list.t.data_ptr = (u64 *) __am.ACMDList[curAcmdList];
t->list.t.data_size = (cmdp - __am.ACMDList[curAcmdList]) * sizeof(Acmd);
t->list.t.type = M_AUDTASK;
t->list.t.ucode_boot = (u64 *)rspbootTextStart;
t->list.t.ucode_boot_size =
((int) rspbootTextEnd - (int) rspbootTextStart);
t->list.t.flags = OS_TASK_DP_WAIT;
/* ##### for n_audio ##### */
/*
t->list.t.ucode = (u64 *) aspMainTextStart;
t->list.t.ucode_data = (u64 *) aspMainDataStart;
*/
t->list.t.ucode = (u64 *)n_aspMainTextStart;
t->list.t.ucode_data = (u64 *)n_aspMainDataStart;
t->list.t.ucode_size = SP_UCODE_SIZE;
/* ##### for n_audio ##### */
t->list.t.ucode_data_size = SP_UCODE_DATA_SIZE;
t->list.t.dram_stack = (u64 *) NULL;
t->list.t.dram_stack_size = 0;
t->list.t.output_buff = (u64 *) NULL;
t->list.t.output_buff_size = 0;
t->list.t.yield_data_ptr = NULL;
t->list.t.yield_data_size = 0;
osSendMesg(sched_cmdQ, (OSMesg) t, OS_MESG_BLOCK);
curAcmdList ^= 1; /* swap which acmd list you use each frame */
return 1;
}