FU Logo
  • Startseite
  • Kontakt
  • Impressum
  • Home
  • Listenauswahl
  • Anleitungen

[linux-minidisc] netmd/netmdcli: download support for any .wav file containing pcm audio

thread -->
date -->
  • From: Thomas Arp <manner.moe@gmx.de>
  • To: "linux-minidisc@lists.fu-berlin.de" <linux-minidisc@lists.fu-berlin.de>
  • Date: Wed, 01 Jan 2014 20:42:40 +0100
  • Subject: [linux-minidisc] netmd/netmdcli: download support for any .wav file containing pcm audio

Hi,

i made some changes to libnetmd and netmdcli to support download of audio files in pcm format (filename: *.wav, codec: pcm, 16 bit, 4100 Hz, stereo). Codec used on the minidisc is Atrac SP stereo, but this can be changed anytime if wanted.

Thomas
>From 7c9b7bc6f6dc87a0d7fb421a271cbde63026a6df Mon Sep 17 00:00:00 2001
From: Thomas Arp <manner.moe@gmx.de>
Date: Wed, 1 Jan 2014 20:30:24 +0100
Subject: [PATCH 1/1] support netmd downloads for any .wav file containing
 pcm(16 bit, 4100 Hz, stereo) audio data

---
 libnetmd/secure.c   |   17 ++--
 libnetmd/secure.h   |    4 +-
 netmdcli/netmdcli.c |  222 ++++++++++++++++++++++++++++++++-------------------
 3 files changed, 151 insertions(+), 92 deletions(-)

diff --git a/libnetmd/secure.c b/libnetmd/secure.c
index a3dbb6e..e770571 100644
--- a/libnetmd/secure.c
+++ b/libnetmd/secure.c
@@ -370,8 +370,8 @@ void netmd_transfer_song_packets(netmd_dev_handle *dev,
         memcpy(buf + 8, p->iv, 8);
         memcpy(buf + 16, p->data, p->length);
 
-        /* ... send it */
-        error = libusb_bulk_transfer((libusb_device_handle*)dev, 2, packet, (int)packet_size, &transferred, 10000);
+        /* ... send it, increased timeout, 10000 is too low for larger files */
+        error = libusb_bulk_transfer((libusb_device_handle*)dev, 2, packet, (int)packet_size, &transferred, 80000);
         netmd_log(NETMD_LOG_DEBUG, "%d %d\n", packet_size, error);
 
         /* cleanup */
@@ -387,11 +387,12 @@ void netmd_transfer_song_packets(netmd_dev_handle *dev,
 
 netmd_error netmd_prepare_packets(unsigned char* data, size_t data_lenght,
                                   netmd_track_packets **packets,
-                                  size_t *packet_count,
-                                  unsigned char *key_encryption_key)
+                                  size_t *packet_count, size_t *frames,
+                                  unsigned char *key_encryption_key, netmd_wireformat format)
 {
     size_t position = 0;
     size_t chunksize = 0xffffffffU;
+    size_t frame_size = netmd_get_frame_size(format);
     netmd_track_packets *last = NULL;
     netmd_track_packets *next = NULL;
 
@@ -417,9 +418,9 @@ netmd_error netmd_prepare_packets(unsigned char* data, size_t data_lenght,
             /* limit chunksize for last packet */
             chunksize = data_lenght - position;
         }
-
-        if ((chunksize % 8) != 0) {
-            chunksize = chunksize + 8 - (chunksize % 8);
+        /* do not truncate frames */
+        if ((chunksize % frame_size) != 0) {
+            chunksize = chunksize + frame_size - (chunksize % frame_size);
         }
 
         /* alloc memory */
@@ -459,6 +460,8 @@ netmd_error netmd_prepare_packets(unsigned char* data, size_t data_lenght,
     gcry_cipher_close(key_handle);
     gcry_cipher_close(data_handle);
 
+    *frames = position/frame_size;
+
     return error;
 }
 
diff --git a/libnetmd/secure.h b/libnetmd/secure.h
index 41e41ce..50a7060 100644
--- a/libnetmd/secure.h
+++ b/libnetmd/secure.h
@@ -191,8 +191,8 @@ netmd_error netmd_secure_delete_track(netmd_dev_handle *dev, uint16_t track,
 
 netmd_error netmd_prepare_packets(unsigned char* data, size_t data_lenght,
                                   netmd_track_packets **packets,
-                                  size_t *packet_count,
-                                  unsigned char *key_encryption_key);
+                                  size_t *packet_count, size_t *frames,
+                                  unsigned char *key_encryption_key, netmd_wireformat format);
 
 void netmd_cleanup_packets(netmd_track_packets **packets);
 
diff --git a/netmdcli/netmdcli.c b/netmdcli/netmdcli.c
index 354d03c..41559e2 100644
--- a/netmdcli/netmdcli.c
+++ b/netmdcli/netmdcli.c
@@ -168,6 +168,26 @@ void retailmac(unsigned char *rootkey, unsigned char *hostnonce,
     gcry_cipher_close(handle2);
 }
 
+static inline unsigned int leword32(const unsigned char * c)
+{
+    return c[3]*16777216+c[2]*65536+c[1]*256+c[0];
+}
+
+static int wav_data_position(const char * data, size_t len)
+{
+    int pos = -1, i = 0;
+    while(pos < 0)
+    {
+        if(i >= len-4) // break at end of data
+            break;
+
+        if(strcmp("data", data+i) == 0)
+            pos = i;
+        i+=2;
+    }
+    return pos;
+}
+
 int main(int argc, char* argv[])
 {
     netmd_dev_handle* devh;
@@ -444,104 +464,140 @@ int main(int argc, char* argv[])
             uint16_t track;
             unsigned char uuid[8] = { 0 };
             unsigned char new_contentid[20] = { 0 };
+            char title[256] = {0};
 
-            error = netmd_secure_leave_session(devh);
-            puts(netmd_strerror(error));
+            size_t frames;
+            int data_position, audio_data_position, audio_data_size, i, file_valid = 0;
+            unsigned char * audio_data;
+            netmd_wireformat wireformat = NETMD_WIREFORMAT_PCM;
+            unsigned char discformat = NETMD_DISKFORMAT_SP_STEREO;
 
-            error = netmd_secure_set_track_protection(devh, 0x01);
-            puts(netmd_strerror(error));
-
-            error = netmd_secure_enter_session(devh);
-            puts(netmd_strerror(error));
+            /* read source */
+            stat(argv[2], &stat_buf);
+            data_size = (size_t)stat_buf.st_size;
+            data = malloc(data_size);
+            f = fopen(argv[2], "rb");
+            fread(data, data_size, 1, f);
+            fclose(f);
 
-            /* build ekb */
-            ekb.id = 0x26422642;
-            ekb.depth = 9;
-            ekb.signature = malloc(sizeof(signature));
-            memcpy(ekb.signature, signature, sizeof(signature));
+            /* TODO: check file for codec, PCM (16 bit, 44100 Hz, stereo) should work correctly*/
+            if((data_position = wav_data_position((char *)data, data_size)) <= 0)
+            {
+                puts("Error: invalid audio file, cannot find data header position");
+                free(data);
+            }
+            else
+            {
+                audio_data_position = data_position+8;
+                audio_data = data+audio_data_position;
+                audio_data_size = leword32(data+(data_position+4));
+                file_valid = 1;
+            }
 
-            /* build ekb key chain */
-            ekb.chain = NULL;
-            for (done = 0; done < sizeof(chain); done+=16U)
+            if(file_valid)
             {
-                next = malloc(sizeof(netmd_keychain));
-                if (ekb.chain == NULL) {
-                    ekb.chain = next;
-                }
-                else {
-                    keychain->next = next;
-                }
-                next->next = NULL;
+                error = netmd_secure_leave_session(devh);
+                puts(netmd_strerror(error));
 
-                next->key = malloc(16);
-                memcpy(next->key, chain + done, 16);
+                error = netmd_secure_set_track_protection(devh, 0x01);
+                puts(netmd_strerror(error));
 
-                keychain = next;
-            }
+                error = netmd_secure_enter_session(devh);
+                puts(netmd_strerror(error));
 
-            error = netmd_secure_send_key_data(devh, &ekb);
-            puts(netmd_strerror(error));
-
-            /* cleanup */
-            free(ekb.signature);
-            keychain = ekb.chain;
-            while (keychain != NULL) {
-                next = keychain->next;
-                free(keychain->key);
-                free(keychain);
-                keychain = next;
-            }
+                /* build ekb */
+                ekb.id = 0x26422642;
+                ekb.depth = 9;
+                ekb.signature = malloc(sizeof(signature));
+                memcpy(ekb.signature, signature, sizeof(signature));
 
-            /* exchange nonces */
-            gcry_create_nonce(hostnonce, sizeof(hostnonce));
-            error = netmd_secure_session_key_exchange(devh, hostnonce, devnonce);
-            puts(netmd_strerror(error));
+                /* build ekb key chain */
+                ekb.chain = NULL;
+                for (done = 0; done < sizeof(chain); done+=16U)
+                {
+                    next = malloc(sizeof(netmd_keychain));
+                    if (ekb.chain == NULL) {
+                        ekb.chain = next;
+                    }
+                    else {
+                        keychain->next = next;
+                        }
+                    next->next = NULL;
+
+                    next->key = malloc(16);
+                    memcpy(next->key, chain + done, 16);
+
+                    keychain = next;
+                }
 
-            /* calculate session key */
-            retailmac(rootkey, hostnonce, devnonce, sessionkey);
+                error = netmd_secure_send_key_data(devh, &ekb);
+                puts(netmd_strerror(error));
+
+                /* cleanup */
+                free(ekb.signature);
+                keychain = ekb.chain;
+                while (keychain != NULL) {
+                    next = keychain->next;
+                    free(keychain->key);
+                    free(keychain);
+                    keychain = next;
+                }
 
-            error = netmd_secure_setup_download(devh, contentid, kek, sessionkey);
-            puts(netmd_strerror(error));
+                /* exchange nonces */
+                gcry_create_nonce(hostnonce, sizeof(hostnonce));
+                error = netmd_secure_session_key_exchange(devh, hostnonce, devnonce);
+                puts(netmd_strerror(error));
 
-            /* read source */
-            stat(argv[2], &stat_buf);
-            data_size = (size_t)stat_buf.st_size;
-            data = malloc(data_size);
-            f = fopen(argv[2], "rb");
-            fseek(f, 60, SEEK_CUR);
-            fread(data, data_size - 60, 1, f);
-            fclose(f);
-            error = netmd_prepare_packets(data, data_size-60, &packets, &packet_count, kek);
-            puts(netmd_strerror(error));
-
-            /* send to device */
-            error = netmd_secure_send_track(devh, NETMD_WIREFORMAT_LP2,
-                                            NETMD_DISKFORMAT_LP2,
-                                            (data_size - 60) / 192, packets,
-                                            packet_count, sessionkey,
-                                            &track, uuid, new_contentid);
-            puts(netmd_strerror(error));
-
-            /* cleanup */
-            netmd_cleanup_packets(&packets);
-
-            /* set title */
-            netmd_log(NETMD_LOG_DEBUG, "New Track: %d\n", track);
-            netmd_cache_toc(devh);
-            netmd_set_title(devh, track, "test");
-            netmd_sync_toc(devh);
+                /* calculate session key */
+                retailmac(rootkey, hostnonce, devnonce, sessionkey);
 
-            /* commit track */
-            error = netmd_secure_commit_track(devh, track, sessionkey);
-            puts(netmd_strerror(error));
+                error = netmd_secure_setup_download(devh, contentid, kek, sessionkey);
+                puts(netmd_strerror(error));
 
-            /* forget key */
-            error = netmd_secure_session_key_forget(devh);
-            puts(netmd_strerror(error));
+                /* audio data byte order conversion, .wav files are little endian, need big endian for pcm raw data*/
+                for(i = 0; i < audio_data_size/2; i+=2)
+                {
+                    unsigned char first = audio_data[i];
+                    audio_data[i] = audio_data[i+1];
+                    audio_data[i+1] = first;
+                }
 
-            /* leave session */
-            error = netmd_secure_leave_session(devh);
-            puts(netmd_strerror(error));
+                /* netmd_prepare_packets() sets correct number of frames depending on the wire format */
+                error = netmd_prepare_packets(audio_data, audio_data_size, &packets, &packet_count, &frames, kek, wireformat);
+                puts(netmd_strerror(error));
+
+                /* send to device */
+                error = netmd_secure_send_track(devh, wireformat,
+                                                discformat,
+                                                frames, packets,
+                                                packet_count, sessionkey,
+                                                &track, uuid, new_contentid);
+                puts(netmd_strerror(error));
+
+                /* cleanup */
+                netmd_cleanup_packets(&packets);
+                free(data);
+                audio_data = NULL;
+
+                /* set title, use filename */
+                memcpy(title, argv[2], strlen(argv[2])-4);
+                netmd_log(NETMD_LOG_DEBUG, "New Track: %d\n", track);
+                netmd_cache_toc(devh);
+                netmd_set_title(devh, track, title);
+                netmd_sync_toc(devh);
+
+                /* commit track */
+                error = netmd_secure_commit_track(devh, track, sessionkey);
+                puts(netmd_strerror(error));
+
+                /* forget key */
+                error = netmd_secure_session_key_forget(devh);
+                puts(netmd_strerror(error));
+
+                /* leave session */
+                error = netmd_secure_leave_session(devh);
+                puts(netmd_strerror(error));
+            }
         }
         else if(strcmp("help", argv[1]) == 0)
         {
-- 
1.7.10.4

thread -->
date -->
  • Follow-Ups:
    • Re: [linux-minidisc] netmd/netmdcli: download support for any .wav file containing pcm audio
      • From: Thomas Arp <manner.moe@gmx.de>
    • Re: [linux-minidisc] netmd/netmdcli: download support for any .wav file containing pcm audio
      • From: Michael Karcher <Michael.Karcher@fu-berlin.de>
  • linux-minidisc - January 2014 - Archives indexes sorted by:
    [ thread ] [ subject ] [ author ] [ date ]
  • Complete archive of the linux-minidisc mailing list
  • More info on this list...

Hilfe

  • FAQ
  • Dienstbeschreibung
  • ZEDAT Beratung
  • postmaster@lists.fu-berlin.de

Service-Navigation

  • Startseite
  • Listenauswahl

Einrichtung Mailingliste

  • ZEDAT-Portal
  • Mailinglisten Portal