7af2528bfd
- The majority of user interaction is now in the system tray icon. There's no longer a traditional user interface window. - Added a proper application version number, GPL and source code comments. - The application will now allow only one instance and will play the file requested in the arguments in the currently running instance. - Added mute control. - Settings are now saved in csv file format instead of idm. - Several bug fixes. - Seeker removed for now. (might but it back in the future)
159 lines
4.3 KiB
C++
159 lines
4.3 KiB
C++
#include "aud_file.h"
|
|
|
|
// This file is part of JustAudio.
|
|
|
|
// JustAudio is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// JustAudio is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with BashWire under the GPL.txt file. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
AudFile::AudFile(QObject *parent) : QFile(parent)
|
|
{
|
|
// QMediaPlayer doesn't handle files with an ID3 tag and variable bitrates that well so
|
|
// this class was created to offset the ID3 tags without modifying the audio files and
|
|
// to have the GUI rely on the actual byte seek position of the audio track rather than
|
|
// the time read by QMediaPlayer.
|
|
|
|
offset = 0;
|
|
v1TagSize = 0;
|
|
activeInterupt = false;
|
|
timer = new QTimer(this);
|
|
|
|
timer->setSingleShot(true);
|
|
|
|
connect(timer, SIGNAL(timeout()), this, SIGNAL(endOfPlayback()));
|
|
}
|
|
|
|
AudFile::~AudFile()
|
|
{
|
|
close();
|
|
}
|
|
|
|
qint64 AudFile::audSize()
|
|
{
|
|
return size() - offset - v1TagSize;
|
|
}
|
|
|
|
qint64 AudFile::getOffset()
|
|
{
|
|
return offset;
|
|
}
|
|
|
|
bool AudFile::openFile(const QString &path)
|
|
{
|
|
close();
|
|
|
|
v1TagSize = 0;
|
|
offset = 0;
|
|
activeInterupt = false;
|
|
|
|
timer->stop();
|
|
|
|
setFileName(path);
|
|
|
|
bool ret = open(QFile::ReadOnly);
|
|
|
|
if (ret)
|
|
{
|
|
seek((size() - 1) - 128);
|
|
|
|
if (peek(3) == "TAG")
|
|
{
|
|
// find an ID3v1 tag if it has any. the tag always start with "TAG" and appended
|
|
// to the end of the file with a fixed length of 128 bytes. v1 tags don't affect
|
|
// playback with QMediaPlayer but this info will still be needed in audSize().
|
|
|
|
v1TagSize = 128;
|
|
}
|
|
|
|
seek(0);
|
|
|
|
if (peek(3) == "ID3")
|
|
{
|
|
// find the ID3v2 tag if it has any and set the file offset position to skip over it.
|
|
// the tag has a 10byte header that starts with "ID3" and ends with a 4 byte
|
|
// big endian integer that indicates the tag size.
|
|
|
|
QByteArray header = read(10);
|
|
QByteArray intBytes = header.mid(6);
|
|
quint64 num = 0;
|
|
quint64 bit = 1;
|
|
|
|
offset += 10; // skip header
|
|
|
|
if (header[5] & 16)
|
|
{
|
|
// footer flag check. if the tag has a footer, an additional 10bytes need
|
|
// to be added to the offset.
|
|
|
|
offset += 10; // skip footer
|
|
}
|
|
|
|
for (int i = intBytes.size() - 1; i >= 0; --i)
|
|
{
|
|
// read the integer bit-by-bit, setting the bits in "num" as it goes. note
|
|
// that its reading the bytes from "right to left" for big endian format.
|
|
|
|
int byte = intBytes[i];
|
|
|
|
for (int inBit = 1; inBit <= 64; inBit *= 2, bit *= 2)
|
|
{
|
|
if ((byte & inBit) != 0) num |= bit;
|
|
}
|
|
}
|
|
|
|
offset += num; // skip tag
|
|
}
|
|
|
|
seek(0);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool AudFile::seek(qint64 off)
|
|
{
|
|
// QMediaPlayer constantly seeks the audio file for data while playing. each seek
|
|
// resets the endOfPlayback() timer. if that timer is allowed to timeout, that is
|
|
// considered finished playback. the player also tend to do another seek after
|
|
// being paused so activeInterupt is there to prevent the timer from restarting
|
|
// unexpectly after userInterupt() is called.
|
|
|
|
if (!activeInterupt)
|
|
{
|
|
timer->stop();
|
|
timer->start(5000);
|
|
}
|
|
|
|
emit bytePos(pos());
|
|
|
|
return QFile::seek(offset + off);
|
|
}
|
|
|
|
void AudFile::userInterupt()
|
|
{
|
|
// the timer needs to be aware if the user paused or stopped playback. this way
|
|
// the endOfPlayback() signal is not called unexpectedly.
|
|
|
|
activeInterupt = true;
|
|
|
|
timer->stop();
|
|
}
|
|
|
|
void AudFile::userResume()
|
|
{
|
|
// after a pause, this class will also need to be aware of a user resume to remove
|
|
// the activeInterupt status.
|
|
|
|
activeInterupt = false;
|
|
}
|