예제 #1
0
void mp3_handle_input(unsigned char *buffer, size_t buf_size)
{
	if (buf_size == 3) {
		for (int i = 0; i < buf_size; i++) {
			switch (buffer[i]) {
				case 0xFF:
					// button
					if (buffer[i + 1] == 2) {
						printf("button_press[%d]: %d\n", buffer[i+1], buffer[i+2]);
						if (buffer[i + 2] == 0) {
							mp3_index ^= 0x01;
							mpg123_seek	(mh_0, 0, SEEK_SET);
							mpg123_seek	(mh_1, 0, SEEK_SET);
						}
					}
					i += 2;
					break;
				case 0xFE:
					printf("accelerometer x: %d y: %d z: %d\n", buffer[i+1], buffer[i+2], buffer[i+3]);
					i += 3;
				default:
					break;
			}
		}
	}
}
예제 #2
0
bool ofxSoundFile::mpg123Open(string path){
	int err;
	mp3File = mpg123_new(NULL,&err);

	mpg123_param(mp3File, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT, 0.);

	if(mpg123_open(mp3File,path.c_str())!=MPG123_OK){
		ofLogError() <<  "ofxSoundFile: couldnt read " + path;
		return false;
	}

	int encoding;
	long int rate;
	mpg123_getformat(mp3File,&rate,&channels,&encoding);
	if(encoding!=MPG123_ENC_SIGNED_16 && encoding != MPG123_ENC_FLOAT_32){
		ofLogError() <<  "ofxSoundFile: unsupported encoding";
		return false;
	}

	mpg123_format_none(mp3File);
	mpg123_format(mp3File, rate, channels, encoding);

	samplerate = rate;


	mpg123_seek(mp3File,0,SEEK_END);
	samples = mpg123_tell(mp3File);
	mpg123_seek(mp3File,0,SEEK_SET);
    
    bitDepth = 16; //TODO:get real bitdepth;.
}
//------------------------------------------------------------
bool ofOpenALSoundPlayer::mpg123Stream(string path,vector<short> & buffer,vector<float> & fftAuxBuffer){
	if(!mp3streamf){
		int err = MPG123_OK;
		mp3streamf = mpg123_new(NULL,&err);
		if(mpg123_open(mp3streamf,path.c_str())!=MPG123_OK){
			mpg123_close(mp3streamf);
			mpg123_delete(mp3streamf);
			ofLogError("ofOpenALSoundPlayer") << "mpg123Stream(): couldn't read \"" << path << "\"";
			return false;
		}

		long int rate;
		mpg123_getformat(mp3streamf,&rate,&channels,(int*)&stream_encoding);
		if(stream_encoding!=MPG123_ENC_SIGNED_16){
			ofLogError("ofOpenALSoundPlayer") << "mpg123Stream(): " << getMpg123EncodingString(stream_encoding)
			<< " encoding for \"" << path << "\"" << " unsupported, expecting MPG123_ENC_SIGNED_16";
			return false;
		}
		samplerate = rate;
		mp3_buffer_size = mpg123_outblock( mp3streamf );


		mpg123_seek(mp3streamf,0,SEEK_END);
		off_t samples = mpg123_tell(mp3streamf);
		duration = float(samples/channels) / float(samplerate);
		mpg123_seek(mp3streamf,0,SEEK_SET);
	}

	int curr_buffer_size = mp3_buffer_size;
	if(speed>1) curr_buffer_size *= (int)round(speed);
	buffer.resize(curr_buffer_size);
	fftAuxBuffer.resize(buffer.size());
	size_t done=0;
	if(mpg123_read(mp3streamf,(unsigned char*)&buffer[0],curr_buffer_size*2,&done)==MPG123_DONE){
		setPosition(0);
		buffer.resize(done/2);
		fftAuxBuffer.resize(done/2);
		if(!bLoop) stopThread();
		stream_end = true;
	}


	for(int i=0;i<(int)buffer.size();i++){
		fftAuxBuffer[i] = float(buffer[i])/32565.f;
	}

	return true;
}
예제 #4
0
int check_sample(struct seeko *so, size_t i)
{
	short buf[2]; /* at max one left and right sample */
	size_t got = 0;

	if(mpg123_seek(m, so->position[i], SEEK_SET) != so->position[i])
	{
		printf("Error seeking to %"OFF_P": %s\n", (off_p)so->position[i],  mpg123_strerror(m));
		return -1;
	}

	if( mpg123_read(m, (unsigned char*)buf, channels*sizeof(short), &got) != MPG123_OK
		 ||
		 got/sizeof(short) != channels )
	{
		printf("Error occured on reading sample %"SIZE_P"! (%s)\n", (size_p)i, mpg123_strerror(m));
		return -1;
	}
	if(buf[0] == so->left[i] && (channels == 1 || buf[1] == so->right[i]))
	{
		printf("sample %"SIZE_P" PASS\n", (size_p)i);
	}
	else
	{
		if(channels == 1) printf("sample %"SIZE_P" FAIL (%i != %i)\n", (size_p)i, buf[0], so->left[i]);
		else printf("sample %"SIZE_P" FAIL (%i != %i || %i != %i)\n", (size_p)i, buf[0], so->left[i], buf[1], so->right[i]);
		return -1;
	}
	return 0;
}
예제 #5
0
bool MPG123Decoder::seek(size_t ms_offset, bool ms, bool mayrestart)
{
	int enc, channels;
	long srate;

	if (!mayrestart || ms_offset > 0)
	{
		if (mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK)
		{
			size_t smp_offset = ms ? (size_t)((double)ms_offset / 1000. * srate) : ms_offset;
			if (mpg123_seek(MPG123, (off_t)smp_offset, SEEK_SET) >= 0)
			{
				Done = false;
				return true;
			}
		}
		return false;
	}
	else
	{
		// Restart the song instead of rewinding. A rewind seems to cause distortion when done repeatedly.
		// offset is intentionally ignored here.
		if (MPG123)
		{
			mpg123_close(MPG123);
			mpg123_delete(MPG123);
			MPG123 = 0;
		}
		Reader.Seek(0, FileReader::SeekSet);
		// Do not call open with our own reader variable, that would be catastrophic.
		auto reader = std::move(Reader);
		return open(reader);
	}
}
예제 #6
0
bool MPG123Decoder::seek(size_t ms_offset, bool ms, bool mayrestart)
{
	int enc, channels;
	long srate;

	if (!mayrestart || ms_offset > 0)
	{
		if (mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK)
		{
			size_t smp_offset = ms ? (size_t)((double)ms_offset / 1000. * srate) : ms_offset;
			if (mpg123_seek(MPG123, (off_t)smp_offset, SEEK_SET) >= 0)
			{
				Done = false;
				return true;
			}
		}
		return false;
	}
	else
	{
		// Restart the song instead of rewinding. A rewind seems to cause distortion when done repeatedly.
		// offset is intentionally ignored here.
		if (MPG123)
		{
			mpg123_close(MPG123);
			mpg123_delete(MPG123);
			MPG123 = 0;
		}
		Reader->Seek(0, SEEK_SET);
		return open(Reader);
	}
}
예제 #7
0
int Waveform::GetTrackSize(mpg123_handle *mh,int bits, int channels)
{
    size_t buffer_size;
    unsigned char *buffer;
    size_t done;
    int trackSize=0;
    int fileSize=0;

    if(mpg123_length(mh) > 0)
    {
        return mpg123_length(mh);
    }

    buffer_size = mpg123_outblock(mh);
    buffer = (unsigned char*) malloc(buffer_size * sizeof(unsigned char));

    mpg123_seek(mh,0,SEEK_SET);
    for (fileSize = 0 ; mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK ; )
    {
        fileSize += done;
    }

    free(buffer);
    trackSize = fileSize/(bits*channels);
    return trackSize;
}
예제 #8
0
/* Stop playback while seeking if buffer is involved. */
static void seekmode(mpg123_handle *mh, out123_handle *ao)
{
	if(param.usebuffer && !stopped)
	{
		int channels = 0;
		int encoding = 0;
		int pcmframe;
		off_t back_samples = 0;

		stopped = TRUE;
		out123_pause(ao);
		mpg123_getformat(mh, NULL, &channels, &encoding);
		pcmframe = out123_encsize(encoding)*channels;
		if(pcmframe > 0)
			back_samples = out123_buffered(ao)/pcmframe;
		fprintf(stderr, "\nseeking back %"OFF_P" samples from %"OFF_P"\n"
		,	(off_p)back_samples, (off_p)mpg123_tell(mh));
		mpg123_seek(mh, -back_samples, SEEK_CUR);
		out123_drop(ao);
		fprintf(stderr, "\ndropped, now at %"OFF_P"\n"
		,	(off_p)mpg123_tell(mh));
		fprintf(stderr, "%s", MPG123_STOPPED_STRING);
		if(param.verbose)
			print_stat(mh, 0, ao);
	}
}
예제 #9
0
static void generic_load(mpg123_handle *fr, char *arg, int state)
{
    out123_drop(ao);
    if(mode != MODE_STOPPED)
    {
        close_track();
        mode = MODE_STOPPED;
    }
    if(!open_track(arg))
    {
        generic_sendmsg("E Error opening stream: %s", arg);
        generic_sendmsg("P 0");
        return;
    }
    mpg123_seek(fr, 0, SEEK_SET); /* This finds ID3v2 at beginning. */
    if(mpg123_meta_check(fr) & MPG123_NEW_ID3)
    {
        generic_sendinfoid3(fr);
    }
    else generic_sendinfo(arg);

    if(htd.icy_name.fill) generic_sendmsg("I ICY-NAME: %s", htd.icy_name.p);
    if(htd.icy_url.fill)  generic_sendmsg("I ICY-URL: %s", htd.icy_url.p);

    mode = state;
    init = 1;
    generic_sendmsg(mode == MODE_PAUSED ? "P 1" : "P 2");
}
예제 #10
0
void DecoderMPG123::seek(qint64 pos)
{
    if(m_totalTime > 0)
    {
        mpg123_seek(m_handle, pos * m_rate / 1000, SEEK_SET);
    }
}
예제 #11
0
파일: plain_id3.c 프로젝트: 5py/libmpg123
int test_whence(const char* path, int scan_before)
{
	int err = MPG123_OK;
	mpg123_handle* mh = NULL;
	off_t length, pos;

	mh = mpg123_new(NULL, &err );
	if(mh == NULL) return -1;

	err = mpg123_open(mh, path );
	if(err != MPG123_OK) return -1;

	if(scan_before) mpg123_scan(mh);

	pos = mpg123_seek( mh, 0, SEEK_END);
	if(pos < 0){ error1("seek failed: %s", mpg123_strerror(mh)); return -1; }

	pos = mpg123_tell(mh);
	length = mpg123_length(mh);

	/* Later: Read samples and compare different whence values with identical seek positions. */

	mpg123_close(mh);
	mpg123_delete(mh);

	fprintf(stdout, "length %"OFF_P" vs. pos %"OFF_P"\n", length, pos);

	return (pos == length) ? 0 : -1;
}
예제 #12
0
bool Mpg123Decoder::rewind()
{
	eof = false;

	if (mpg123_seek(handle, 0, SEEK_SET) >= 0)
		return true;
	else
		return false;
}
예제 #13
0
파일: mpg123.cpp 프로젝트: udoprog/momd
void input_mpg123::seek(double pos)
{
    int err;
    off_t off = mpg123_timeframe(handle, pos);

    if (off < 0) {
        throw input_error(mpg123_plain_strerror(off));
    }

    if ((err = mpg123_seek(handle, off, SEEK_SET)) != MPG123_OK) {
        throw input_error(mpg123_plain_strerror(err));
    }
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::setPosition(float pct){
	if(sources.empty()) return;
	if(mp3streamf){
		mpg123_seek(mp3streamf,duration*pct*samplerate*channels,SEEK_SET);
	}else if(streamf){
		sf_seek(streamf,duration*pct*samplerate*channels,SEEK_SET);
		stream_samples_read = 0;
	}else{
		for(int i=0;i<(int)channels;i++){
			alSourcef(sources[sources.size()-channels+i],AL_SEC_OFFSET,pct*duration);
		}
	}
}
예제 #15
0
bool Mpg123Decoder::seek(float s)
{
	off_t offset = (off_t) (s * (double) sampleRate);

	if (offset < 0)
		return false;

	if (mpg123_seek(handle, offset, SEEK_SET) >= 0)
	{
		eof = false;
		return true;
	}
	else
		return false;
}
void ofOpenALSoundPlayer::setPositionMS(int ms){
	if(sources.empty()) return;
#ifdef OF_USING_MPG123
	if(mp3streamf){
		mpg123_seek(mp3streamf,float(ms)/1000.f*samplerate*channels,SEEK_SET);
	}else
#endif
	if(streamf){
		sf_seek(streamf,float(ms)/1000.f*samplerate*channels,SEEK_SET);
		stream_samples_read = 0;
	}else{
		for(int i=0;i<(int)channels;i++){
			alSourcef(sources[sources.size()-channels+i],AL_SEC_OFFSET,float(ms)/1000.f);
		}
	}
}
예제 #17
0
//--------------------------------------------------------------
bool ofxSoundFile::seekTo(unsigned int sample){
	sample = min(samples,sample);
#ifdef OF_USING_SNDFILE
	if(sndFile) sf_seek(sndFile,sample,SEEK_SET);
#endif
#ifdef OF_USING_LAD
	if(audioDecoder) audioDecoder->seek(sample);
#endif
	
#ifdef OF_USING_MPG123
	else if(mp3File) mpg123_seek(mp3File,sample,SEEK_SET);
#endif
	else return false;
	
	return true; //TODO: check errors
}
예제 #18
0
static switch_status_t shout_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence)
{
	shout_context_t *context = handle->private_info;

	if (handle->handler || switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
		return SWITCH_STATUS_FALSE;
	} else {
		if (whence == SWITCH_SEEK_CUR) {
			samples -= switch_buffer_inuse(context->audio_buffer) / sizeof(int16_t);
		}

		switch_buffer_zero(context->audio_buffer);
		*cur_sample = mpg123_seek(context->mh, (off_t) samples, whence);

		return *cur_sample >= 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
	}
}
//------------------------------------------------------------
void ofOpenALSoundPlayer_TimelineAdditions::setPosition(float pct){
	if(sources.empty()) return;
#ifdef OF_USING_MPG123
	if(mp3streamf){
		mpg123_seek(mp3streamf,duration*pct*samplerate*channels,SEEK_SET);
	}else
#endif
	if(streamf){
		sf_seek(streamf,duration*pct*samplerate*channels,SEEK_SET);
		stream_samples_read = 0;
	}else{
		for(int i=0;i<(int)channels;i++){
			alSourcef(sources[sources.size()-channels+i],AL_SEC_OFFSET,pct*duration);
		}
	}
    timeSet = true;
    justSetTime = pct;
}
예제 #20
0
파일: sound.c 프로젝트: mahiso/JudoShiai
static void start_sound(gpointer ok)

{
    size_t done;

    G_LOCK(sound);

    if (mh == NULL || dev == NULL || sound_file == NULL) {
        G_UNLOCK(sound);
        return;
    }

    mpg123_seek(mh, 0, SEEK_SET);

    while (play && (*((gboolean *)ok)) &&
           mpg123_read(mh, (unsigned char *)buffer, buffer_size, &done) == MPG123_OK)
        ao_play(dev, buffer, done);

    G_UNLOCK(sound);
}
예제 #21
0
void Waveform::LoadTrackData(mpg123_handle *mh,char  * data, int maxSize)
{
    size_t buffer_size;
    unsigned char *buffer;
    size_t done;
    int bytesRead=0;
    buffer_size = mpg123_outblock(mh);
    buffer = (unsigned char*) malloc(buffer_size * sizeof(unsigned char));
    mpg123_seek(mh,0,SEEK_SET);
    for (bytesRead = 0 ; mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK ; )
    {
        if ((bytesRead + done) >= maxSize) {
            wxMessageBox("Error reading data from mp3, too much data read.");
            free(buffer);
            return;
        }
        memcpy(data+bytesRead,buffer,done);
        bytesRead+=done;
    }
    free(buffer);
}
예제 #22
0
  void MP3InputStream::reset()
  {
    ADR_GUARD("MP3InputStream::reset");

    m_file->seek(0, File::BEGIN);
    m_eof = false;

    m_buffer.clear();

    if (m_seekable) mpg123_seek(mh, 0, SEEK_SET);
     else
     {
         mpg123_close(mh);
         mpg123_open_feed(mh);
     }


    m_input_position = 0;
    m_input_length = 0;
    m_position = 0;
  }
예제 #23
0
int MusicPlayer::pullAudio(AUDIO_SAMPLE_TYPE *target, int sampleCount)
	{
	memset(target, 0, sampleCount * sizeof(AUDIO_SAMPLE_TYPE));
#ifdef USE_OGG
	if(v && error==0)
		{
		int samples = stb_vorbis_get_samples_short_interleaved(v, channels, target, sampleCount);
		if(samples==0)
			{
			stb_vorbis_seek_start(v);
			stb_vorbis_get_samples_short_interleaved(v, channels, target, sampleCount);
			// error=-1;
			}
		}
#endif

#ifdef USE_MP3
	if (!error)
		{
		size_t done;
		error = mpg123_read(mh, (unsigned char *) target, sampleCount
				* sizeof(AUDIO_SAMPLE_TYPE), &done);
		// read will return MPG123_DONE eventually...

		if (error == MPG123_DONE)
			{

			mpg123_seek(mh, 0, SEEK_SET);
			error = mpg123_read(mh, (unsigned char *) target, sampleCount
					* sizeof(AUDIO_SAMPLE_TYPE), &done);
			}

		// ignore format change
		if (error == MPG123_NEW_FORMAT)
			error = MPG123_OK;

		}
#endif
	return sampleCount;
	}
예제 #24
0
void AudioStreamerMpg123::seek_impl(ptrdiff_t offset)
{
	mpg123_handle* mh = static_cast<mpg123_handle*>(handle_);
	mpg123_seek(mh, offset, SEEK_SET);
}
예제 #25
0
파일: mpg123.c 프로젝트: jkhart/audite
VALUE rb_mpg123_seek(VALUE self, VALUE offset)
{
  return INT2FIX(mpg123_seek(DATA_PTR(self), FIX2INT(offset), SEEK_SET));
}
예제 #26
0
int mp3decode_reg(struct xlplayer *xlplayer)
    {
    static pthread_once_t once_control = PTHREAD_ONCE_INIT;
    struct mp3decode_vars *self;
    struct chapter *chapter;
    int fd, rv;
    long rate;
    int channels, encoding;
    int src_error;

    pthread_once(&once_control, decoder_library_init);
    if (!decoder_library_ok)
        {
        fprintf(stderr, "mp3decode_reg: decoder library is not ok\n");
        goto rej;
        }


    if (!(self = xlplayer->dec_data = calloc(1, sizeof (struct mp3decode_vars))))
        {
        fprintf(stderr, "mp3decode_reg: malloc failure\n");
        goto rej;
        }


    if (!(self->mh = mpg123_new(NULL, NULL)))
        {
        fprintf(stderr, "mp3decode_reg: handle not okay");
        goto rej_;
        }

#ifdef MPG123_AUTO_RESAMPLE
    if (mpg123_param(self->mh, MPG123_REMOVE_FLAGS, MPG123_AUTO_RESAMPLE, 0.0) != MPG123_OK)
        {
        fprintf(stderr, "mpgdecode_reg: failed to turn off auto resampling\n");
        goto rej_;
        }
#endif

    if (mpg123_param(self->mh, MPG123_ADD_FLAGS, MPG123_FORCE_STEREO, 0.0) != MPG123_OK)
        {
        fprintf(stderr, "mpgdecode_reg: failed to set flags");
        goto rej_;
        }

    if (mpg123_format_none(self->mh) != MPG123_OK)
        {
        fprintf(stderr, "mp3decode_reg: failed to clear output formats");
        goto rej_;
        }

    /* all the permitted mp3 sample rates are enabled
     * forced stereo is in effect so no need to add mono formats
     */
    mpg123_format(self->mh, 48000, MPG123_STEREO, MPG123_ENC_FLOAT_32);
    mpg123_format(self->mh, 44100, MPG123_STEREO, MPG123_ENC_FLOAT_32);
    mpg123_format(self->mh, 32000, MPG123_STEREO, MPG123_ENC_FLOAT_32);
    mpg123_format(self->mh, 24000, MPG123_STEREO, MPG123_ENC_FLOAT_32);
    mpg123_format(self->mh, 22050, MPG123_STEREO, MPG123_ENC_FLOAT_32);
    mpg123_format(self->mh, 16000, MPG123_STEREO, MPG123_ENC_FLOAT_32);
    mpg123_format(self->mh, 12000, MPG123_STEREO, MPG123_ENC_FLOAT_32);
    mpg123_format(self->mh, 11025, MPG123_STEREO, MPG123_ENC_FLOAT_32);
    mpg123_format(self->mh, 8000, MPG123_STEREO, MPG123_ENC_FLOAT_32);

    if (!(self->fp = fopen(xlplayer->pathname, "r")))
        {
        fprintf(stderr, "mp3decode_reg: failed to open %s\n", xlplayer->pathname);
        goto rej_;
        }

    mp3_tag_read(&self->taginfo, self->fp);
    lseek(fd = fileno(self->fp), 0, SEEK_SET);

    if ((rv = mpg123_open_fd(self->mh, fd)) != MPG123_OK)
        {
        fprintf(stderr, "mp3decode_reg: mpg123_open_fd failed with return value %d\n", rv);
        goto rej__;
        }
        
    if (mpg123_getformat(self->mh, &rate, &channels, &encoding) != MPG123_OK || channels != 2)
        {
        fprintf(stderr, "mp3decode_reg: mpg123_getformat returned unexpected value\n");
        goto rej___;
        }
    
    if (rate != xlplayer->samplerate)
        {
        fprintf(stderr, "mp3decode_reg: configuring resampler\n");

        xlplayer->src_state = src_new(xlplayer->rsqual, channels, &src_error);
        if (src_error)
            {
            fprintf(stderr, "mp3decode_reg: src_new reports %s\n", src_strerror(src_error));
            goto rej___;
            }

        xlplayer->src_data.src_ratio = (double)xlplayer->samplerate / (double)rate;
        xlplayer->src_data.end_of_input = 0;
        
        size_t output_frames = (size_t)(xlplayer->src_data.src_ratio * 1.1 * 1152);
        xlplayer->src_data.output_frames = (long)output_frames;
        if (!(xlplayer->src_data.data_out = malloc(output_frames * 2 * sizeof (float))))
            {
            fprintf(stderr, "mp3decode_reg: malloc failure\n");
            goto rej____;
            }

        self->resample = TRUE;
        }

    xlplayer->dec_init = mp3decode_init;
    xlplayer->dec_play = mp3decode_play;
    xlplayer->dec_eject = mp3decode_eject;


    if ((chapter = mp3_tag_chapter_scan(&self->taginfo, xlplayer->play_progress_ms + 70)))
        {
        self->current_chapter = chapter;
        xlplayer_set_dynamic_metadata(xlplayer, dynamic_metadata_form[chapter->title.encoding], chapter->artist.text, chapter->title.text, chapter->album.text, 0);
        }

    if (xlplayer->seek_s)
        if (mpg123_seek(self->mh, (off_t)rate * xlplayer->seek_s, SEEK_SET) < 0)
            {
            fprintf(stderr, "mp3decode_init: seek failed\n");
            mp3decode_eject(xlplayer);
            xlplayer->playmode = PM_STOPPED;
            xlplayer->command = CMD_COMPLETE;
            }

    return ACCEPTED;

    rej____:
    xlplayer->src_state = src_delete(xlplayer->src_state);
    rej___:
    mpg123_delete(self->mh);
    rej__:
    mp3_tag_cleanup(&self->taginfo);
    fclose(self->fp);
    rej_:
    free(self);
    rej:
    return REJECTED;
    }
예제 #27
0
bool Mpg123Decoder::seek(uint64_t pos)
{
    off_t newpos = mpg123_seek(mMpg123, pos, SEEK_SET);
    if(newpos < 0) return false;
    return true;
}
예제 #28
0
int control_generic (mpg123_handle *fr)
{
    struct timeval tv;
    fd_set fds;
    int n;

    /* ThOr */
    char alive = 1;
    char silent = 0;

    /* responses to stderr for frontends needing audio data from stdout */
    if (param.remote_err)
        outstream = stderr;
    else
        outstream = stdout;

#ifndef WIN32
    setlinebuf(outstream);
#else /* perhaps just use setvbuf as it's C89 */
    /*
    fprintf(outstream, "You are on Win32 and want to use the control interface... tough luck: We need a replacement for select on STDIN first.\n");
    return 0;
    setvbuf(outstream, (char*)NULL, _IOLBF, 0);
    */
#endif
    /* the command behaviour is different, so is the ID */
    /* now also with version for command availability */
    fprintf(outstream, "@R MPG123 (ThOr) v8\n");
#ifdef FIFO
    if(param.fifo)
    {
        if(param.fifo[0] == 0)
        {
            error("You wanted an empty FIFO name??");
            return 1;
        }
#ifndef WANT_WIN32_FIFO
        unlink(param.fifo);
        if(mkfifo(param.fifo, 0666) == -1)
        {
            error2("Failed to create FIFO at %s (%s)", param.fifo, strerror(errno));
            return 1;
        }
        debug("going to open named pipe ... blocking until someone gives command");
#endif /* WANT_WIN32_FIFO */
#ifdef WANT_WIN32_FIFO
        control_file = win32_fifo_mkfifo(param.fifo);
#else
        control_file = open(param.fifo,O_RDONLY);
#endif /* WANT_WIN32_FIFO */
        debug("opened");
    }
#endif

    while (alive)
    {
        tv.tv_sec = 0;
        tv.tv_usec = 0;
        FD_ZERO(&fds);
        FD_SET(control_file, &fds);
        /* play frame if no command needs to be processed */
        if (mode == MODE_PLAYING) {
#ifdef WANT_WIN32_FIFO
            n = win32_fifo_read_peek(&tv);
#else
            n = select(32, &fds, NULL, NULL, &tv);
#endif
            if (n == 0) {
                if (!play_frame())
                {
                    /* When the track ended, user may want to keep it open (to seek back),
                       so there is a decision between stopping and pausing at the end. */
                    if(param.keep_open)
                    {
                        mode = MODE_PAUSED;
                        /* Hm, buffer should be stopped already, shouldn't it? */
                        if(param.usebuffer) out123_pause(ao);
                        generic_sendmsg("P 1");
                    }
                    else
                    {
                        mode = MODE_STOPPED;
                        close_track();
                        generic_sendmsg("P 0");
                    }
                    continue;
                }
                if (init) {
                    print_remote_header(fr);
                    init = 0;
                }
                if(silent == 0)
                {
                    generic_sendstat(fr);
                    if(mpg123_meta_check(fr) & MPG123_NEW_ICY)
                    {
                        char *meta;
                        if(mpg123_icy(fr, &meta) == MPG123_OK)
                            generic_sendmsg("I ICY-META: %s", meta != NULL ? meta : "<nil>");
                    }
                }
            }
        }
        else {
            /* wait for command */
            while (1) {
#ifdef WANT_WIN32_FIFO
                n = win32_fifo_read_peek(NULL);
#else
                n = select(32, &fds, NULL, NULL, NULL);
#endif
                if (n > 0)
                    break;
            }
        }

        /*  on error */
        if (n < 0) {
            fprintf(stderr, "Error waiting for command: %s\n", strerror(errno));
            return 1;
        }

        /* read & process commands */
        if (n > 0)
        {
            short int len = 1; /* length of buffer */
            char *cmd, *arg; /* variables for parsing, */
            char *comstr = NULL; /* gcc thinks that this could be used uninitialited... */
            char buf[REMOTE_BUFFER_SIZE];
            short int counter;
            char *next_comstr = buf; /* have it initialized for first command */

            /* read as much as possible, maybe multiple commands */
            /* When there is nothing to read (EOF) or even an error, it is the end */
#ifdef WANT_WIN32_FIFO
            len = win32_fifo_read(buf,REMOTE_BUFFER_SIZE);
#else
            len = read(control_file, buf, REMOTE_BUFFER_SIZE);
#endif
            if(len < 1)
            {
#ifdef FIFO
                if(len == 0 && param.fifo)
                {
                    debug("fifo ended... reopening");
#ifdef WANT_WIN32_FIFO
                    win32_fifo_mkfifo(param.fifo);
#else
                    close(control_file);
                    control_file = open(param.fifo,O_RDONLY|O_NONBLOCK);
#endif
                    if(control_file < 0) {
                        error1("open of fifo failed... %s", strerror(errno));
                        break;
                    }
                    continue;
                }
#endif
                if(len < 0) error1("command read error: %s", strerror(errno));
                break;
            }

            debug1("read %i bytes of commands", len);
            /* one command on a line - separation by \n -> C strings in a row */
            for(counter = 0; counter < len; ++counter)
            {
                /* line end is command end */
                if( (buf[counter] == '\n') || (buf[counter] == '\r') )
                {
                    debug1("line end at counter=%i", counter);
                    buf[counter] = 0; /* now it's a properly ending C string */
                    comstr = next_comstr;

                    /* skip the additional line ender of \r\n or \n\r */
                    if( (counter < (len - 1)) && ((buf[counter+1] == '\n') || (buf[counter+1] == '\r')) ) buf[++counter] = 0;

                    /* next "real" char is first of next command */
                    next_comstr = buf + counter+1;

                    /* directly process the command now */
                    debug1("interpreting command: %s", comstr);
                    if(strlen(comstr) == 0) continue;

                    /* PAUSE */
                    if (!strcasecmp(comstr, "P") || !strcasecmp(comstr, "PAUSE")) {
                        if(mode != MODE_STOPPED)
                        {
                            if (mode == MODE_PLAYING) {
                                mode = MODE_PAUSED;
                                out123_pause(ao);
                                generic_sendmsg("P 1");
                            } else {
                                mode = MODE_PLAYING;
                                out123_continue(ao);
                                generic_sendmsg("P 2");
                            }
                        } else generic_sendmsg("P 0");
                        continue;
                    }

                    /* STOP */
                    if (!strcasecmp(comstr, "S") || !strcasecmp(comstr, "STOP")) {
                        if (mode != MODE_STOPPED) {
                            /* Do we want to drop here? */
                            out123_drop(ao);
                            out123_stop(ao);
                            close_track();
                            mode = MODE_STOPPED;
                            generic_sendmsg("P 0");
                        } else generic_sendmsg("P 0");
                        continue;
                    }

                    /* SILENCE */
                    if(!strcasecmp(comstr, "SILENCE")) {
                        silent = 1;
                        generic_sendmsg("silence");
                        continue;
                    }

                    if(!strcasecmp(comstr, "T") || !strcasecmp(comstr, "TAG")) {
                        generic_sendalltag(fr);
                        continue;
                    }

                    if(!strcasecmp(comstr, "SCAN"))
                    {
                        if(mode != MODE_STOPPED)
                        {
                            if(mpg123_scan(fr) == MPG123_OK)
                                generic_sendmsg("SCAN done");
                            else
                                generic_sendmsg("E %s", mpg123_strerror(fr));
                        }
                        else generic_sendmsg("E No track loaded!");

                        continue;
                    }

                    if(!strcasecmp(comstr, "SAMPLE"))
                    {
                        off_t pos = mpg123_tell(fr);
                        off_t len = mpg123_length(fr);
                        /* I need to have portable printf specifiers that do not truncate the type... more autoconf... */
                        if(len < 0) generic_sendmsg("E %s", mpg123_strerror(fr));
                        else generic_sendmsg("SAMPLE %li %li", (long)pos, (long)len);
                        continue;
                    }

                    if(!strcasecmp(comstr, "FORMAT"))
                    {
                        long rate;
                        int ch;
                        int ret = mpg123_getformat(fr, &rate, &ch, NULL);
                        /* I need to have portable printf specifiers that do not truncate the type... more autoconf... */
                        if(ret < 0) generic_sendmsg("E %s", mpg123_strerror(fr));
                        else generic_sendmsg("FORMAT %li %i", rate, ch);
                        continue;
                    }

                    if(!strcasecmp(comstr, "SHOWEQ"))
                    {
                        int i;
                        generic_sendmsg("SHOWEQ {");
                        for(i=0; i<32; ++i)
                        {
                            generic_sendmsg("SHOWEQ %i : %i : %f", MPG123_LEFT, i, mpg123_geteq(fr, MPG123_LEFT, i));
                            generic_sendmsg("SHOWEQ %i : %i : %f", MPG123_RIGHT, i, mpg123_geteq(fr, MPG123_RIGHT, i));
                        }
                        generic_sendmsg("SHOWEQ }");
                        continue;
                    }

                    if(!strcasecmp(comstr, "STATE"))
                    {
                        long val;
                        generic_sendmsg("STATE {");
                        /* Get some state information bits and display them. */
                        if(mpg123_getstate(fr, MPG123_ACCURATE, &val, NULL) == MPG123_OK)
                            generic_sendmsg("STATE accurate %li", val);

                        generic_sendmsg("STATE }");
                        continue;
                    }

                    /* QUIT */
                    if (!strcasecmp(comstr, "Q") || !strcasecmp(comstr, "QUIT")) {
                        alive = FALSE;
                        continue;
                    }

                    /* some HELP */
                    if (!strcasecmp(comstr, "H") || !strcasecmp(comstr, "HELP")) {
                        generic_sendmsg("H {");
                        generic_sendmsg("H HELP/H: command listing (LONG/SHORT forms), command case insensitve");
                        generic_sendmsg("H LOAD/L <trackname>: load and start playing resource <trackname>");
                        generic_sendmsg("H LOADPAUSED/LP <trackname>: load but do not start playing resource <trackname>");
                        generic_sendmsg("H LOADLIST/LL <entry> <url>: load a playlist from given <url>, and display its entries, optionally load and play one of these specificed by the integer <entry> (<0: just list, 0: play last track, >0:play track with that position in list)");
                        generic_sendmsg("H PAUSE/P: pause playback");
                        generic_sendmsg("H STOP/S: stop playback (closes file)");
                        generic_sendmsg("H JUMP/J <frame>|<+offset>|<-offset>|<[+|-]seconds>s: jump to mpeg frame <frame> or change position by offset, same in seconds if number followed by \"s\"");
                        generic_sendmsg("H VOLUME/V <percent>: set volume in % (0..100...); float value");
                        generic_sendmsg("H RVA off|(mix|radio)|(album|audiophile): set rva mode");
                        generic_sendmsg("H EQ/E <channel> <band> <value>: set equalizer value for frequency band 0 to 31 on channel %i (left) or %i (right) or %i (both)", MPG123_LEFT, MPG123_RIGHT, MPG123_LR);
                        generic_sendmsg("H EQFILE <filename>: load EQ settings from a file");
                        generic_sendmsg("H SHOWEQ: show all equalizer settings (as <channel> <band> <value> lines in a SHOWEQ block (like TAG))");
                        generic_sendmsg("H SEEK/K <sample>|<+offset>|<-offset>: jump to output sample position <samples> or change position by offset");
                        generic_sendmsg("H SCAN: scan through the file, building seek index");
                        generic_sendmsg("H SAMPLE: print out the sample position and total number of samples");
                        generic_sendmsg("H FORMAT: print out sampling rate in Hz and channel count");
                        generic_sendmsg("H SEQ <bass> <mid> <treble>: simple eq setting...");
                        generic_sendmsg("H PITCH <[+|-]value>: adjust playback speed (+0.01 is 1 %% faster)");
                        generic_sendmsg("H SILENCE: be silent during playback (meaning silence in text form)");
                        generic_sendmsg("H STATE: Print auxiliary state info in several lines (just try it to see what info is there).");
                        generic_sendmsg("H TAG/T: Print all available (ID3) tag info, for ID3v2 that gives output of all collected text fields, using the ID3v2.3/4 4-character names. NOTE: ID3v2 data will be deleted on non-forward seeks.");
                        generic_sendmsg("H    The output is multiple lines, begin marked by \"@T {\", end by \"@T }\".");
                        generic_sendmsg("H    ID3v1 data is like in the @I info lines (see below), just with \"@T\" in front.");
                        generic_sendmsg("H    An ID3v2 data field is introduced via ([ ... ] means optional):");
                        generic_sendmsg("H     @T ID3v2.<NAME>[ [lang(<LANG>)] desc(<description>)]:");
                        generic_sendmsg("H    The lines of data follow with \"=\" prefixed:");
                        generic_sendmsg("H     @T =<one line of content in UTF-8 encoding>");
                        generic_sendmsg("H meaning of the @S stream info:");
                        generic_sendmsg("H %s", remote_header_help);
                        generic_sendmsg("H The @I lines after loading a track give some ID3 info, the format:");
                        generic_sendmsg("H      @I ID3:artist  album  year  comment genretext");
                        generic_sendmsg("H     where artist,album and comment are exactly 30 characters each, year is 4 characters, genre text unspecified.");
                        generic_sendmsg("H     You will encounter \"@I ID3.genre:<number>\" and \"@I ID3.track:<number>\".");
                        generic_sendmsg("H     Then, there is an excerpt of ID3v2 info in the structure");
                        generic_sendmsg("H      @I ID3v2.title:Blabla bla Bla");
                        generic_sendmsg("H     for every line of the \"title\" data field. Likewise for other fields (author, album, etc).");
                        generic_sendmsg("H }");
                        continue;
                    }

                    /* commands with arguments */
                    cmd = NULL;
                    arg = NULL;
                    cmd = strtok(comstr," \t"); /* get the main command */
                    arg = strtok(NULL,""); /* get the args */

                    if (cmd && strlen(cmd) && arg && strlen(arg))
                    {
#ifndef NO_EQUALIZER
                        /* Simple EQ: SEQ <BASS> <MID> <TREBLE>  */
                        if (!strcasecmp(cmd, "SEQ")) {
                            double b,m,t;
                            int cn;
                            if(sscanf(arg, "%lf %lf %lf", &b, &m, &t) == 3)
                            {
                                /* Consider adding mpg123_seq()... but also, on could define a nicer courve for that. */
                                if ((t >= 0) && (t <= 3))
                                    for(cn=0; cn < 1; ++cn)	mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, b);

                                if ((m >= 0) && (m <= 3))
                                    for(cn=1; cn < 2; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, m);

                                if ((b >= 0) && (b <= 3))
                                    for(cn=2; cn < 32; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, t);

                                generic_sendmsg("bass: %f mid: %f treble: %f", b, m, t);
                            }
                            else generic_sendmsg("E invalid arguments for SEQ: %s", arg);
                            continue;
                        }

                        /* Equalizer control :) (JMG) */
                        if (!strcasecmp(cmd, "E") || !strcasecmp(cmd, "EQ")) {
                            double e; /* ThOr: equalizer is of type real... whatever that is */
                            int c, v;
                            /*generic_sendmsg("%s",updown);*/
                            if(sscanf(arg, "%i %i %lf", &c, &v, &e) == 3)
                            {
                                if(mpg123_eq(fr, c, v, e) == MPG123_OK)
                                    generic_sendmsg("%i : %i : %f", c, v, e);
                                else
                                    generic_sendmsg("E failed to set eq: %s", mpg123_strerror(fr));
                            }
                            else generic_sendmsg("E invalid arguments for EQ: %s", arg);
                            continue;
                        }

                        if(!strcasecmp(cmd, "EQFILE"))
                        {
                            equalfile = arg;
                            if(load_equalizer(fr) == 0)
                                generic_sendmsg("EQFILE done");
                            else
                                generic_sendmsg("E failed to parse given eq file");

                            continue;
                        }
#endif
                        /* SEEK to a sample offset */
                        if(!strcasecmp(cmd, "K") || !strcasecmp(cmd, "SEEK"))
                        {
                            off_t soff;
                            off_t oldpos;
                            off_t newpos;
                            char *spos = arg;
                            int whence = SEEK_SET;
                            if(mode == MODE_STOPPED)
                            {
                                generic_sendmsg("E No track loaded!");
                                continue;
                            }
                            oldpos = mpg123_tell(fr);

                            soff = (off_t) atobigint(spos);
                            if(spos[0] == '-' || spos[0] == '+') whence = SEEK_CUR;
                            if(0 > (soff = mpg123_seek(fr, soff, whence)))
                            {
                                generic_sendmsg("E Error while seeking: %s", mpg123_strerror(fr));
                                mpg123_seek(fr, 0, SEEK_SET);
                            }
                            out123_drop(ao);

                            newpos = mpg123_tell(fr);
                            if(newpos <= oldpos) mpg123_meta_free(fr);

                            generic_sendmsg("K %"OFF_P, (off_p)newpos);
                            continue;
                        }
                        /* JUMP */
                        if (!strcasecmp(cmd, "J") || !strcasecmp(cmd, "JUMP")) {
                            char *spos;
                            off_t offset;
                            off_t oldpos;
                            double secs;

                            spos = arg;
                            if(mode == MODE_STOPPED)
                            {
                                generic_sendmsg("E No track loaded!");
                                continue;
                            }
                            oldpos = framenum;

                            if(spos[strlen(spos)-1] == 's' && sscanf(arg, "%lf", &secs) == 1) offset = mpg123_timeframe(fr, secs);
                            else offset = atol(spos);
                            /* totally replaced that stuff - it never fully worked
                               a bit usure about why +pos -> spos+1 earlier... */
                            if (spos[0] == '-' || spos[0] == '+') offset += framenum;

                            if(0 > (framenum = mpg123_seek_frame(fr, offset, SEEK_SET)))
                            {
                                generic_sendmsg("E Error while seeking");
                                mpg123_seek_frame(fr, 0, SEEK_SET);
                            }
                            out123_drop(ao);

                            if(framenum <= oldpos) mpg123_meta_free(fr);
                            generic_sendmsg("J %d", framenum);
                            continue;
                        }

                        /* VOLUME in percent */
                        if(!strcasecmp(cmd, "V") || !strcasecmp(cmd, "VOLUME"))
                        {
                            double v;
                            mpg123_volume(fr, atof(arg)/100);
                            mpg123_getvolume(fr, &v, NULL, NULL); /* Necessary? */
                            generic_sendmsg("V %f%%", v * 100);
                            continue;
                        }

                        /* PITCH (playback speed) in percent */
                        if(!strcasecmp(cmd, "PITCH"))
                        {
                            double p;
                            if(sscanf(arg, "%lf", &p) == 1)
                            {
                                set_pitch(fr, ao, p);
                                generic_sendmsg("PITCH %f", param.pitch);
                            }
                            else generic_sendmsg("E invalid arguments for PITCH: %s", arg);
                            continue;
                        }

                        /* RVA mode */
                        if(!strcasecmp(cmd, "RVA"))
                        {
                            if(!strcasecmp(arg, "off")) param.rva = MPG123_RVA_OFF;
                            else if(!strcasecmp(arg, "mix") || !strcasecmp(arg, "radio")) param.rva = MPG123_RVA_MIX;
                            else if(!strcasecmp(arg, "album") || !strcasecmp(arg, "audiophile")) param.rva = MPG123_RVA_ALBUM;
                            mpg123_volume_change(fr, 0.);
                            generic_sendmsg("RVA %s", rva_name[param.rva]);
                            continue;
                        }

                        /* LOAD - actually play */
                        if (!strcasecmp(cmd, "L") || !strcasecmp(cmd, "LOAD")) {
                            generic_load(fr, arg, MODE_PLAYING);
                            continue;
                        }

                        if (!strcasecmp(cmd, "LL") || !strcasecmp(cmd, "LOADLIST")) {
                            generic_loadlist(fr, arg);
                            continue;
                        }

                        /* LOADPAUSED */
                        if (!strcasecmp(cmd, "LP") || !strcasecmp(cmd, "LOADPAUSED")) {
                            generic_load(fr, arg, MODE_PAUSED);
                            continue;
                        }

                        /* no command matched */
                        generic_sendmsg("E Unknown command: %s", cmd); /* unknown command */
                    } /* end commands with arguments */
                    else generic_sendmsg("E Unknown command or no arguments: %s", comstr); /* unknown command */

                } /* end of single command processing */
            } /* end of scanning the command buffer */

            /*
               when last command had no \n... should I discard it?
               Ideally, I should remember the part and wait for next
            	 read() to get the rest up to a \n. But that can go
            	 to infinity. Too long commands too quickly are just
            	 bad. Cannot/Won't change that. So, discard the unfinished
            	 command and have fingers crossed that the rest of this
            	 unfinished one qualifies as "unknown".
            */
            if(buf[len-1] != 0)
            {
                char lasti = buf[len-1];
                buf[len-1] = 0;
                generic_sendmsg("E Unfinished command: %s%c", comstr, lasti);
            }
        } /* end command reading & processing */
    } /* end main (alive) loop */
    debug("going to end");
    /* quit gracefully */
    debug("closing control");
#ifdef FIFO
#if WANT_WIN32_FIFO
    win32_fifo_close();
#else
    close(control_file); /* be it FIFO or STDIN */
    if(param.fifo) unlink(param.fifo);
#endif /* WANT_WIN32_FIFO */
#endif
    debug("control_generic returning");
    return 0;
}
예제 #29
0
int gaudio_Mp3Seek(g_id gid, long int offset, int whence)
{
    GGMp3Handle *handle = (GGMp3Handle*)gid;

    return mpg123_seek(handle->mh, offset, whence);
}
예제 #30
0
static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
{
	debug1("term_handle_key: %c", val);
	switch(tolower(val))
	{
	case MPG123_BACK_KEY:
		out123_pause(ao);
		out123_drop(ao);
		if(paused) pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr));

		if(mpg123_seek_frame(fr, 0, SEEK_SET) < 0)
		error1("Seek to begin failed: %s", mpg123_strerror(fr));

		framenum=0;
	break;
	case MPG123_NEXT_KEY:
		out123_pause(ao);
		out123_drop(ao);
		next_track();
	break;
	case MPG123_NEXT_DIR_KEY:
		out123_pause(ao);
		out123_drop(ao);
		next_dir();
	break;
	case MPG123_QUIT_KEY:
		debug("QUIT");
		if(stopped)
		{
			stopped = 0;
			out123_pause(ao); /* no chance for annoying underrun warnings */
			out123_drop(ao);
		}
		set_intflag();
		offset = 0;
	break;
	case MPG123_PAUSE_KEY:
		paused=1-paused;
		out123_pause(ao); /* underrun awareness */
		out123_drop(ao);
		if(paused)
		{
			/* Not really sure if that is what is wanted
				 This jumps in audio output, but has direct reaction to pausing loop. */
			out123_param_float(ao, OUT123_PRELOAD, 0.);
			pause_recycle(fr);
		}
		else
			out123_param_float(ao, OUT123_PRELOAD, param.preload);
		if(stopped)
			stopped=0;
		if(param.verbose)
			print_stat(fr, 0, ao);
		else
			fprintf(stderr, "%s", (paused) ? MPG123_PAUSED_STRING : MPG123_EMPTY_STRING);
	break;
	case MPG123_STOP_KEY:
	case ' ':
		/* TODO: Verify/ensure that there is no "chirp from the past" when
		   seeking while stopped. */
		stopped=1-stopped;
		if(paused) {
			paused=0;
			offset -= pause_cycle;
		}
		if(stopped)
			out123_pause(ao);
		else
		{
			if(offset) /* If position changed, old is outdated. */
				out123_drop(ao);
			/* No out123_continue(), that's triggered by out123_play(). */
		}
		if(param.verbose)
			print_stat(fr, 0, ao);
		else
			fprintf(stderr, "%s", (stopped) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING);
	break;
	case MPG123_FINE_REWIND_KEY:
		seekmode(fr, ao);
		offset--;
	break;
	case MPG123_FINE_FORWARD_KEY:
		seekmode(fr, ao);
		offset++;
	break;
	case MPG123_REWIND_KEY:
		seekmode(fr, ao);
		  offset-=10;
	break;
	case MPG123_FORWARD_KEY:
		seekmode(fr, ao);
		offset+=10;
	break;
	case MPG123_FAST_REWIND_KEY:
		seekmode(fr, ao);
		offset-=50;
	break;
	case MPG123_FAST_FORWARD_KEY:
		seekmode(fr, ao);
		offset+=50;
	break;
	case MPG123_VOL_UP_KEY:
		mpg123_volume_change(fr, 0.02);
	break;
	case MPG123_VOL_DOWN_KEY:
		mpg123_volume_change(fr, -0.02);
	break;
	case MPG123_PITCH_UP_KEY:
	case MPG123_PITCH_BUP_KEY:
	case MPG123_PITCH_DOWN_KEY:
	case MPG123_PITCH_BDOWN_KEY:
	case MPG123_PITCH_ZERO_KEY:
	{
		double new_pitch = param.pitch;
		switch(val) /* Not tolower here! */
		{
			case MPG123_PITCH_UP_KEY:    new_pitch += MPG123_PITCH_VAL;  break;
			case MPG123_PITCH_BUP_KEY:   new_pitch += MPG123_PITCH_BVAL; break;
			case MPG123_PITCH_DOWN_KEY:  new_pitch -= MPG123_PITCH_VAL;  break;
			case MPG123_PITCH_BDOWN_KEY: new_pitch -= MPG123_PITCH_BVAL; break;
			case MPG123_PITCH_ZERO_KEY:  new_pitch = 0.0; break;
		}
		set_pitch(fr, ao, new_pitch);
		fprintf(stderr, "New pitch: %f\n", param.pitch);
	}
	break;
	case MPG123_VERBOSE_KEY:
		param.verbose++;
		if(param.verbose > VERBOSE_MAX)
		{
			param.verbose = 0;
			clear_stat();
		}
		mpg123_param(fr, MPG123_VERBOSE, param.verbose, 0);
	break;
	case MPG123_RVA_KEY:
		if(++param.rva > MPG123_RVA_MAX) param.rva = 0;
		if(param.verbose)
			fprintf(stderr, "\n");
		mpg123_param(fr, MPG123_RVA, param.rva, 0);
		mpg123_volume_change(fr, 0.);
	break;
	case MPG123_PREV_KEY:
		out123_pause(ao);
		out123_drop(ao);

		prev_track();
	break;
	case MPG123_PREV_DIR_KEY:
		out123_pause(ao);
		out123_drop(ao);
		prev_dir();
	break;
	case MPG123_PLAYLIST_KEY:
		fprintf(stderr, "%s\nPlaylist (\">\" indicates current track):\n", param.verbose ? "\n" : "");
		print_playlist(stderr, 1);
		fprintf(stderr, "\n");
	break;
	case MPG123_TAG_KEY:
		fprintf(stderr, "%s\n", param.verbose ? "\n" : "");
		print_id3_tag(fr, param.long_id3, stderr);
		fprintf(stderr, "\n");
	break;
	case MPG123_MPEG_KEY:
		if(param.verbose) print_stat(fr,0,ao); /* Make sure that we are talking about the correct frame. */
		fprintf(stderr, "\n");
		if(param.verbose > 1)
			print_header(fr);
		else
			print_header_compact(fr);
		fprintf(stderr, "\n");
	break;
	case MPG123_HELP_KEY:
	{ /* This is more than the one-liner before, but it's less spaghetti. */
		int i;
		fprintf(stderr,"\n\n -= terminal control keys =-\n");
		for(i=0; i<(sizeof(term_help)/sizeof(struct keydef)); ++i)
		{
			if(term_help[i].key2) fprintf(stderr, "[%c] or [%c]", term_help[i].key, term_help[i].key2);
			else fprintf(stderr, "[%c]", term_help[i].key);

			fprintf(stderr, "\t%s\n", term_help[i].desc);
		}
		fprintf(stderr, "\nAlso, the number row (starting at 1, ending at 0) gives you jump points into the current track at 10%% intervals.\n");
		fprintf(stderr, "\n");
	}
	break;
	case MPG123_FRAME_INDEX_KEY:
	case MPG123_VARIOUS_INFO_KEY:
		if(param.verbose) fprintf(stderr, "\n");
		switch(val) /* because of tolower() ... */
		{
			case MPG123_FRAME_INDEX_KEY:
			print_index(fr);
			{
				long accurate;
				if(mpg123_getstate(fr, MPG123_ACCURATE, &accurate, NULL) == MPG123_OK)
				fprintf(stderr, "Accurate position: %s\n", (accurate == 0 ? "no" : "yes"));
				else
				error1("Unable to get state: %s", mpg123_strerror(fr));
			}
			break;
			case MPG123_VARIOUS_INFO_KEY:
			{
				const char* curdec = mpg123_current_decoder(fr);
				if(curdec == NULL) fprintf(stderr, "Cannot get decoder info!\n");
				else fprintf(stderr, "Active decoder: %s\n", curdec);
			}
		}
	break;
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	{
		off_t len;
		int num;
		num = val == '0' ? 10 : val - '0';
		--num; /* from 0 to 9 */

		/* Do not swith to seekmode() here, as we are jumping once to a
		   specific position. Dropping buffer contents is enough and there
		   is no race filling the buffer or waiting for more incremental
		   seek orders. */
		len = mpg123_length(fr);
		out123_pause(ao);
		out123_drop(ao);
		if(len > 0)
			mpg123_seek(fr, (off_t)( (num/10.)*len ), SEEK_SET);
	}
	break;
	case MPG123_BOOKMARK_KEY:
		continue_msg("BOOKMARK");
	break;
	default:
		;
	}
}