Mailing List Archive

[PATCH] mono support, take two
Hi John,

On Mon, Nov 18, 2002 at 11:53:03PM -0500, John Coiner wrote:
> It would be great to parameterize all of the player/recorder code in
> terms of 'channels' and 'bytes_per_sample' etc. The option to use mono
> would be pretty sweet for people with mono tuners, especially if they
> don't have the world's fastest CPU.

Ok here it is. I actually have sound now for the first time. And
it's in sync!

> ( By the way -- if you're going to work on the player -- take a minute
> to see if you can figure out how the audio buffer's read pointer could
> "lap" the write pointer. This should never happen, though Isaac seems to
> have observed just this. We were stymied, the code looked pretty solid,
> to me anyway...)

Will do, now that I have a better grasp on the code.

-Jim

Index: libs/libNuppelVideo/NuppelVideoPlayer.cpp
===================================================================
RCS file: /var/lib/cvs/MC/libs/libNuppelVideo/NuppelVideoPlayer.cpp,v
retrieving revision 1.96
diff -u -r1.96 NuppelVideoPlayer.cpp
--- libs/libNuppelVideo/NuppelVideoPlayer.cpp 18 Nov 2002 01:59:38 -0000 1.96
+++ libs/libNuppelVideo/NuppelVideoPlayer.cpp 19 Nov 2002 10:37:59 -0000
@@ -41,6 +41,7 @@
#define wsEnter 0x8d + 256
#define wsReturn 0x0d + 256

+int audio_ioctl_int(int fd, int arg, int *value, const char *decscription);

NuppelVideoPlayer::NuppelVideoPlayer(void)
{
@@ -75,7 +76,9 @@
weMadeBuffer = false;

osd = NULL;
+ audio_channels = 2;
audio_samplerate = 44100;
+ audio_bits_per_sample = 16;
editmode = false;
advancevideo = resetvideo = advancedecoder = false;

@@ -171,13 +174,13 @@

void NuppelVideoPlayer::InitSound(void)
{
- int bits = 16, stereo = 1, speed = audio_samplerate, caps;
+ int caps;

if (usingextradata)
{
- bits = extradata.audio_bits_per_sample;
- stereo = (extradata.audio_channels == 2);
- speed = extradata.audio_sample_rate;
+ audio_bits_per_sample = extradata.audio_bits_per_sample;
+ audio_channels = extradata.audio_channels;
+ audio_samplerate = extradata.audio_sample_rate;
}

if (disableaudio)
@@ -194,25 +197,10 @@
return;
}

- if (ioctl(audiofd, SNDCTL_DSP_SAMPLESIZE, &bits) < 0)
- {
- cerr << "problem setting sample size, exiting\n";
- close(audiofd);
- audiofd = -1;
- return;
- }
-
- if (ioctl(audiofd, SNDCTL_DSP_STEREO, &stereo) < 0)
- {
- cerr << "problem setting to stereo, exiting\n";
- close(audiofd);
- audiofd = -1;
- return;
- }
-
- if (ioctl(audiofd, SNDCTL_DSP_SPEED, &speed) < 0)
+ if(audio_ioctl_int(audiofd, SNDCTL_DSP_CHANNELS, &audio_channels, "audio output channels") < 0 ||
+ audio_ioctl_int(audiofd, SNDCTL_DSP_SPEED, &audio_samplerate, "audio output samplerate") < 0 ||
+ audio_ioctl_int(audiofd, SNDCTL_DSP_SAMPLESIZE, &audio_bits_per_sample, "audio output bits per sample") < 0)
{
- cerr << "problem setting sample rate, exiting\n";
close(audiofd);
audiofd = -1;
return;
@@ -480,7 +468,11 @@
foundit = 0;
effdsp = audio_samplerate;
if (usingextradata)
+ {
effdsp = extradata.audio_sample_rate;
+ audio_channels = extradata.audio_channels;
+ audio_bits_per_sample = extradata.audio_bits_per_sample;
+ }

while (!foundit)
{
@@ -856,8 +848,8 @@
ioctl(audiofd, SNDCTL_DSP_GETODELAY, &soundcard_buffer); // bytes
totalbuffer = audiolen(false) + soundcard_buffer;

- audiotime = audbuf_timecode - (int)((double)totalbuffer * 25000.0 /
- (double)effdsp);
+ audiotime = audbuf_timecode -
+ (int)(totalbuffer * 100000.0 / (audio_bits_per_sample/8 * audio_channels * effdsp));

gettimeofday(&audiotime_updated, NULL);

@@ -993,17 +985,29 @@

do
{
- lameret = lame_decode(strm, packetlen, pcmlbuffer,
- pcmrbuffer);
+ mp3data_struct header;
+ lameret = lame_decode_headers(strm, packetlen, pcmlbuffer,
+ pcmrbuffer, &header);

if (lameret > 0)
{
int itemp = 0;
int afree = audiofree(false);

- if (lameret * 4 > afree)
+ if(header.header_parsed && header.stereo != audio_channels &&
+ audio_ioctl_int(audiofd, SNDCTL_DSP_CHANNELS, &(audio_channels=header.stereo),
+ "audio mp3 output channels") < 0 ||
+ header.header_parsed && header.samplerate != audio_samplerate &&
+ audio_ioctl_int(audiofd, SNDCTL_DSP_SPEED, &(audio_samplerate=header.samplerate),
+ "audio mp3 output samplerate") < 0)
+ {
+ close(audiofd);
+ audiofd = -1;
+ }
+
+ if (lameret * audio_channels * audio_bits_per_sample/8 > afree)
{
- lameret = afree / 4;
+ lameret = afree / audio_channels / (audio_bits_per_sample/8);
cout << "Audio buffer overflow, audio data lost!\n";
}

@@ -1011,11 +1015,17 @@

for (itemp = 0; itemp < lameret; itemp++)
{
- saudbuffer[waud / 2] = pcmlbuffer[itemp];
- saudbuffer[waud / 2 + 1] = pcmrbuffer[itemp];
-
- waud += 4;
- len += 4;
+ if(audio_channels == 2)
+ {
+ saudbuffer[waud / 2] = pcmlbuffer[itemp];
+ saudbuffer[waud / 2 + 1] = pcmrbuffer[itemp];
+ }
+ else
+ {
+ saudbuffer[waud] = pcmlbuffer[itemp];
+ }
+ waud += audio_channels * audio_bits_per_sample/8;
+ len += audio_channels * audio_bits_per_sample/8;
if (waud >= AUDBUFSIZE)
waud -= AUDBUFSIZE;
}
@@ -1388,8 +1398,8 @@
/* do audio output */

/* approximate # of audio bytes for each frame. */
- bytesperframe = 4 * (int)((1.0/video_frame_rate) *
- ((double)effdsp/100.0) + 0.5);
+ bytesperframe = (audio_channels * audio_bits_per_sample/8) *
+ (int)(effdsp / 100.0 / video_frame_rate + 0.5);

// wait for the buffer to fill with enough to play
if (bytesperframe >= audiolen(true))
Index: libs/libNuppelVideo/NuppelVideoPlayer.h
===================================================================
RCS file: /var/lib/cvs/MC/libs/libNuppelVideo/NuppelVideoPlayer.h,v
retrieving revision 1.48
diff -u -r1.48 NuppelVideoPlayer.h
--- libs/libNuppelVideo/NuppelVideoPlayer.h 15 Nov 2002 00:12:07 -0000 1.48
+++ libs/libNuppelVideo/NuppelVideoPlayer.h 19 Nov 2002 10:37:59 -0000
@@ -183,6 +183,8 @@
unsigned char *buf2;
char lastct;
int effdsp; // from the recorded stream
+ int audio_channels; // from the recorded stream
+ int audio_bits_per_sample; // per channel
int audio_samplerate; // rate to tell the output device
int filesize;
int startpos;
Index: libs/libNuppelVideo/NuppelVideoRecorder.cpp
===================================================================
RCS file: /var/lib/cvs/MC/libs/libNuppelVideo/NuppelVideoRecorder.cpp,v
retrieving revision 1.62
diff -u -r1.62 NuppelVideoRecorder.cpp
--- libs/libNuppelVideo/NuppelVideoRecorder.cpp 18 Nov 2002 01:59:38 -0000 1.62
+++ libs/libNuppelVideo/NuppelVideoRecorder.cpp 19 Nov 2002 10:38:00 -0000
@@ -24,6 +24,21 @@

pthread_mutex_t avcodeclock = PTHREAD_MUTEX_INITIALIZER;

+int audio_ioctl_int(int fd, int arg, int *value, const char *decscription)
+{
+ int ret, orig = *value;
+
+ cout << "setting " << decscription << " to " << *value << endl;
+
+ if ((ret=ioctl(fd, arg, value)) < 0)
+ cerr << "problem setting " << decscription << ", exiting\n";
+ else
+ if(orig != *value)
+ cout << decscription << " forced to use " << *value
+ << " instead of " << orig << endl;
+ return ret;
+}
+
NuppelVideoRecorder::NuppelVideoRecorder(void)
{
sfilename = "output.nuv";
@@ -71,6 +86,8 @@
keyframedist = KEYFRAMEDIST;

audiobytes = 0;
+ audio_channels = 2;
+ audio_bits_per_sample = 16;
audio_samplerate = 44100;

picture_format = PIX_FMT_YUV420P;
@@ -259,6 +276,8 @@
lame_set_bWriteVbrTag(gf, 0);
lame_set_quality(gf, mp3quality);
lame_set_compression_ratio(gf, 11);
+ lame_set_mode(gf, audio_channels == 2 ? STEREO : MONO);
+ lame_set_num_channels(gf, audio_channels);
lame_set_out_samplerate(gf, audio_samplerate);
lame_set_in_samplerate(gf, audio_samplerate);
lame_init_params(gf);
@@ -313,5 +332,5 @@
int NuppelVideoRecorder::AudioInit(void)
{
int afmt, afd;
- int frag, channels, rate, blocksize = 4096;
+ int frag, blocksize = 4096;

@@ -331,30 +351,18 @@
if (afmt != AFMT_S16_LE)
{
cerr << "Can't get 16 bit DSP, exiting\n";
- return(1);
- }
-
- channels = 2;
- ioctl(afd, SNDCTL_DSP_CHANNELS, &channels);
-
- /* sample rate */
- rate = audio_samplerate;
- if (ioctl(afd, SNDCTL_DSP_SPEED, &rate) < 0)
- {
- cerr << "setting sample rate failed, exiting\n";
return 1;
}

- if (rate != audio_samplerate)
- {
- cerr << "setting sample rate to " << audio_samplerate << " failed\n";
- return 1;
- }
+ if(audio_ioctl_int(afd, SNDCTL_DSP_CHANNELS, &audio_channels, "audio input channels") < 0 ||
+ audio_ioctl_int(afd, SNDCTL_DSP_SPEED, &audio_samplerate, "audio input samplerate") < 0 ||
+ audio_ioctl_int(afd, SNDCTL_DSP_SAMPLESIZE, &audio_bits_per_sample, "audio input bits per sample") < 0)
+ return 1;

if (-1 == ioctl(afd, SNDCTL_DSP_GETBLKSIZE, &blocksize))
{
cerr << "Can't get DSP blocksize, exiting\n";
- return(1);
+ return 1;
}
blocksize *= 4;

@@ -1107,8 +1115,8 @@
}

moredata.audio_sample_rate = audio_samplerate;
- moredata.audio_channels = 2;
- moredata.audio_bits_per_sample = 16;
+ moredata.audio_channels = audio_channels;
+ moredata.audio_bits_per_sample = audio_bits_per_sample;

extendeddataOffset = ringBuffer->GetFileWritePosition();

@@ -1238,8 +1246,8 @@
{
int afmt = 0, trigger = 0;
int afd = 0, act = 0, lastread = 0;
- int frag = 0, channels = 0, rate = 0, blocksize = 0;
+ int frag = 0, blocksize = 0;
unsigned char *buffer;
audio_buf_info ispace;
struct timeval anow;

@@ -1265,12 +1274,13 @@
return;
}

- channels = 2;
- ioctl(afd, SNDCTL_DSP_CHANNELS, &channels);
-
- /* sample rate */
- rate = audio_samplerate;
- ioctl(afd, SNDCTL_DSP_SPEED, &rate);
+ if(audio_ioctl_int(afd, SNDCTL_DSP_CHANNELS, &audio_channels, "audio input channels") < 0 ||
+ audio_ioctl_int(afd, SNDCTL_DSP_SPEED, &audio_samplerate, "audio input samplerate") < 0 ||
+ audio_ioctl_int(afd, SNDCTL_DSP_SAMPLESIZE, &audio_bits_per_sample, "audio input bits per sample") < 0)
+ {
+ close(afd);
+ return;
+ }

if (-1 == ioctl(afd, SNDCTL_DSP_GETBLKSIZE, &blocksize))
{
@@ -1339,8 +1349,8 @@
/* Back up the timecode. The more stuff is in the hw buffer,
the earlier this audio was actually recorded. */
audiobuffer[act]->timecode -=
- (int) ( ( (double)ispace.fragments * (double)ispace.fragsize * 1000.0 ) /
- ( (double)audio_samplerate * 4.0 ) );
+ (int)(ispace.fragments * ispace.fragsize * 1000.0 /
+ (audio_samplerate * audio_bits_per_sample/8 * audio_channels));

memcpy(audiobuffer[act]->buffer, buffer, audio_buffer_size);

@@ -1728,12 +1738,12 @@
// wrong guess ;-)
// need seconds instead of msec's
//mt = (double)timecode/1000.0;
- mt = (double)timecode;
+ mt = timecode;
if (mt > 0.0)
{
//eff = (abytes/4.0)/mt;
//effectivedsp=(int)(100.0*eff);
- eff = (abytes/mt)*((double)100000.0/(double)4.0);
+ eff = (abytes/mt)*(100000.0 / (audio_bits_per_sample/8) / audio_channels);
effectivedsp=(int)eff;
}
}
@@ -1745,10 +1755,21 @@
int gaplesssize = 0;
int lameret = 0;

- lameret = lame_encode_buffer_interleaved(gf, (short int *)buf,
- audio_buffer_size / 4,
- (unsigned char *)mp3buf,
- mp3buf_size);
+ if(audio_channels == 2)
+ {
+ lameret = lame_encode_buffer_interleaved(gf, (short int *)buf,
+ audio_buffer_size / audio_channels / (audio_bits_per_sample/8),
+ (unsigned char *)mp3buf,
+ mp3buf_size);
+ }
+ else
+ {
+ lameret = lame_encode_buffer(gf, (short int *)buf, (short int *)buf,
+ audio_buffer_size / audio_channels / (audio_bits_per_sample/8),
+ (unsigned char *)mp3buf,
+ mp3buf_size);
+ }
+
if (lameret < 0)
{
cerr << "lame error, exiting\n";
Index: libs/libNuppelVideo/NuppelVideoRecorder.h
===================================================================
RCS file: /var/lib/cvs/MC/libs/libNuppelVideo/NuppelVideoRecorder.h,v
retrieving revision 1.25
diff -u -r1.25 NuppelVideoRecorder.h
--- libs/libNuppelVideo/NuppelVideoRecorder.h 18 Nov 2002 01:59:38 -0000 1.25
+++ libs/libNuppelVideo/NuppelVideoRecorder.h 19 Nov 2002 10:38:00 -0000
@@ -126,6 +126,8 @@
int compression;
int compressaudio;
unsigned long long audiobytes;
+ int audio_channels; // channels to request from sounddevice
+ int audio_bits_per_sample; // per channel
int audio_samplerate; // rate we request from sounddevice
int effectivedsp; // actual measured rate
Re: Re: [PATCH] mono support, take two [ In reply to ]
On Tue, Nov 19, 2002 at 08:49:53PM -0500, Isaac Richards wrote:
> Is there really a need for the audio_ioctl_int stuff? It's, well, ugly, IMO.

It is there, more or less, just to gather the debugging prints in one
location so I could turn them off at once, so that I didn't keep
having cut and paste errors :-), and so that the error checking was
uniform. I left them in so you could see why the other code is
necessary (see below). You can replace audio_ioctl_int with ioctl and
everything should work fine.

> There shouldn't be a need for detecting if the samplerate/channels changed in
> the middle of the stream, either..

The information seems to only be available "in the middle of the
stream", and it only runs once as far as I can tell. The code can be
moved out-of-line if you're worried about cache footprint. The
problem is that lame, like oss, doesn't use what you tell it to.
Specifically when I tell it to do one channel and mono it doesn't do
it, it converts it to two channels. So the myth header says 1 channel
(which it would be without compression) and the lame header says 2.
So first we set up one channel and then lame comes along and says, no
I really used two.

The overall architectural thing that could be fixed here would be to
figure out what the input/output/stream support before starting up and
then choosing the appropriate mode and possibly conversion routines.

-Jim
Re: Re: [PATCH] mono support, take two [ In reply to ]
On Wed, Nov 20, 2002 at 12:24:13AM -0500, John Coiner wrote:
> We can dial these parameters into the sound card once, at the start
> of playing.

> So, I think the code to update them on the fly would never run. We don't
> want to have code hanging around that never runs, except error checking.

But the information is only available once you have parsed the stream
and it only gets returned once as long as the stream is uniform.
Secondly, it is possible and it happens in my case that the myth
header says mono and the lame header says stereo. See my previous
message.

> After this patch does go in, I may put a knob in settings.txt to allow a
> user to optionally specify a preference for mono or stereo. For example,
> if you had a mono tuner and a stereo sound card, you would want to
> record and encode mono (since everything downstream of the tuner is L==R
> anyway) and save the extra CPU cycles. Does that seem reasonable?

That works now automatically. The case we don't currently handle well
is when the tuner is stereo and the sound card is mono. Should we
record in stereo and drop data when playing but have a good recording,
or just drop the data when recording? That's more a user choice,

Let me know if you still object and I'll try and come up with a
different solution.

-Jim
Re: Re: [PATCH] mono support, take two [ In reply to ]
On Thu, Nov 21, 2002 at 08:55:16PM -0500, Isaac Richards wrote:
> In your patch, it looks like you're telling it to do stereo no matter what,
> since it's initializing the lame stuff before it probes the audio..

You're right, I missed that.

> Think if the initialization order's fixed, that would be too?

Yep. I'll look into it and send a new patch.

-Jim
Re: Re: [PATCH] mono support, take two [ In reply to ]
On Tuesday 19 November 2002 09:43 pm, John Coiner wrote:
> Jim,
>
> Thanks for the patch, it looks good.
>
> I plan to check it in as soon as I get a chance to test it (just to see
> that stereo still works.) Right now the box is busy taping something.

Is there really a need for the audio_ioctl_int stuff? It's, well, ugly, IMO.

There shouldn't be a need for detecting if the samplerate/channels changed in
the middle of the stream, either..

Isaac
Re: [PATCH] mono support, take two [ In reply to ]
Jim,

Thanks for the patch, it looks good.

I plan to check it in as soon as I get a chance to test it (just to see
that stereo still works.) Right now the box is busy taping something.

john
Re: [PATCH] mono support, take two [ In reply to ]
oh yeah, i missed that with the changing samplerate and bitdepth on the
fly. That's sketchy... It is safe to assume uniform parameters
throughout the stream. We can dial these parameters into the sound card
once, at the start of playing.

So, I think the code to update them on the fly would never run. We don't
want to have code hanging around that never runs, except error checking.

Jim, could you please resend without that? Or is there a reason it's
necessary?

After this patch does go in, I may put a knob in settings.txt to allow a
user to optionally specify a preference for mono or stereo. For example,
if you had a mono tuner and a stereo sound card, you would want to
record and encode mono (since everything downstream of the tuner is L==R
anyway) and save the extra CPU cycles. Does that seem reasonable?

thanks.

-- john
Re: Re: [PATCH] mono support, take two [ In reply to ]
On Saturday 16 November 2002 11:43 am, Jim Radford wrote:
> The information seems to only be available "in the middle of the
> stream", and it only runs once as far as I can tell. The code can be
> moved out-of-line if you're worried about cache footprint. The
> problem is that lame, like oss, doesn't use what you tell it to.
> Specifically when I tell it to do one channel and mono it doesn't do
> it, it converts it to two channels. So the myth header says 1 channel
> (which it would be without compression) and the lame header says 2.
> So first we set up one channel and then lame comes along and says, no
> I really used two.

In your patch, it looks like you're telling it to do stereo no matter what,
since it's initializing the lame stuff before it probes the audio.. Think if
the initialization order's fixed, that would be too?

Isaac