Example #1
0
void PlayerWidget::mouseReleaseEvent(QMouseEvent *e) {
	if (_down == OverVolume) {
		mouseMoveEvent(e);
		Local::writeUserSettings();
	} else if (_down == OverPlayback) {
		mouseMoveEvent(e);
		SongMsgId playing;
		AudioPlayerState playingState = AudioPlayerStopped;
		int64 playingPosition = 0, playingDuration = 0;
		int32 playingFrequency = 0;
		audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
		if (playing == _song && playingDuration) {
			_downDuration = playingDuration;
			audioPlayer()->seek(qRound(_downProgress * _downDuration));

			_showPause = true;

			a_progress = anim::fvalue(_downProgress, _downProgress);
			_a_progress.stop();
		}
		update();
	} else if (_down == OverClose && _over == OverClose) {
		stopPressed();
	}
	_down = OverNone;
}
Example #2
0
void PlayerWidget::pausePressed() {
	if (!_song || isHidden()) return;

	SongMsgId playing;
	AudioPlayerState playingState = AudioPlayerStopped;
	audioPlayer()->currentState(&playing, &playingState);
	if (playing == _song && !(playingState & AudioPlayerStoppedMask)) {
		if (playingState == AudioPlayerStarting || playingState == AudioPlayerResuming || playingState == AudioPlayerPlaying || playingState == AudioPlayerFinishing) {
			audioPlayer()->pauseresume(OverviewDocuments);
		}
	}
}
Example #3
0
void PlayerWidget::playPausePressed() {
	if (!_song || isHidden()) return;

	SongMsgId playing;
	AudioPlayerState playingState = AudioPlayerStopped;
	audioPlayer()->currentState(&playing, &playingState);
	if (playing == _song && !(playingState & AudioPlayerStoppedMask)) {
		audioPlayer()->pauseresume(OverviewDocuments);
	} else {
		audioPlayer()->play(_song);
		if (App::main()) App::main()->documentPlayProgress(_song);
	}
}
Example #4
0
void PlayerWidget::mousePressEvent(QMouseEvent *e) {
	QPoint pos(myrtlpoint(e->pos()));

	if (e->button() == Qt::LeftButton) {
		_down = OverNone;
		if (_song && _over == OverPlay) {
			playPausePressed();
			return;
		} else if (_over == OverPrev) {
			prevPressed();
		} else if (_over == OverNext) {
			nextPressed();
		} else if (_over == OverClose) {
			_down = OverClose;
		} else if (_over == OverVolume) {
			_down = OverVolume;
			_downCoord = pos.x() - _volumeRect.x();
			cSetSongVolume(snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.));
			emit audioPlayer()->songVolumeChanged();
			rtlupdate(_volumeRect);
		} else if (_over == OverPlayback) {
			SongMsgId playing;
			AudioPlayerState playingState = AudioPlayerStopped;
			int64 playingPosition = 0, playingDuration = 0;
			int32 playingFrequency = 0;
			audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
			if (playing == _song && playingDuration) {
				if (playingState == AudioPlayerPlaying || playingState == AudioPlayerStarting || playingState == AudioPlayerResuming) {
					audioPlayer()->pauseresume(OverviewDocuments);
				}
				_down = OverPlayback;
				_downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.);
				_downDuration = playingDuration;
				_downFrequency = (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency);

				rtlupdate(_playbackRect);
				updateDownTime();
			}
		} else if (_over == OverFull && _song) {
			if (HistoryItem *item = App::histItemById(_song.msgId)) {
				App::main()->showMediaOverview(item->history()->peer, OverviewAudioDocuments);
			}
		} else if (_over == OverRepeat) {
			_repeat = !_repeat;
			updateOverRect(OverRepeat);
		}
	}
}
Example #5
0
void PlayerWidget::nextPressed() {
	if (isHidden()) return;

	const History::MediaOverview *o = _history ? &_history->overview[OverviewAudioDocuments] : 0;
	if (audioPlayer() && o && _index >= 0 && _index < o->size() - 1) {
		startPlay(FullMsgId(_history->channelId(), o->at(_index + 1)));
	}
}
Example #6
0
void PlayerWidget::startPlay(const FullMsgId &msgId) {
	if (HistoryItem *item = App::histItemById(msgId)) {
		if (HistoryDocument *doc = static_cast<HistoryDocument*>(item->getMedia())) {
			audioPlayer()->play(SongMsgId(doc->getDocument(), item->fullId()));
			updateState();
		}
	}
}
Example #7
0
void PlayerWidget::nextPressed() {
	if (isHidden()) return;

	History *history = _msgmigrated ? _migrated : _history;
	const History::MediaOverview *o = history ? &history->overview[OverviewAudioDocuments] : 0;
	if (audioPlayer() && o && _index >= 0 && _index < o->size() - 1) {
		startPlay(FullMsgId(history->channelId(), o->at(_index + 1)));
	} else if (o && (_index == o->size() - 1) && _msgmigrated && _history->overviewLoaded(OverviewAudioDocuments)) {
		o = &_history->overview[OverviewAudioDocuments];
		if (!o->isEmpty()) {
			startPlay(FullMsgId(_history->channelId(), o->at(0)));
		}
	}
}
Example #8
0
void PlayerWidget::prevPressed() {
	if (isHidden()) return;

	History *history = _msgmigrated ? _migrated : _history;
	const History::MediaOverview *o = history ? &history->overview[OverviewAudioDocuments] : 0;
	if (audioPlayer() && o && _index > 0 && _index <= o->size() && !o->isEmpty()) {
		startPlay(FullMsgId(history->channelId(), o->at(_index - 1)));
	} else if (!_index && _history && _migrated && !_msgmigrated) {
		o = &_migrated->overview[OverviewAudioDocuments];
		if (!o->isEmpty()) {
			startPlay(FullMsgId(_migrated->channelId(), o->at(o->size() - 1)));
		}
	}
}
void AudioPlayerLoaders::onStart(const AudioMsgId &audio, qint64 position) {
	auto type = audio.type();
	clear(type);
	{
		QMutexLocker lock(internal::audioPlayerMutex());
		AudioPlayer *voice = audioPlayer();
		if (!voice) return;

		auto data = voice->dataForType(type);
		if (!data) return;

		data->loading = true;
	}

	loadData(audio, position);
}
void TitleButton::updatePauseState() {
    AudioMsgId playing;
    auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
    auto stopped = ((playbackState.state & AudioPlayerStoppedMask) || playbackState.state == AudioPlayerFinishing);
    auto showPause = !stopped && (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting);
    if (exists() && instance()->isSeeking()) {
        showPause = true;
    }
    auto state = [audio = playing.audio(), showPause] {
        if (audio && audio->loading()) {
            return State::Cancel;
        } else if (showPause) {
            return State::Pause;
        }
        return State::Play;
    };
    _layout->setState(state());
}
void AudioPlayerLoaders::onCancel(const AudioMsgId &audio) {
	switch (audio.type()) {
	case AudioMsgId::Type::Voice: if (_audio == audio) clear(audio.type()); break;
	case AudioMsgId::Type::Song: if (_song == audio) clear(audio.type()); break;
	case AudioMsgId::Type::Video: if (_video == audio) clear(audio.type()); break;
	}

	QMutexLocker lock(internal::audioPlayerMutex());
	AudioPlayer *voice = audioPlayer();
	if (!voice) return;

	for (int i = 0; i < AudioSimultaneousLimit; ++i) {
		auto data = voice->dataForType(audio.type(), i);
		if (data->audio == audio) {
			data->loading = false;
		}
	}
}
AudioPlayer::AudioMsg *AudioPlayerLoaders::checkLoader(AudioMsgId::Type type) {
	AudioPlayer *voice = audioPlayer();
	if (!voice) return 0;

	auto data = voice->dataForType(type);
	bool isGoodId = false;
	AudioPlayerLoader *l = nullptr;
	switch (type) {
	case AudioMsgId::Type::Voice: l = _audioLoader.get(); isGoodId = (data->audio == _audio); break;
	case AudioMsgId::Type::Song: l = _songLoader.get(); isGoodId = (data->audio == _song); break;
	case AudioMsgId::Type::Video: l = _videoLoader.get(); isGoodId = (data->audio == _video); break;
	}
	if (!l || !data) return nullptr;

	if (!isGoodId || !data->loading || !l->check(data->file, data->data)) {
		LOG(("Audio Error: playing changed while loading"));
		return nullptr;
	}

	return data;
}
Example #13
0
void PlayerWidget::updateSelected() {
	QPoint pos(myrtlpoint(mapFromGlobal(_lastMousePos)));

	if (_down == OverVolume) {
		int32 delta = (pos.x() - _volumeRect.x()) - _downCoord;
		float64 startFrom = snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.);
		float64 add = delta / float64(4 * st::playerVolume.pxWidth()), result = snap(startFrom + add, 0., 1.);
		if (result != cSongVolume()) {
			cSetSongVolume(result);
			emit audioPlayer()->songVolumeChanged();
			rtlupdate(_volumeRect);
		}
	} else if (_down == OverPlayback) {
		_downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.);
		rtlupdate(_playbackRect);
		updateDownTime();
	} else if (_down == OverNone) {
		bool inInfo = ((pos.x() >= _infoRect.x()) && (pos.x() < _fullRect.x() + _fullRect.width()) && (pos.y() >= _playRect.y()) && (pos.y() <= _playRect.y() + _playRect.height()));
		if (_prevAvailable && _prevRect.contains(pos)) {
			updateOverState(OverPrev);
		} else if (_nextAvailable && _nextRect.contains(pos)) {
			updateOverState(OverNext);
		} else if (_playRect.contains(pos)) {
			updateOverState(OverPlay);
		} else if (_closeRect.contains(pos)) {
			updateOverState(OverClose);
		} else if (_volumeRect.contains(pos)) {
			updateOverState(OverVolume);
		} else if (_repeatRect.contains(pos)) {
			updateOverState(OverRepeat);
		} else if (_duration && _playbackRect.contains(pos)) {
			updateOverState(OverPlayback);
		} else if (_fullAvailable && inInfo) {
			updateOverState(OverFull);
		} else if (_over != OverNone) {
			updateOverState(OverNone);
		}
	}
}
AudioPlayerLoader *AudioPlayerLoaders::setupLoader(const AudioMsgId &audio, SetupError &err, qint64 &position) {
	err = SetupErrorAtStart;
	QMutexLocker lock(internal::audioPlayerMutex());
	AudioPlayer *voice = audioPlayer();
	if (!voice) return nullptr;

	auto data = voice->dataForType(audio.type());
	if (!data || data->audio != audio || !data->loading) {
		emit error(audio);
		LOG(("Audio Error: trying to load part of audio, that is not current at the moment"));
		err = SetupErrorNotPlaying;
		return nullptr;
	}

	bool isGoodId = false;
	AudioPlayerLoader *l = nullptr;
	switch (audio.type()) {
	case AudioMsgId::Type::Voice: l = _audioLoader.get(); isGoodId = (_audio == audio); break;
	case AudioMsgId::Type::Song: l = _songLoader.get(); isGoodId = (_song == audio); break;
	case AudioMsgId::Type::Video: l = _videoLoader.get(); isGoodId = (_video == audio); break;
	}

	if (l && (!isGoodId || !l->check(data->file, data->data))) {
		clear(audio.type());
		l = nullptr;
	}

	if (!l) {
		std_::unique_ptr<AudioPlayerLoader> *loader = nullptr;
		switch (audio.type()) {
		case AudioMsgId::Type::Voice: _audio = audio; loader = &_audioLoader; break;
		case AudioMsgId::Type::Song: _song = audio; loader = &_songLoader; break;
		case AudioMsgId::Type::Video: _video = audio; break;
		}

		if (audio.type() == AudioMsgId::Type::Video) {
			if (!data->videoData) {
				data->playbackState.state = AudioPlayerStoppedAtError;
				emit error(audio);
				LOG(("Audio Error: video sound data not ready"));
				return nullptr;
			}
			_videoLoader = std_::make_unique<ChildFFMpegLoader>(data->videoPlayId, std_::move(data->videoData));
			l = _videoLoader.get();
		} else {
			*loader = std_::make_unique<FFMpegLoader>(data->file, data->data);
			l = loader->get();
		}

		if (!l->open(position)) {
			data->playbackState.state = AudioPlayerStoppedAtStart;
			return nullptr;
		}
		int64 duration = l->duration();
		if (duration <= 0) {
			data->playbackState.state = AudioPlayerStoppedAtStart;
			return nullptr;
		}
		data->playbackState.duration = duration;
		data->playbackState.frequency = l->frequency();
		if (!data->playbackState.frequency) data->playbackState.frequency = AudioVoiceMsgFrequency;
		err = SetupNoErrorStarted;
	} else {
		if (!data->skipEnd) {
			err = SetupErrorLoadedFull;
			LOG(("Audio Error: trying to load part of audio, that is already loaded to the end"));
			return nullptr;
		}
	}
	return l;
}
Example #15
0
void PlayerWidget::stopPressed() {
	if (!_song || isHidden()) return;

	audioPlayer()->stop(OverviewFiles);
}
Example #16
0
void PlayerWidget::stopPressed() {
	if (!_song || isHidden()) return;

	audioPlayer()->stop(OverviewDocuments);
	if (App::main()) App::main()->hidePlayer();
}
Example #17
0
void PlayerWidget::updateState(SongMsgId playing, AudioPlayerState playingState, int64 playingPosition, int64 playingDuration, int32 playingFrequency) {
	if (!playing) {
		audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
	}

	bool songChanged = false;
	if (playing && _song != playing) {
		songChanged = true;
		_song = playing;
		if (HistoryItem *item = App::histItemById(_song.msgId)) {
			_history = item->history();
			if (_history->peer->migrateFrom()) {
				_migrated = App::history(_history->peer->migrateFrom()->id);
				_msgmigrated = false;
			} else if (_history->peer->migrateTo()) {
				_migrated = _history;
				_history = App::history(_migrated->peer->migrateTo()->id);
				_msgmigrated = true;
			}
			findCurrent();
		} else {
			_history = 0;
			_msgmigrated = false;
			_index = -1;
		}
		SongData *song = _song.song->song();
		if (song->performer.isEmpty()) {
			_name.setText(st::linkFont, song->title.isEmpty() ? (_song.song->name.isEmpty() ? qsl("Unknown Track") : _song.song->name) : song->title, _textNameOptions);
		} else {
			TextCustomTagsMap custom;
			custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink()));
			_name.setRichText(st::linkFont, QString::fromUtf8("[c]%1[/c] \xe2\x80\x93 %2").arg(textRichPrepare(song->performer)).arg(song->title.isEmpty() ? qsl("Unknown Track") : textRichPrepare(song->title)), _textNameOptions, custom);
		}
		updateControls();
	}

	qint64 position = 0, duration = 0, display = 0;
	if (playing == _song) {
		if (!(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
			display = position = playingPosition;
			duration = playingDuration;
		} else {
			display = playingDuration;
		}
		display = display / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency);
	} else if (_song) {
		display = _song.song->song()->duration;
	}
	bool showPause = false, stopped = ((playingState & AudioPlayerStoppedMask) || playingState == AudioPlayerFinishing);
	bool wasPlaying = !!_duration;
	if (!stopped) {
		showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
	}
	QString time;
	float64 progress = 0.;
	int32 loaded;
	float64 loadProgress = 1.;
	if (duration || !_song || !_song.song || !_song.song->loading()) {
		time = (_down == OverPlayback) ? _time : formatDurationText(display);
		progress = duration ? snap(float64(position) / duration, 0., 1.) : 0.;
		loaded = duration ? _song.song->size : 0;
	} else {
		loaded = _song.song->loading() ? _song.song->loadOffset() : 0;
		time = formatDownloadText(loaded, _song.song->size);
		loadProgress = snap(float64(loaded) / qMax(_song.song->size, 1), 0., 1.);
	}
	if (time != _time || showPause != _showPause) {
		if (_time != time) {
			_time = time;
			_timeWidth = st::linkFont->width(_time);
		}
		_showPause = showPause;
		if (duration != _duration || position != _position || loaded != _loaded) {
			if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) {
				a_progress.start(progress);
				a_loadProgress.start(loadProgress);
				_a_progress.start();
			} else {
				a_progress = anim::fvalue(progress, progress);
				a_loadProgress = anim::fvalue(loadProgress, loadProgress);
				_a_progress.stop();
			}
			_position = position;
			_duration = duration;
			_loaded = loaded;
		}
		update();
	} else if (duration != _duration || position != _position || loaded != _loaded) {
		if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) {
			a_progress.start(progress);
			a_loadProgress.start(loadProgress);
			_a_progress.start();
		} else {
			a_progress = anim::fvalue(progress, progress);
			a_loadProgress = anim::fvalue(loadProgress, loadProgress);
			_a_progress.stop();
		}
		_position = position;
		_duration = duration;
		_loaded = loaded;
	}

	if (wasPlaying && playingState == AudioPlayerStoppedAtEnd) {
		if (_repeat) {
			startPlay(_song.msgId);
		} else {
			nextPressed();
		}
	}

	if (songChanged) {
		emit playerSongChanged(_song.msgId);
	}
}
void AudioPlayerLoaders::loadData(AudioMsgId audio, qint64 position) {
	SetupError err = SetupNoErrorStarted;
	auto type = audio.type();
	AudioPlayerLoader *l = setupLoader(audio, err, position);
	if (!l) {
		if (err == SetupErrorAtStart) {
			emitError(type);
		}
		return;
	}

	bool started = (err == SetupNoErrorStarted);
	bool finished = false;
	bool waiting = false;
	bool errAtStart = started;

	QByteArray samples;
	int64 samplesCount = 0;
	if (l->holdsSavedDecodedSamples()) {
		l->takeSavedDecodedSamples(&samples, &samplesCount);
	}
	while (samples.size() < AudioVoiceMsgBufferSize) {
		auto res = l->readMore(samples, samplesCount);
		using Result = AudioPlayerLoader::ReadResult;
		if (res == Result::Error) {
			if (errAtStart) {
				{
					QMutexLocker lock(internal::audioPlayerMutex());
					AudioPlayer::AudioMsg *m = checkLoader(type);
					if (m) m->playbackState.state = AudioPlayerStoppedAtStart;
				}
				emitError(type);
				return;
			}
			finished = true;
			break;
		} else if (res == Result::EndOfFile) {
			finished = true;
			break;
		} else if (res == Result::Ok) {
			errAtStart = false;
		} else if (res == Result::Wait) {
			waiting = (samples.size() < AudioVoiceMsgBufferSize);
			if (waiting) {
				l->saveDecodedSamples(&samples, &samplesCount);
			}
			break;
		}

		QMutexLocker lock(internal::audioPlayerMutex());
		if (!checkLoader(type)) {
			clear(type);
			return;
		}
	}

	QMutexLocker lock(internal::audioPlayerMutex());
	AudioPlayer::AudioMsg *m = checkLoader(type);
	if (!m) {
		clear(type);
		return;
	}

	if (started) {
		if (m->source) {
			alSourceStop(m->source);
			for (int32 i = 0; i < 3; ++i) {
				if (m->samplesCount[i]) {
					ALuint buffer = 0;
					alSourceUnqueueBuffers(m->source, 1, &buffer);
					m->samplesCount[i] = 0;
				}
			}
			m->nextBuffer = 0;
		}
		m->skipStart = position;
		m->skipEnd = m->playbackState.duration - position;
		m->playbackState.position = position;
		m->started = 0;
	}
	if (samplesCount) {
		if (!m->source) {
			alGenSources(1, &m->source);
			alSourcef(m->source, AL_PITCH, 1.f);
			alSource3f(m->source, AL_POSITION, 0, 0, 0);
			alSource3f(m->source, AL_VELOCITY, 0, 0, 0);
			alSourcei(m->source, AL_LOOPING, 0);
		}
		if (!m->buffers[m->nextBuffer]) {
			alGenBuffers(3, m->buffers);
		}

		// If this buffer is queued, try to unqueue some buffer.
		if (m->samplesCount[m->nextBuffer]) {
			ALint processed = 0;
			alGetSourcei(m->source, AL_BUFFERS_PROCESSED, &processed);
			if (processed < 1) { // No processed buffers, wait.
				l->saveDecodedSamples(&samples, &samplesCount);
				return;
			}

			// Unqueue some processed buffer.
			ALuint buffer = 0;
			alSourceUnqueueBuffers(m->source, 1, &buffer);
			if (!internal::audioCheckError()) {
				setStoppedState(m, AudioPlayerStoppedAtError);
				emitError(type);
				return;
			}

			// Find it in the list and make it the nextBuffer.
			bool found = false;
			for (int i = 0; i < 3; ++i) {
				if (m->buffers[i] == buffer) {
					found = true;
					m->nextBuffer = i;
					break;
				}
			}
			if (!found) {
				LOG(("Audio Error: Could not find the unqueued buffer! Buffer %1 in source %2 with processed count %3").arg(buffer).arg(m->source).arg(processed));
				setStoppedState(m, AudioPlayerStoppedAtError);
				emitError(type);
				return;
			}

			if (m->samplesCount[m->nextBuffer]) {
				m->skipStart += m->samplesCount[m->nextBuffer];
				m->samplesCount[m->nextBuffer] = 0;
			}
		}

		auto frequency = l->frequency();
		auto format = l->format();
		m->samplesCount[m->nextBuffer] = samplesCount;
		alBufferData(m->buffers[m->nextBuffer], format, samples.constData(), samples.size(), frequency);

		alSourceQueueBuffers(m->source, 1, m->buffers + m->nextBuffer);
		m->skipEnd -= samplesCount;

		m->nextBuffer = (m->nextBuffer + 1) % 3;

		if (!internal::audioCheckError()) {
			setStoppedState(m, AudioPlayerStoppedAtError);
			emitError(type);
			return;
		}
	} else {
		if (waiting) {
			return;
		}
		finished = true;
	}

	if (finished) {
		m->skipEnd = 0;
		m->playbackState.duration = m->skipStart + m->samplesCount[0] + m->samplesCount[1] + m->samplesCount[2];
		clear(type);
	}

	m->loading = false;
	if (m->playbackState.state == AudioPlayerResuming || m->playbackState.state == AudioPlayerPlaying || m->playbackState.state == AudioPlayerStarting) {
		ALint state = AL_INITIAL;
		alGetSourcei(m->source, AL_SOURCE_STATE, &state);
		if (internal::audioCheckError()) {
			if (state != AL_PLAYING) {
				audioPlayer()->resumeDevice();

				switch (type) {
				case AudioMsgId::Type::Voice: alSourcef(m->source, AL_GAIN, internal::audioSuppressGain()); break;
				case AudioMsgId::Type::Song: alSourcef(m->source, AL_GAIN, internal::audioSuppressSongGain() * Global::SongVolume()); break;
				case AudioMsgId::Type::Video: alSourcef(m->source, AL_GAIN, internal::audioSuppressSongGain() * Global::VideoVolume()); break;
				}
				if (!internal::audioCheckError()) {
					setStoppedState(m, AudioPlayerStoppedAtError);
					emitError(type);
					return;
				}

				alSourcePlay(m->source);
				if (!internal::audioCheckError()) {
					setStoppedState(m, AudioPlayerStoppedAtError);
					emitError(type);
					return;
				}

				emit needToCheck();
			}
		} else {
			setStoppedState(m, AudioPlayerStoppedAtError);
			emitError(type);
		}
	}
}