Skip to content

Commit

Permalink
Use libcdio-paranoia to read CDs on Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
vicr123 committed Dec 1, 2024
1 parent e8d4274 commit 84425f2
Show file tree
Hide file tree
Showing 24 changed files with 1,158 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/linux-appimage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Install gstreamer
run: |
sudo apt-get update
sudo apt-get install libutf8proc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio libcdio++-dev libcups2-dev
sudo apt-get install libutf8proc-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio libcdio++-dev libcups2-dev libcdio-paranoia-dev
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
Expand Down
1 change: 1 addition & 0 deletions plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_subdirectory(CdrdaoPlugin)
add_subdirectory(LinuxIntegration)
add_subdirectory(GstPlugin)
add_subdirectory(ParanoiaPlugin)
ENDIF()

IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
Expand Down
3 changes: 2 additions & 1 deletion plugins/GstPlugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ find_package(libthefrisbee)
pkg_check_modules(gstreamer IMPORTED_TARGET REQUIRED gstreamer-1.0)
pkg_check_modules(cdio IMPORTED_TARGET libcdio++)
pkg_check_modules(MusicBrainz IMPORTED_TARGET libmusicbrainz5)
pkg_check_modules(cdio-paranoia IMPORTED_TARGET libcdio_paranoia)

set(SOURCES
mediaitem/gstmediaitem.cpp
Expand Down Expand Up @@ -45,7 +46,7 @@ qt_add_qml_module(plugin-gst
SOURCES gsttrackinfo.h gsttrackinfo.cpp
)

target_link_libraries(plugin-gst PRIVATE Qt::Widgets Qt::DBus PkgConfig::gstreamer libcontemporary libthefrisbee libthebeat)
target_link_libraries(plugin-gst PRIVATE Qt::Widgets Qt::DBus PkgConfig::gstreamer PkgConfig::cdio-paranoia libcontemporary libthefrisbee libthebeat)

IF(${cdio_FOUND})
target_link_libraries(plugin-gst PRIVATE PkgConfig::cdio)
Expand Down
39 changes: 39 additions & 0 deletions plugins/GstPlugin/gstcdcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
#include <sourcemanager.h>
#include <statemanager.h>

#include <QAudioSink>

#ifdef HAVE_CDIO
#include <cdio++/cdio.hpp>
#include <cdio/paranoia/paranoia.h>
#endif

struct GstCdControllerPrivate {
Expand Down Expand Up @@ -98,6 +101,42 @@ void GstCdController::readCd() {
}
}

auto drive = cdio_cddap_identify_cdio(device.getCdIo(), CDDA_MESSAGE_PRINTIT, nullptr);
cdio_cddap_open(drive);
// auto drive = cdio_cddap_find_a_cdrom(CDDA_MESSAGE_PRINTIT, nullptr);
auto firstSector = cdda_disc_firstsector(drive);
cdda_verbose_set(drive, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT);

auto paranoia = cdio_paranoia_init(drive);
cdio_paranoia_modeset(paranoia, PARANOIA_MODE_FULL);
cdio_paranoia_seek(paranoia, firstSector, SEEK_SET);

QAudioFormat format;
format.setChannelCount(2);
format.setSampleRate(44100);
format.setSampleFormat(QAudioFormat::Int16);

QAudioSink sink(format);
auto audioOut = sink.start();

for (auto i = 0; i < 1000; i++) {
auto buf = cdio_paranoia_read(paranoia, nullptr);
char* psz_err = cdda_errors(drive);
char* psz_mes = cdda_messages(drive);

if (psz_mes || psz_err)
printf("%s%s\n", psz_mes ? psz_mes : "", psz_err ? psz_err : "");

while (sink.bytesFree() < CDIO_CD_FRAMESIZE_RAW);
if (!buf) continue;
audioOut->write(reinterpret_cast<const char*>(buf), CDIO_CD_FRAMESIZE_RAW);

free(psz_err);
free(psz_mes);
}

cdio_paranoia_free(paranoia);

emit dataChanged(index(0), index(rowCount() - 1));

if (discFields.contains("TITLE")) {
Expand Down
4 changes: 2 additions & 2 deletions plugins/GstPlugin/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ Plugin::~Plugin() {
void Plugin::activate() {
StateManager::instance()->url()->registerHandler(new GstUrlHandler());

d->monitor = new CdMonitor();
// d->monitor = new CdMonitor();
}

void Plugin::deactivate() {
d->monitor->deleteLater();
// d->monitor->deleteLater();
}
2 changes: 1 addition & 1 deletion plugins/GstPlugin/translations/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<context>
<name>GstCdController</name>
<message>
<location filename="../gstcdcontroller.cpp" line="30"/>
<location filename="../gstcdcontroller.cpp" line="33"/>
<source>CD</source>
<translation type="unfinished"></translation>
</message>
Expand Down
2 changes: 1 addition & 1 deletion plugins/GstPlugin/translations/he_IL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
<context>
<name>GstCdController</name>
<message>
<location filename="../gstcdcontroller.cpp" line="30"/>
<location filename="../gstcdcontroller.cpp" line="33"/>
<source>CD</source>
<translation type="unfinished">CD</translation>
</message>
Expand Down
49 changes: 49 additions & 0 deletions plugins/ParanoiaPlugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
project(lib VERSION 1.0.0 LANGUAGES CXX)

find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets DBus)
find_package(libcontemporary)
find_package(libthefrisbee)
pkg_check_modules(cdio IMPORTED_TARGET libcdio++)
pkg_check_modules(MusicBrainz IMPORTED_TARGET libmusicbrainz5)
pkg_check_modules(cdio-paranoia IMPORTED_TARGET libcdio_paranoia)

set(SOURCES
cdmonitor.cpp
plugin.cpp
)

set(HEADERS
cdmonitor.h
plugin.h
)

add_library(plugin-paranoia SHARED ${SOURCES} ${HEADERS})
target_include_directories(plugin-paranoia PUBLIC ../../libthebeat/)

cntp_init_plugin(thebeat plugin-paranoia 20 paranoia)
cntp_translate(plugin-paranoia)

set_target_properties(plugin-paranoia PROPERTIES
OUTPUT_NAME paranoiaPlugin
FRAMEWORK FALSE)

qt_add_qml_module(plugin-paranoia
URI com.vicr123.thebeat.plugin.paranoia
VERSION 1.0
QML_FILES
ParanoiaCdPane.qml
NO_CACHEGEN
SOURCES paranoiacdcontroller.h paranoiacdcontroller.cpp
SOURCES paranoiatrackinfo.h paranoiatrackinfo.cpp
QML_FILES ParanoiaPlugin.json
SOURCES paranoiacdpluginmediasource.h paranoiacdpluginmediasource.cpp
SOURCES paranoiaplayer.h paranoiaplayer.cpp
SOURCES paranoiamediaitem.h paranoiamediaitem.cpp
)

target_link_libraries(plugin-paranoia PRIVATE Qt::Widgets Qt::DBus PkgConfig::cdio PkgConfig::cdio-paranoia libcontemporary libthefrisbee libthebeat)

IF(${MusicBrainz_FOUND})
target_link_libraries(plugin-paranoia PRIVATE PkgConfig::MusicBrainz)
target_compile_definitions(plugin-paranoia PUBLIC HAVE_MUSICBRAINZ)
ENDIF()
105 changes: 105 additions & 0 deletions plugins/ParanoiaPlugin/ParanoiaCdPane.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import QtQuick 2.15
import QtQuick.Layouts
import QtQuick.Controls
import com.vicr123.Contemporary
import com.vicr123.thebeat

Item {
property var source
readonly property var controller: source.controller

Pager {
anchors.fill: parent

Item {
LibraryHeader {
id: grandstand
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
innerTopMargin: SafeZone.top
z: 20

text: controller.albumName
color: layer1.color

RowLayout {
anchors.left: parent.left
anchors.right: parent.right

Button {
text: qsTr("Enqueue All")
icon.name: "view-media-playlist"

onClicked: () => trackList.enqueueAll()
}
Button {
text: qsTr("Play All")
icon.name: "media-playback-start"

onClicked: () => {
PlaylistManager.clear();
trackList.enqueueAll();
}
}
Button {
text: qsTr("Shuffle All")
icon.name: "media-playlist-shuffle"

onClicked: () => {
trackList.enqueueAll();
PlaylistManager.shuffle = true;
PlaylistManager.next();
}
}
Item {
Layout.fillWidth: true
}
Button {
text: qsTr("Eject")
icon.name: "media-eject"

onClicked: () => {
controller.eject();
}
}
}
}

ColumnLayout {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.top: grandstand.bottom

LibraryListing {
id: trackList
Layout.fillHeight: true
Layout.fillWidth: true
model: controller

onEnqueueItem: index => {
const mediaItem = controller.mediaItem(index);
PlaylistManager.addItem(mediaItem);
PlaylistManager.currentItem = mediaItem;
}

function enqueueAll() {
for (var i = 0; i < trackList.model.rowCount(); i++) {
const mediaItem = controller.mediaItem(i);
PlaylistManager.addItem(mediaItem);
}
}
}
}
}
}

Connections {
target: controller

function onEjectError() {
ejectErrorDialog.visible = true;
}
}
}
5 changes: 5 additions & 0 deletions plugins/ParanoiaPlugin/ParanoiaPlugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Paranoia",
"icon": "media-cd-import",
"uuid": "1a1f9695-efee-4e68-96fb-b8c392f14885"
}
54 changes: 54 additions & 0 deletions plugins/ParanoiaPlugin/cdmonitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "cdmonitor.h"

#include "paranoiacdcontroller.h"
#include <DriveObjects/blockinterface.h>
#include <DriveObjects/diskobject.h>
#include <DriveObjects/driveinterface.h>
#include <driveobjectmanager.h>
#include <statemanager.h>

struct CdMonitorPrivate {
QMap<DiskObject*, ParanoiaCdController*> panes;
};

CdMonitor::CdMonitor(QObject* parent) :
QObject{parent} {
d = new CdMonitorPrivate();

for (auto drive : DriveObjectManager::drives()) {
connect(drive, &DriveInterface::changed, this, &CdMonitor::updateDisks);
}
connect(DriveObjectManager::instance(), &DriveObjectManager::driveAdded, this, [this](DriveInterface* drive) {
connect(drive, &DriveInterface::changed, this, &CdMonitor::updateDisks);
});
QTimer::singleShot(0, this, &CdMonitor::updateDisks);
updateDisks();
}

CdMonitor::~CdMonitor() {
delete d;
}

void CdMonitor::updateDisks() {
QList<DiskObject*> keepDisks;
for (auto disk : DriveObjectManager::opticalDisks()) {
auto drive = disk->interface<BlockInterface>()->drive();
if (!drive->mediaAvailable()) continue;
if (drive->audioTracks() == 0) continue;

if (!d->panes.contains(disk)) {
auto widget = new ParanoiaCdController(disk);
d->panes.insert(disk, widget);
}

keepDisks.append(disk);
}

auto oldDisks = d->panes.keys();
for (auto disk : oldDisks) {
if (!keepDisks.contains(disk)) {
auto widget = d->panes.take(disk);
widget->deleteLater();
}
}
}
21 changes: 21 additions & 0 deletions plugins/ParanoiaPlugin/cdmonitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef CDMONITOR_H
#define CDMONITOR_H

#include <QObject>

struct CdMonitorPrivate;
class CdMonitor : public QObject {
Q_OBJECT
public:
explicit CdMonitor(QObject* parent = nullptr);
~CdMonitor();

signals:

private:
CdMonitorPrivate* d;

void updateDisks();
};

#endif // CDMONITOR_H
Loading

0 comments on commit 84425f2

Please sign in to comment.