1 คะแนน โดย GN⁺ 2025-05-24 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เริ่มจากปัญหาที่แม้ในปี 2025 ผู้ใช้ก็ยังฟังและซิงก์ไฟล์เพลงที่ตนเองมีบน iPhone ได้อย่างอิสระได้ยาก จึงลงมือสร้าง เครื่องเล่นเพลงแบบ local-first ด้วย SwiftUI เอง
  • หากยกเลิกการสมัคร Apple Music การซิงก์ผ่าน iCloud Music Library จะหยุดลง ส่วน iTunes Match เป็นตัวเลือกแบบเสียเงินแยกต่างหากปีละ $24.99 สำหรับเก็บสำเนา AAC 256kbps ไว้ออนไลน์
  • React Native/Expo มักล้มเหลวหรือแครชบ่อยเมื่อไล่ดูโฟลเดอร์ iCloud ที่ซ้อนลึกและจัดการสิทธิ์ไฟล์ ทำให้การทำด้วย SwiftUI ที่ใช้ API เนทีฟของ Apple เป็นทางเลือกที่ง่ายกว่า
  • แอปใช้ SQLite FTS5 ค้นหาชื่อไฟล์และเมทาดาทาอย่าง artist, album, title และแยกส่วน iCloud import, การจัดการไลบรารี, หน้าจอเล่นเพลงออกจากกัน
  • เนื่องจาก iOS มีข้อจำกัดเรื่อง security-scoped bookmark และบิลด์สำหรับนักพัฒนาส่วนบุคคล แม้แต่การใช้งานแอปที่สร้างเองให้เสถียรในระยะยาวก็ยังมี อุปสรรคด้านการเข้าถึงไฟล์และการแจกจ่าย อยู่

ทำไมถึงสร้างเครื่องเล่นเพลงเอง

  • หากต้องการเล่นไฟล์เพลงที่ผู้ใช้มีเองบน iPhone สถานการณ์ที่ต้องจ่ายเงินให้ Apple หรือหาทางอ้อมข้อจำกัดหลายอย่างยังคงมีอยู่
  • สำหรับผู้ใช้ที่จัดไลบรารีเพลงในเครื่องไว้แล้ว บริการสตรีมมิงเป็นเหมือนฟีเจอร์เพิ่มความสะดวกมากกว่าสิ่งจำเป็น
  • หากยกเลิกการสมัคร Apple Music การซิงก์ข้ามอุปกรณ์ที่อิงกับ iCloud Music Library จะหยุดลง
    • ฟีเจอร์นี้อยู่หลังการสมัครสมาชิก
    • หากใช้ iTunes Match จะกลับมาใช้ได้ในราคา $24.99 ต่อปี
    • Match เก็บสำเนา AAC 256kbps ไว้ออนไลน์ และไฟล์ต้นฉบับจะยังคงอยู่เหมือนเดิมตราบใดที่ผู้ใช้ไม่เปลี่ยนเอง
    • หากไม่มีการสมัครสมาชิก การซิงก์ผ่านคลาวด์จะหายไป และต้องกลับไปซิงก์ผ่านสายเคเบิล/Wi‑Fi
  • เป้าหมายคือการสร้างเครื่องเล่นเพลงพื้นฐานด้วยตัวเองที่นำเข้า จัดระเบียบ และเล่นไฟล์เสียงได้
  • แอปที่เสร็จแล้วมี การค้นหาข้อความแบบเต็ม, รองรับ iCloud, ให้ประสบการณ์แบบ local-first และเผยแพร่บน GitHub

ทำไมตัวเลือกเดิมจึงไม่เหมาะ

  • แอปพื้นฐานของ Apple

    • แอป Files สามารถเล่นไฟล์เพลงใน iCloud ได้โดยตรง แต่ไม่ได้ออกแบบมาเป็นแอปสำหรับฟังเพลง
    • ขาดฟีเจอร์หลักอย่าง การจัดการเพลย์ลิสต์, การเรียงตามเมทาดาทา และคิวเล่นเพลง
    • แม้จะเล่นเพลงได้ แต่ประสบการณ์ผู้ใช้โดยรวมมีข้อจำกัด และมีการพูดคุยเรื่องนี้ในชุมชน Apple เช่นกัน
  • แอปภายนอก

    • ใน App Store มีแอปเล่นเพลงออฟไลน์จำนวนมาก แต่หลายแอปใช้ ราคาแบบสมัครสมาชิก แม้กับแอปที่เล่นไฟล์ที่ผู้ใช้เป็นเจ้าของอยู่แล้ว
    • Doppler มีข้อดีตรงที่เป็นโมเดลจ่ายครั้งเดียว
    • แต่ UX เน้นการจัดการอัลบั้ม คุณภาพการค้นหาไม่เป็นที่พอใจ และการนำเข้าจาก iCloud ที่มีโฟลเดอร์ซ้อนกันจำนวนมากช้าและใช้งานยาก

กระบวนการเปลี่ยนจาก React Native เป็น SwiftUI

  • React Native/Expo ที่เลือกในตอนแรก

    • เพราะเคยมีประสบการณ์กับ Swift ที่ไม่ดีนัก จึงเลี่ยง Swift และเลือก React Native/Expo ก่อนเพื่อใช้ประสบการณ์พัฒนาเว็บซ้ำได้
    • การทำ UI เล่นเพลงทำได้ง่าย และใช้ เทมเพลต music-player ของ Gionatha Sturba ซึ่งดูเหมือนมีฟีเจอร์ที่ต้องการ
    • ปัญหาเริ่มใหญ่ขึ้นที่การเข้าถึงระบบไฟล์และการซิงก์ไฟล์บนคลาวด์
    • ไลบรารีอย่าง expo-filesystem รองรับการเลือกไฟล์พื้นฐาน แต่การไล่ดูไดเรกทอรี iCloud ที่ซ้อนลึกแบบ recursive มักล้มเหลวหรือทำให้แอปแครช
    • sandbox ของ iOS ไม่อนุญาตให้แอปอ่านไฟล์ได้หากไม่มีสิทธิ์จากผู้ใช้อย่างชัดเจน
    • สุดท้ายแนวทางที่ใช้ JavaScript กลับซับซ้อนกว่าการใช้ API เนทีฟของ Apple โดยตรง และเมื่อย้ายไป Swift ก็สามารถควบคุมการเข้าถึงไฟล์ iCloud และสิทธิ์ sandbox ได้ดีกว่า
  • เหตุผลที่เลือก SwiftUI

    • เลือก SwiftUI แทน UIKit หรือ storyboard เพื่อสร้างเลเยอร์ UI แบบ declarative และโฟกัสกับโดเมนลอจิกและการซิงก์ข้อมูล
    • การผสาน async/await และ Swift Actors ของ Swift ทำให้การจัดการ data flow และ concurrency ง่ายขึ้น
    • SwiftUI ช่วยจัดโครงสร้างแอปเป็นคอมโพเนนต์ ViewModel ที่เป็นอิสระต่อกัน
    • LLM อย่าง OpenAI o1 และ DeepSeek สร้างโค้ด UI ล้วน ๆ หรือโค้ด data binding โดยทำให้เกิดการพึ่งพากันซับซ้อนน้อยกว่า

โครงสร้างแอปและโมเดลข้อมูล

  • หน้าจอหลักสามส่วน

    • แอปแบ่งเป็นสาม flow คือ Library import, Library management, และ Player and playback
    • Library import เพิ่มโฟลเดอร์ไลบรารี iCloud สแกนไฟล์เสียงในทุกโฟลเดอร์ และใส่ path ลงในฐานข้อมูล SQLite
    • Library management ใช้จัดการเพลงที่เพิ่มเข้ามาและสร้างเพลย์ลิสต์
    • Player and playback รับผิดชอบการเล่นซ้ำ สุ่มเพลง จัดการคิว เล่น/หยุด/เพลงถัดไป
    • ตัวเลือกไฟล์พื้นฐานของ Apple ไม่เหมาะกับ flow ที่ต้องค้นหาหลายไดเรกทอรีด้วยคีย์เวิร์ดและเลือกหลายไฟล์พร้อมกัน
    • เมื่อเปิดแอปด้วยไลบรารีว่าง แท็บ Sync จะแสดงปุ่ม “Add iCloud Source”
    • เมื่อเลือกโฟลเดอร์ จะแสดงแถบความคืบหน้าระหว่างไล่ดู tree และเมื่อทำดัชนีเสร็จจะย้ายไปแท็บ Library
    • หน้าจอแรกแสดง Playlists / Artists / Albums / Songs
    • เมื่อแตะ track จะมี Mini-Player ปรากฏด้านล่าง และแตะตรงนั้นเพื่อไปยัง Player แบบเต็มจอ
    • Player แบบเต็มจอมี shuffle, repeat, จัดลำดับคิวใหม่ และปรับระดับเสียง
    • เมื่อกด “+” ใน Sync เพื่อเลือกโฟลเดอร์อื่น เพลงใหม่จะถูก merge ในเบื้องหลังโดยไม่ต้องรีสตาร์ต
  • เลเยอร์ลอจิกแบบเซิร์ฟเวอร์

    • แม้เป็นแอปมือถือ แต่ใช้ สถาปัตยกรรมแบบแบ็กเอนด์ จากประสบการณ์ด้านเว็บและคลาวด์แบ็กเอนด์
    • แยกเลเยอร์โดเมน/ลอจิกออกจากเลเยอร์ View และ ViewModel เพื่อจัดการการซิงก์คลาวด์ การ parse เมทาดาทา และการเข้าถึงข้อมูล SQLite ได้อย่างเสถียร
    • โครงสร้างเลเยอร์เป็นดังนี้
      • SQLite เก็บแถวเพลงต้นฉบับและดัชนี FTS
      • Repository ครอบฐานข้อมูลและเปิด API แบบ async
      • Domain Actor ถือ business rule อย่าง import, search, queue logic
      • ViewModel subscribe Actor และแปลงเป็น struct สำหรับ UI
      • SwiftUI View render ข้อมูลที่ได้รับมา
    • การเปลี่ยนสถานะถูกทำให้ thread-safe และ iCloud sync, การเล่นเพลง, UI ถูกแยกไม่ให้ข้ามเลเยอร์โดยตรง

การค้นหาในเครื่องด้วย SQLite FTS5

  • ใช้ SQLite เป็นที่เก็บถาวร และไม่ได้ใช้ CoreData
  • เหตุผลที่เลี่ยง CoreData คือจำเป็นต้องควบคุม schema, raw query และโดยเฉพาะ การค้นหาข้อความแบบเต็ม เอง
  • ตั้งแต่ราว iOS 11 เป็นต้นมา สามารถใช้ SQLite ที่มีฟีเจอร์ FTS ได้โดยไม่ต้องตั้งค่าเพิ่ม
  • ใช้ SQLite.swift สำหรับ query ทั่วไป แต่ FTS query จัดการด้วยคำสั่ง SQL ปกติ
  • SQLite FTS5 ช่วยให้ query ชื่อไฟล์และเมทาดาทาอย่าง artist, album, title ได้โดยไม่ต้องมีโครงสร้างพื้นฐานค้นหาแยกต่างหาก
  • โครงสร้างตาราง FTS

    • ใช้ตาราง FTS5 สองตาราง
    • songs_fts: ใช้โดย SQLiteSongRepository และทำดัชนี artist, title, album, albumArtist
    • source_paths_fts: ใช้โดย SQLiteSourcePathSearchRepository และทำดัชนี fullPath, fileName
    • ตาราง FTS ทั้งสองอยู่ข้างตาราง B-tree พื้นฐานอย่าง songs, source_paths
    • FTS เป็น อ่านอย่างเดียว ใน UI และการเขียนทั้งหมดจัดการภายใน Repository
    • ตัวอย่างการสร้าง songs_fts ตั้ง songId เป็น UNINDEXED และใช้ tokenizer unicode61
    • เลือก unicode61 เพื่อรองรับอักขระหลากหลาย
    • คีย์ที่ไม่ค้นหาจะกำหนดเป็น UNINDEXED เพื่อไม่ให้พจนานุกรมคำขยายใหญ่โดยไม่จำเป็น
  • การค้นหาบางส่วนและการจัดอันดับ

    • เมื่อผู้ใช้พิมพ์ “lumine” ภายในจะเติม wildcard เป็น “lumine*” เพื่อค้นหาผลลัพธ์ partial search ได้ทันที
    • การเรียงผลการค้นหาใช้ bm25 ของ SQLite
    • วิธีนี้ให้ schema ที่คาดเดาได้ แนวทาง local-first และการค้นหาข้อความแบบเต็มที่ทรงพลัง โดยไม่เพิ่มการพึ่งพาเครือข่ายหรือบริการภายนอก

การเข้าถึงไฟล์ iOS และข้อจำกัดของ bookmark

  • แอป iOS สามารถเก็บ bookmark แบบถาวรเพื่อจำตำแหน่งไฟล์ได้
  • แต่ security-scoped bookmarks ที่ให้สิทธิ์ขยายในการเข้าถึงไฟล์นอก sandbox ของแอปมีให้เฉพาะบน macOS
  • แอป iOS สามารถใช้ bookmark ปกติเพื่อจำ path ของไฟล์และขอสิทธิ์เข้าถึงอีกครั้งผ่าน document picker ได้ แต่ไม่มีการรับประกันว่าสิทธิ์นั้นจะคงอยู่เงียบ ๆ ต่อไป
  • รายละเอียดที่เกี่ยวข้องอยู่ใน เอกสาร bookmark ของ Apple
  • เพื่อบรรเทาปัญหานี้ จึงทำกลไกสำรองที่คัดลอกไฟล์เข้า sandbox container ของแอปเอง
    • คัดลอกไฟล์ล่วงหน้าในเบื้องหลังขณะที่ bookmark ยังใช้ได้
    • ลดความเสี่ยงในการเข้าถึง reference ไฟล์เสียงที่ใช้ไม่ได้เมื่อ iOS รีเซ็ตสิทธิ์
  • แนวทางนี้ยังช่วยเพิ่มความเร็วการทำดัชนีด้วย
    • สแกนโครงสร้างโฟลเดอร์หนึ่งครั้งขณะที่การเข้าถึงยัง active
    • นำเข้าเฉพาะไฟล์เสียงที่เกี่ยวข้อง
    • ไล่ดูไดเรกทอรีที่ซ้อนลึกได้อย่างปลอดภัย
  • ปัญหาการเล่นไฟล์เสียงแต่ละไฟล์จากตำแหน่งภายนอกให้เสถียรหลังรีสตาร์ตอุปกรณ์ยังไม่ได้รับการแก้ไข
  • แม้ในแอปเนทีฟ use case นี้ก็ยังไม่ได้รับการรองรับเพียงพอบน iOS และการจัดการการเข้าถึงไฟล์ให้เสถียรยังซับซ้อนอยู่

การเล่นเพลงและการทำ UI

  • การ parse เมทาดาทา

    • ใช้ AVURLAsset จากเฟรมเวิร์ก AVFoundation ของ Apple เพื่อ parse เมทาดาทาไฟล์เสียง
    • AVURLAsset สามารถตรวจสอบเมทาดาทาไฟล์สื่ออย่าง title, album artist ได้
    • บาง field อย่าง track number ต้องค้นหาโดยตรงจาก tag ID3
    • เอกสารทางการไม่ได้ครอบคลุม edge case เพียงพอ จึงอ้างอิงตัวอย่างการ implement จากการค้นหาใน GitHub
  • การเล่นเสียง

    • หลังทำดัชนีไลบรารีแล้ว ได้ implement เครื่องเล่นเสียงโดยเริ่มต้น instance ของ AVAudioPlayer แล้วเล่น
    • หากต้องการควบคุมเพลงจาก Control Center ต้อง implement protocol AVAudioPlayerDelegate และเชื่อมกับ MPRemoteCommandCenter ของ Apple
    • MPRemoteCommandCenter ช่วยให้แอปตอบสนองต่อการควบคุมการเล่นในระดับระบบได้

ประสบการณ์พัฒนา Swift และ Apple

  • จุดที่ยาก

    • live SwiftUI preview ของ Xcode ถือเป็นความก้าวหน้า แต่ประสบการณ์พัฒนาโดยรวมยังไม่ถึงระดับการผสานกับ VSCode, การ reload simulator แบบสด, และเครื่องมือ debug ที่คุ้นเคย ซึ่ง Flutter มีให้ตั้งแต่ 5 ปีก่อน
    • การรองรับ LSP ของ Swift สำหรับ Neovim หรือ VSCode ต้องใช้เครื่องมือเพิ่มเติมอย่าง xcode-build-server และยังไม่เหมือนประสบการณ์พัฒนาใน ecosystem ที่เน้นเว็บ
    • บางส่วนของ Apple SDK ยังอยู่ในสไตล์ Objective-C
    • การค้นหาไฟล์ของ Spotlight เปิดให้ใช้ผ่าน NSMetadataQuery เท่านั้น และ NSMetadataQuery ใช้ KVO กับ key แบบ string
    • ยังไม่มี wrapper ที่เป็นมิตรกับ Swift
    • UI แบบ declarative ของ SwiftUI มีประโยชน์ แต่พฤติกรรมทั้งแอปที่เกี่ยวข้องกับสิทธิ์ iCloud ไม่สามารถจำลองใน preview ได้ จึงต้อง mock การโต้ตอบกับคลาวด์เอง
  • จุดที่ดี

    • async/await ทำให้เขียนโค้ด concurrency ที่เน้น I/O ได้เหมือนโค้ด imperative โดยไม่ต้องใช้ callback
    • ใส่โค้ด synchronous ไว้ใน Actor ได้ง่าย และเรียกใช้ได้เหมือน ecosystem ของ JavaScript
    • ใช้ไลบรารีเนทีฟของ Apple ได้โดยตรง โดยไม่ถูกจำกัดด้วย binding โอเพนซอร์สของ React Native หรือ Flutter
    • Apple API จำนวนมากมีตัวอย่าง ทำให้เริ่มต้นได้ง่าย
    • แนวทางจัด UI แบบ React ของ SwiftUI ให้ทั้ง productivity และพื้นที่สำหรับสำรวจ

iOS ที่ทำให้ใช้แอปส่วนตัวระยะยาวได้ยาก

  • หลังจาก implement ประมาณ 1.5 สัปดาห์ ก็ได้ เครื่องเล่นเพลงแบบ local/offline ที่นำเข้าไฟล์เสียงจาก iCloud storage ได้
  • แต่หากไม่มีใบรับรองนักพัฒนา แอปจะ รันได้เพียง 7 วัน จากนั้นต้อง build ใหม่
  • การสมัคร Apple Developer Program ต้องจ่าย $99
  • แม้หลัง DMA Act ของ EU การ sideloading ก็ยังไม่ได้เปิดอย่างสมบูรณ์
    • ผู้ใช้ EU สามารถติดตั้งแอป marketplace ภายนอกจากเว็บไซต์นักพัฒนาได้
    • นักพัฒนายังคงต้องสมัครโปรแกรมของ Apple ปีละ $99
    • ต้องยอมรับ Alternative Terms ของ Apple ด้วย
    • สำหรับการใช้งานส่วนตัว/งานอดิเรก ข้อจำกัดบิลด์นักพัฒนา 7 วันยังไม่หายไป
  • PWA บน iOS ก็ยังมีข้อจำกัด
    • แม้หลังอัปเดต iOS 16~18.x แล้ว PWA ยังรันอยู่ใน sandbox ของ Safari
    • ใช้ WebGL2 และ web-push ได้
    • ไม่มี Web Bluetooth, USB, NFC, Background Sync และพื้นที่เก็บข้อมูลที่รับประกันเกินราว 50MB
    • WebGL ผ่าน Metal shim ทำให้เฟรมเรตจริงมักตามหลังแอป Metal แบบเนทีฟ
  • AI ช่วยให้เข้าถึงความรู้ที่จำเป็นในการจัดการเทคโนโลยีที่ไม่คุ้นเคยได้ง่ายขึ้น และลดความซับซ้อนของการพัฒนาซอฟต์แวร์ แต่แอป iOS ยังต้องทำตามกฎที่ถูกตั้งขึ้นอย่างไม่เป็นธรรมชาติ
  • แม้จะเป็นแอปที่บุคคลสร้างเอง หากต้องการรันนานกว่าหนึ่งสัปดาห์ก็ยังต้องผ่านด่านสุดท้ายของ Apple ซึ่งจำกัดการพัฒนาและแจกจ่ายแอปส่วนบุคคล

1 ความคิดเห็น

 
GN⁺ 2025-05-24
ความคิดเห็นบน Hacker News
  • ผมผ่านยุคที่ Winamp เป็นเครื่องเล่นเพลงหลักมาแล้ว และแม้จะเข้าสู่ยุคสตรีมมิง ก็ยังจัด ไลบรารีเพลงโลคัล ไว้เป็นโฟลเดอร์อยู่
    เหมือนคนอื่น ๆ ในคอมเมนต์นี้ ผมทำเครื่องเล่นเพลงแบบเก่าเป็นโปรเจกต์งานอดิเรกเพื่อฟังเพลงแบบออฟไลน์ เป็นแอป HTML/JS หน้าเดียว ใส่การควบคุมด้วยคีย์บอร์ดครบถ้วนและฟังก์ชันคิวแบบง่าย ๆ
    ลองดูได้ที่: https://nobsutils.com/mp

    • ผ่านมาราว 27 ปีแล้ว ผมก็ยังคิดว่า UI ของ Winamp ใกล้เคียงคำตอบที่ถูกต้องที่สุด
      ผมชอบความเรียบง่ายแบบมีชุดไฟล์ในไดเรกทอรี สุ่มทั้งคอลเลกชัน หรือเล่นเฉพาะไดเรกทอรีหนึ่ง ๆ
    • ผมกำลังหาเครื่องเล่นเพลงเรียบง่ายแบบนี้อยู่พอดี เลยชอบมาก
      อนึ่ง คีย์ลัดบุ๊กมาร์กมาตรฐานปัจจุบันอย่าง cmd + d กลับเปลี่ยนธีมแทนที่จะบุ๊กมาร์กเว็บไซต์
    • สำหรับผม เครื่องเล่นเพลงหลักคือ foobar2000 มาตลอด และตอนนี้ใช้แอป Cog แทนอยู่
  • ผมสร้างคอลเลกชันเพลงใน ฟอร์แมต FLAC มาตลอด 25 ปี และปีที่แล้วซื้อโทรศัพท์ Android กับการ์ด MicroSD 1TB จนสามารถใส่เพลงทั้งหมดลงไปได้
    เป็นโปรเจกต์ยาวนานกว่าที่เทคโนโลยีจะไล่ทัน แต่ตอนนี้พกทั้งหมดไว้ในกระเป๋าได้แล้ว เลยค่อนข้างพอใจ
    ผมคงไม่ใช่คนเดียวที่ไม่อยากกลายเป็นผู้เช่าที่ยอมสละการควบคุม โดยสตรีมสิ่งที่อุตสาหกรรมผลักมาให้และทนดูโฆษณา และการได้เห็นคนที่ถึงขั้นสร้างแอปเองก็เจ๋งดี

    • จริง ๆ เทคโนโลยีไล่ทันมานานแล้ว และนี่ออกจะเป็นการยึดติดกับฟอร์แมตที่ไม่เหมาะกับวัตถุประสงค์มากกว่า
      ถ้ารีเอนโค้ดอย่างดี คุณจะใส่เพลงทั้งหมดลงในการ์ดที่เล็กกว่ามากได้ด้วย คุณภาพเสียงที่โปร่งใส จนฟังไม่ออกว่าต่าง และเก็บ FLAC ไว้บนเดสก์ท็อปเป็นแบ็กอัพก็พอ
    • คุณคัดเลือกคอลเลกชันได้ดีจริง ๆ
      คอลเลกชันของผมมี FLAC/APE/ALAC/WavePack แค่ประมาณ 25% แต่ก็เกิน 3TB แล้ว เลยทำให้ฟังเพลงระหว่างเดินทางได้ยาก
      ผมเลือกไว้ล่วงหน้าไม่ได้ว่าจะใส่อะไรลงอุปกรณ์มือถือ
    • ผมก็ทำคอลเลกชันส่วนตัวให้เป็น FLAC ล้วน อยู่ แม้จะสั้นกว่า 25 ปีมาก แต่ตอนนี้ก็เกิน 1TB ไปแล้ว
      ชุดที่ใช้แล้วดีมากคือเซิร์ฟเวอร์ https://www.navidrome.org กับไคลเอนต์ https://symfonium.app
      ตอนนี้มีการ์ด SD 2TB แล้วด้วย ถ้าราคาลงก็คงซื้อสักอัน
    • บน Android ผมเจอปัญหา ภาพปกกับชื่อเพลง ไม่นิ่งอยู่เสมอ
      เปลี่ยนแล้วไม่ยอมติด หรือเปลี่ยนติดแล้วจู่ ๆ ก็กลายเป็นภาพปกผิด ๆ ซึ่งดูเหมือนบั๊กของ Android
      อยากรู้ว่าคุณเคยเจอปัญหาแบบนี้ไหม
  • ผมทำ เว็บแอป ขึ้นเองเพื่อให้ฟังทั้งอัลบั้มได้ แต่พักกลางทางหรือเปลี่ยนอุปกรณ์ได้
    ผมชอบฟังอัลบั้มตั้งแต่ต้นจนจบ แต่อย่างน้อย YouTube Music ก็ไม่จำตำแหน่งที่เล่นไว้ และต้องเปิดอัลบั้มบนอุปกรณ์อื่นอีกครั้งแล้วหาจุดที่หยุดไว้เอง ซึ่งไม่สะดวก
    เว็บแอปของผมให้วาง URL ได้ แล้วเซิร์ฟเวอร์จะดาวน์โหลดด้วย yt-dlp และสตรีมจากตรงนั้นได้ พร้อมจำตำแหน่งเล่นไว้เสมอ ทำให้ฟังในรถด้วยโทรศัพท์ แล้วไปต่อที่แล็ปท็อปตอนอยู่ที่ทำงานได้
    ยังเหมาะเวลาเพิ่มมิกซ์จากแหล่งอื่นอย่าง NTS Radio ด้วย

    • สิ่งที่น่าหงุดหงิดที่สุดใน YouTube Music สำหรับผมก็คือ คิวที่บันทึกได้ กับการสลับอุปกรณ์ที่ลื่นไหลกว่านี้
      ถ้าเว็บแอปนั้นเปิดเผยอยู่ ผมอยากลองดูสักครั้ง
  • ยังอ่านไม่จบทั้งหมด แต่เป็นบทความที่ดี และผมชอบอ่านว่านักพัฒนาตัดสินใจรายละเอียดต่าง ๆ อย่างไรและทำไมถึงทำแบบนั้น
    ผมเห็นด้วยกับความคิดที่ว่าเครื่องเล่นเสียงที่ลองใช้มายังไม่ถูกใจ
    ในโลกของแอปเพลง ดูเหมือนเลย์เอาต์หน้าจอกับ UI จะกลายเป็นมาตรฐานแทบเหมือนกันหมด แต่ผมไม่ชอบและก็ไม่ค่อยเข้าใจด้วย
    รู้สึกเหมือนต้องต่อยกับแอปเพลงทุกตัว เลยดีใจที่มีคนพยายามสร้างอะไรใหม่ ๆ

  • ผมยังใส่ ไฟล์โลคัล ของตัวเองไว้ในแอป Apple Music ใช้อยู่
    ปิดบริการสตรีมมิง Apple Music แล้วนำทั้งหมดเข้าแอป Apple Music บน macOS จากนั้นเสียบโทรศัพท์กับแล็ปท็อปแล้วซิงก์เหมือน iPod ราวกับเป็นปี 2007
    ทุกอย่างทำงานตามที่คาดไว้ และเพลงไม่ได้เปลี่ยนบ่อย การซิงก์จึงไม่ใช่ปัญหา
    เวลาซิงก์ผ่านสายยังให้ความรู้สึกคิดถึงอดีตแบบแปลก ๆ ด้วย

    • เท่าที่ผมรู้ การซิงก์ Wi‑Fi อัตโนมัติ ด้วย iTunes ก็ยังทำงานได้ดีอยู่
  • ผมเห็นด้วยยากกับคำกล่าวที่ว่าในยุคที่ยังไม่มี async/await การเขียนโค้ด concurrency ใน Swift รู้สึกเทอะทะและมี boilerplate มากกว่า Go หรือ JS/TS
    การประมวลผลแบบอะซิงโครนัสอาจทำให้เขียนโค้ด concurrency ได้ง่ายขึ้น แต่เมื่อโค้ดเบสใหญ่ขึ้น การให้เหตุผลกับโค้ดอาจไม่เรียบง่ายเท่าเดิม
    ในโค้ดเบสอะซิงโครนัสที่ซับซ้อน การเข้าใจ flow ของโค้ดและ concurrency จะยากขึ้น
    ถ้าเป้าหมายคือการลดต้นทุนการรันโค้ดแบบเธรด ก็มีทางออกอย่าง green thread น้ำหนักเบา และถ้าเป้าหมายคือการลดต้นทุนการบำรุงรักษาโค้ดแบบเธรด ระยะยาวแล้ว async อาจต้องใช้ความพยายามมากกว่า

    • concurrency ที่ดีควรทำให้การทำความเข้าใจและการให้เหตุผลง่ายขึ้นเมื่อโค้ดใหญ่ขึ้น
      แค่การ encapsulate แบบอิง process หรือ service ก็ให้ประโยชน์มากแล้ว
      ผมมองว่านี่ไม่ใช่ความล้มเหลวของ concurrency เอง แต่ใกล้เคียงกับข้อจำกัดของ abstraction แบบ async/await มากกว่า
    • ในกรณีนี้เป้าหมายคือเครื่องเล่นเสียงแบบง่าย ๆ จึงคงไม่เป็นปัญหาใหญ่มากนัก
  • คาดหวังว่าบทความจะพูดถึงไม่ใช่แค่ซอฟต์แวร์สำหรับจัดการและเล่นเพลง แต่รวมถึง อุปกรณ์จริง ด้วย
    เมื่อไม่กี่ปีก่อน ผมพยายามซื้อเครื่องเล่น MP3 ให้ลูกชายวัย 10 ขวบที่ชอบฟังเพลงมาก แต่ยังไม่พร้อมใช้โทรศัพท์ และก็ต้องตกใจกับตัวเลือกที่แย่มาก
    ผมมองว่า Apple สร้างช่องว่างขนาดใหญ่ที่ไม่มีใครเติมได้ไว้ตอนเลิกทำ iPod
    iPod shuffle แบบแท่ง USB เป็นเครื่องเล่น MP3 ที่ดีที่สุดเท่าที่เคยใช้มา เล็ก เสียบได้โดยไม่ต้องมีสายแยก และแบตเตอรี่ก็อึด
    มันไม่มีหน้าจอสำหรับค้นหาเพลง แต่นั่นคือเจตนาอยู่แล้ว แค่ปล่อยให้สุ่มเพลงก็พอ
    น่าเสียดายที่แม้แต่แนวคิดที่ค่อนข้างเรียบง่ายแบบนี้ก็ยังไม่มีใครทำซ้ำได้ดีในตลาดฮาร์ดแวร์
    หลายคนคงบอกว่านี่ไม่ใช่ปัญหาฮาร์ดแวร์ แต่เป็นปัญหาซอฟต์แวร์/DRM ทว่าเรื่องที่ไม่มีอุปกรณ์เล่นเพลงพกพาราคาถูกดี ๆ นั้นน่าเสียดายจริง ๆ

    • ผมคิดว่าความเปลี่ยนแปลงที่แท้จริงตรงนี้ไม่ใช่การหายไปของ iPod
      การมีอยู่ของ Spotify และสมาร์ตโฟน ต่างหากที่ฆ่าเครื่องเล่น MP3 และทั้งสองอย่างแย่งพื้นที่ในตลาดไปมากจนทำให้สิ่งอื่น ๆ หายใจไม่ออก
    • Fiio ทำสินค้าในหมวดนี้อยู่ไม่น้อย
      ตัวอย่างเช่น https://www.fiio.com/cp13 และ https://www.fiio.com/jm21
    • iPod มือสองมีอยู่มาก และรุ่นเก่าที่ใช้ฮาร์ดดิสก์หลายรุ่นก็ดัดแปลงเป็นสตอเรจแฟลชได้ค่อนข้างง่าย จึงอาจเป็นตัวเลือกได้
      ยังเหมาะเป็นโปรเจกต์การเรียนรู้เล็ก ๆ เรื่องอิเล็กทรอนิกส์และการนำอุปกรณ์ “เก่า” กลับมาใช้ใหม่ด้วย
      มีตัวเลือกปรับแต่งรูปลักษณ์ภายนอกเยอะ และยังดัดแปลงลึกกว่านั้นได้ เช่น เพิ่ม Bluetooth หรือ USB‑C
      อย่างที่คนอื่น ๆ พูดกัน ฝั่งซอฟต์แวร์ก็ยังทำงานได้ดีผ่าน iTunes บน Windows และการผสาน Finder/Music บน macOS
    • ผมมองว่าไม่ใช่ปัญหาฮาร์ดแวร์หรือซอฟต์แวร์ แต่ใกล้เคียงกับ ปัญหาด้านอุปสงค์ มากกว่า
      ผู้ผลิตจีนขายอุปกรณ์อย่าง Mini iPhone 16 หรือ Mini S24 ที่หน้าตาดี เล่นเพลงได้ และมีฟังก์ชันสมาร์ตโฟน ในราคา 50~100 ดอลลาร์
      ผู้ปกครองมีแนวโน้มจะซื้ออุปกรณ์ลักษณะนั้นให้ลูก ๆ มากกว่าเครื่องเล่น MP3
      การไม่ให้โทรศัพท์กับลูกชายวัย 14 ปีเป็นแนวทางการเลี้ยงดูที่ไม่ใช่ธรรมเนียมทั่วไป และผมก็เคารพ แต่ดูเหมือนว่าอุปสงค์แบบนั้นไม่ได้มากเกินกว่าซัพพลายที่มีอยู่ตอนนี้
    • Sony ยังทำเครื่องเล่นที่ค่อนข้างดีภายใต้แบรนด์ Walkman อยู่: https://electronics.sony.com/audio/walkman-digital-recorders...
      สำหรับเด็ก 10 ขวบอาจแพงไป แต่บางทีอาจหาเครื่องมือสองใน eBay ได้
  • การใช้ Music.app จัดการเพลงในเครื่องร่วมกับการซิงก์ iPhone ผ่าน Finder ยังเหมาะกับผมอยู่
    แต่ Music.app ดูเหมือนไม่ได้รับการดูแลมาหลายปีแล้วหลังจากการเขียนใหม่ใน Big Sur ที่แย่มาก และยังมีบั๊กน่ารำคาญด้วย
    แม้จะมีข้อบกพร่อง แต่ชุดนี้เป็นโซลูชัน การจัดการไลบรารีเพลง + การซิงก์มือถือ ที่ดีที่สุดเท่าที่ผมเคยเห็น อย่างไรก็ตาม ซอฟต์แวร์ไม่ได้รับการดูแลและไม่ใช่ข้ามแพลตฟอร์ม สุดท้ายผมคงต้องเขียนระบบเอง

    • ปัญหาส่วนหนึ่งของการ “เขียนใหม่” ของ Music อยู่ที่มันเหมือนคัดลอกมาจาก iTunes มากกว่าจะเป็นการเขียนใหม่จริง ๆ
      ยังมี ความประหลาดเฉพาะตัวของ iTunes เหลืออยู่เยอะ เช่น หน้าต่างตั้งค่า/Preferences แบบโมดัลที่เป็นร่องรอยจากยุค OS 9
      หน้าต่างตั้งค่าของ OS X ไม่ควรเป็นโมดัล
      ผมเดาว่าข้างในคงกำลังทำเวอร์ชันที่สร้างใหม่ตั้งแต่ต้น เหมือน Music สำหรับ Windows ที่ใช้ WinUI และทำขึ้นใหม่ทั้งหมด
    • บน Video iPod รุ่นที่ 5 ของผม การซิงก์ด้วย Finder ในตัวยังเสียอยู่มาก
      สิ่งที่น่าหงุดหงิดที่สุดคือการซิงก์พอดแคสต์ สมัยที่ iTunes รับหน้าที่นี้เคยทำงานได้ดี แต่ในการซิงก์ผ่าน Finder ของ Big Sur มีบั๊กที่ทำให้เมื่อไปฟังแทร็กหรือพอดแคสต์อื่นแล้วกลับมา มันจำตำแหน่งที่เล่นค้างไว้ไม่ได้
      ทำให้พอดแคสต์ที่ยาวกว่าสองสามนาทีแทบใช้งานบนอุปกรณ์ไม่ได้
    • จะลองใช้แอปของผมก็ได้: https://www.plastaq.com/minimoon
  • ผู้คนถึงขั้นสร้าง แอปเพลง ของตัวเองจริง ๆ แทนที่จะย้ายไป Android
    สงสัยว่าเป็นเพราะฟองข้อความสีน้ำเงิน หรือเพราะทุกอย่าง “ใช้งานได้เลย” กันแน่ ยกเว้นการเล่นเพลงออฟไลน์

    • คุณจะย้ายไปอยู่อีกประเทศจริง ๆ เพียงเพราะขนมปังที่นั่นอร่อยกว่าหรือ?
      ผมไม่เห็นว่าการอบเองจะมีปัญหาอะไร
    • ผมทำโปรโตไทป์เครื่องเล่นเพลงสำหรับ iOS ไว้
      เพราะแอป VLC ไม่สามารถพาร์สเมทาดาทาของไฟล์ FLAC บนไฟล์เซิร์ฟเวอร์ของผมได้อย่างเสถียร และด้วยข้อจำกัดด้านพื้นที่เก็บข้อมูล ผมก็ไม่สามารถเก็บคอลเลกชันเพลงทั้งหมดไว้บนเครื่องได้
      แอปของผมยังเป็นโปรโตไทป์ในแง่ที่ผมอยากได้ฟีเจอร์มากกว่านี้ แต่ก็มีการเล่นเสียง การเข้าถึงไฟล์ระยะไกล และการพาร์สเมทาดาทา FLAC จนทำงานได้ดีพอที่จะทำให้ผมขาดแรงจูงใจในการเพิ่มฟีเจอร์ต่อ
      เหมือนผู้เขียน ตอนแรกผมอยากใช้ React Native เพราะมีประสบการณ์และมีแอปหลายตัวที่ดูแลอยู่ แต่ผมไม่อยากกำหนดเป้าหมายหรือดีบักบนแพลตฟอร์มอื่น
      ดังนั้นผมจึงลองใช้ SwiftUI และสร้าง สิ่งที่คล้าย hot reload ด้วยเครื่องมือพิเศษ[1]
      มันเป็นแฮ็กที่ค่อนข้างสกปรก ต้องใส่ custom linker flags ใน Xcode แต่ก็ทำงานได้ดีพอที่ผมจะไม่คิดถึง developer experience ของ TypeScript และ React Native
      [1] https://github.com/johnno1962/InjectionIII
    • ผู้คนเขียนโค้ดจริง ๆ เพื่อหลีกเลี่ยง Android
    • foobar2000 อยู่ใน App Store มานานแล้ว
      จริง ๆ แล้วผมว่าค่อนข้างแปลกที่ผู้เขียนหาแอปเพลงฟรีที่ถูกใจไม่ได้
      แต่ถึงอย่างไร การจะสร้างอะไรสักอย่างก็ไม่จำเป็นต้องมีข้ออ้างเสมอไป นี่อาจเป็นแค่เรื่องเล่าเพื่อเริ่มต้นบทความก็ได้
    • ผมใช้ชุดแอป navidrome/substreamer บน iOS อยู่ และทำงานได้ดี
      ยังเก็บเพลงที่เลือกไว้บนอุปกรณ์ได้ด้วย
  • หากมีคอลเลกชันเพลงแบบออฟไลน์ ขอแนะนำ https://play.google.com/store/apps/details?id=in.krosbits.mu... จริง ๆ
    ใช้งานได้ดีไม่มีปัญหา

    • Symfonium ก็ยอดเยี่ยมเช่นกัน
      รองรับ Plex, Jellyfin, WebDAV, SMB และอื่น ๆ ด้วย