QVAC کا استعمال کرتے ہوئے اپنے ہارڈ ویئر پر پرائیویٹ ٹیکسٹ ٹو اسپیچ کیسے چلائیں

جب میں نے QuizRope کو حتمی شکل دی، ایک تعلیمی موبائل ایپ جو LLM کو حقیقی وقت میں ٹیوشن اور ہوم ورک میں مدد کے لیے استعمال کرتی ہے، مجھے معلوم تھا کہ اگلا منطقی مرحلہ آواز تھا۔ اسکرین پر متن پڑھنا بہت اچھا ہے، لیکن جسمانی طور پر ایک AI استاد ہونا کہنا آپ کے سیکھنے کے پورے تجربے کو بدل دیتا ہے۔

قدرتی طور پر، میری پہلی جبلت کلاؤڈ فراہم کرنے والوں کو دیکھنا تھی۔ ElevenLabs جیسی خدمات حیرت انگیز آواز کا معیار پیش کرتی ہیں، لیکن میں نے تیزی سے نمبر چلا لیا۔ API کی قیمتوں کے درمیان، طویل ٹیوشن سیشنز کے لیے ٹوکن کی کھپت، اور صارفین کی بڑی تعداد جس کی ہم توقع کر رہے تھے، ریاضی بہت تیزی سے خراب ہو گئی۔ کسی ایپ کے اندر بولے جانے والے ہر ایک جملے کے لیے ادا شدہ API پر انحصار کرنا آزاد ڈویلپرز کے لیے پائیدار نہیں تھا۔

اگر آپ پوچھ رہے ہیں کہ "آپ نے QuizRope کو کہاں تک استعمال کیا ہے؟”، میں ایماندار ہوں گا: میں نے اس پروجیکٹ کو فوراً ترک کر دیا کیونکہ مجھے TTS فعالیت کے لیے کوئی معقول، سستا حل نہیں مل سکا۔

بہت زیادہ لاگت کے علاوہ، انتظار کا وقت تھا۔ سرور کی طرف سے پرامپٹس پر کارروائی کرنے، آڈیو بنانے، اور اسے موبائل ڈیوائس پر واپس سٹریم کرنے کا انتظار گفتگو کا وہم مکمل طور پر ختم کر دیتا ہے۔ اور، سب سے بری بات، اس کا مطلب یہ تھا کہ طالب علم کے پوچھے گئے ہر سوال کو تھرڈ پارٹی سرور کو بھیجا جائے گا۔

یہ مایوسی ایک ایسا حل تلاش کرنے کے لیے اتپریرک بن گئی جو قابل بھروسہ، آف لائن، اور مکمل طور پر لاگت سے پاک ہو۔

اس آرٹیکل میں، ہم ایک React Native ایپلی کیشن بنائیں گے جو آلے کے اپنے ہارڈ ویئر کا استعمال کرتے ہوئے اعلیٰ معیار کے ٹیکسٹ ٹو اسپیچ (TTS) کو مکمل طور پر آف لائن انجام دیتا ہے۔

اگر آپ نے اپنا ماحول ترتیب نہیں دیا ہے یا مقامی تخمینہ کی بنیادی باتوں پر ریفریشر کی ضرورت ہے، تو میں ہمارا پچھلا مضمون پڑھنے کی تجویز کرتا ہوں: QVAC کا استعمال کرتے ہوئے React Native میں مقامی LLM کو آف لائن کیسے چلایا جائے۔ اس میں پروجیکٹ کی شروعات، پری بلڈ، اور بنیادی ہارڈویئر انحصار شامل ہیں۔

یہ گائیڈ فرض کرتا ہے کہ آپ کے پاس پہلے سے ہی QVAC SDK کنفیگر ہو چکا ہے اور ایک پروجیکٹ فزیکل ڈیوائس پر چلنے کے لیے تیار ہے۔

انڈیکس

شرطیں

اس مضمون سے زیادہ سے زیادہ فائدہ اٹھانے کے لیے، آپ کو جدید ویب اور موبائل ڈیولپمنٹ میں مضبوط بنیاد رکھنی چاہیے۔

  • JavaScript/TypeScript اور React: React کے تصورات اور خاص طور پر ہکس کا علم useState، useEffectاور useRef.

  • مقامی اور ایکسپو پر ردعمل ظاہر کریں۔: ترتیب کے ڈھانچے کی بنیادی تفہیم، جیسے View، ScrollView، TextInput) اور اسٹائل کے قواعد۔

  • غیر مطابقت پذیر جاوا اسکرپٹ اور بائنری بفرز: تجربہ async/awaitوعدوں اور صفوں کی بنیادی ہیرا پھیری Int16Array یا Buffer.

  • ترقی کی تعمیر کا ماحول: مقامی ترقیاتی تالیف کے احکامات پر عمل درآمد کا علم، خاص طور پر npx expo prebuild مقامی iOS اور Android ماڈیولز بنائیں۔

  • جسمانی موبائل آلہ: چونکہ مقامی مشین لرننگ ماڈلز ڈیوائس کے لیے مخصوص ہارڈویئر ایکسلریشن اور مقامی اصلاح کا استعمال کرتے ہیں، اس لیے QVAC SDK سمیلیٹر ماحول کو سپورٹ نہیں کرتا ہے۔ آپ کے پاس ایک فزیکل iOS یا اینڈرائیڈ ٹیسٹ ڈیوائس ہونا چاہیے جس میں ڈیولپر موڈ فعال ہو۔

QVAC کیا ہے؟

مزید مؤثر طریقے سے پیروی کرنے میں آپ کی مدد کرنے کے لیے، آئیے معلوم کریں کہ QVAC کیا ہے اور یہ کیوں موجود ہے۔

QVAC، Tether کے ذریعے تیار کیا گیا، ایک مقامی-پہلا AI SDK ہے جسے کراس پلیٹ فارم، پیئر ٹو پیئر (P2P) ایپلی کیشنز اور سسٹمز بنانے کے لیے ڈیزائن کیا گیا ہے۔

بڑے لینگویج ماڈل (LLM) یا ٹیکسٹ ٹو اسپیچ (TTS) انجنوں کا فائدہ اٹھانے والی بہت سی موبائل ایپلیکیشنز کلاؤڈ ہوسٹڈ APIs (جیسے OpenAI یا ElevenLabs) کو نیٹ ورک کی درخواستوں پر انحصار کرتی ہیں۔ اگرچہ آسان ہے، یہ ماڈل نیٹ ورک کنیکٹیویٹی، بار بار چلنے والی API کے استعمال کی فیس، اور صارف کے ڈیٹا کو فریق ثالث سرورز پر منتقل کرنے پر انحصار متعارف کراتا ہے۔

QVAC AI ماڈلز کو براہ راست کلائنٹ ڈیوائس پر چلا کر ایک متبادل فراہم کرتا ہے۔ یہ مقامی-پہلا فن تعمیر کئی عملی فوائد پیش کرتا ہے:

  • پہلے لوکل چلائیں۔: کلائنٹ ہارڈویئر پر براہ راست اندازہ چلاتا ہے، کسی بیرونی API یا فعال انٹرنیٹ کنکشن کی ضرورت نہیں ہے۔

  • پیئر ٹو پیئر (P2P) سپورٹ: انفرنس ٹاسکس کو مقامی نیٹ ورک پر تقسیم کیا جا سکتا ہے، جس سے مرکزی سرور کے بغیر کام کے بوجھ کو متوازن کرنے میں مدد ملتی ہے۔

  • کراس پلیٹ فارم مطابقت: ایک واحد JavaScript/TypeScript انٹرفیس فراہم کرتا ہے جو مختلف قسم کے ہارڈ ویئر اور رن ٹائم ماحول میں مستقل طور پر کام کرتا ہے۔

  • مربوط خصوصیات: ٹیکسٹ جنریشن، ٹرانسکرپشن، امیج جنریشن، اور اسپیچ سنتھیسز کو ایک ہی پیکج میں ظاہر کرتا ہے۔

آن ڈیوائس انفرنس کے کلیدی تصورات

یہ سمجھنے کے لیے کہ QVAC موبائل آلات پر کیسے چلتا ہے، آپ کو چند اہم تصورات کو ذہن میں رکھنے کی ضرورت ہے۔

  • آن ڈیوائس کا اندازہ: مقامی طور پر ماڈل کیلکولیشن چلائیں۔ ایک انجن پر انحصار کرنے کے بجائے، QVAC کام انجام دے سکتا ہے، جیسے llama.cpp متن کے لیے، whisper.cpp ٹرانسکرپشن یا امیج جنریشن کے لیے کسٹم ڈفیوژن بیک اینڈ)۔ اندرونی طور پر، ان انجنوں کی میموری میپنگ ماڈل کے وزن کو براہ راست ڈیوائس کی RAM میں شمار کرتی ہے اور مقامی GPU ہارڈویئر ایکسلریشن کا استعمال کرتے ہوئے حسابات کو انجام دیتی ہے۔

  • کوانٹائزیشن (GGUF فارمیٹ): ایک ریاضیاتی اصلاح کی تکنیک جو ماڈل کے وزن کو سکیڑتی ہے (مثلاً معیاری 16 بٹ فلوٹنگ پوائنٹ پریزیشن سے 4- یا 8 بٹ انٹیجرز تک)۔ یہ ماڈل کو صارفین کے موبائل ہارڈ ویئر کی میموری کی رکاوٹوں کو اپناتے ہوئے اعلی آؤٹ پٹ کوالٹی برقرار رکھنے کی اجازت دیتا ہے۔

  • کلیدی قدر (KV) کیشے: میموری کا ایک علاقہ جو پچھلے ٹوکنز کی حسابی حالت کو ذخیرہ کرتا ہے تاکہ پورے سیاق و سباق کی ونڈو کو ہر لفظ یا ٹوکن کے ساتھ دوبارہ جانچنے کی ضرورت نہ پڑے۔

QVAC کے ذریعہ تعاون یافتہ فن تعمیرات

کسی بھی کوڈ کو لکھنے سے پہلے، یہ سمجھنا ضروری ہے کہ اصل میں ہڈ کے نیچے کیا ہو رہا ہے۔ آپ کے آلے کو پگھلائے بغیر مقامی عمل کو سنبھالنے کے لیے، QVAC SDK آپٹمائزڈ کمیونٹی مینٹیننس سے منسلک ہوتے ہوئے ہارڈویئر بائنڈنگز اور ماڈل لائف سائیکل کا انتظام کرتا ہے۔ جی جی ایم ایل انفرنس پسدید۔

ایک سائز میں فٹ ہونے والے تمام اپروچ کے بجائے، QVAC SDK تقریر کی ترکیب کے لیے دو الگ اعصابی فن تعمیر کی حمایت کرتا ہے۔ آپ کی درخواست کی ضروریات پر منحصر ہے، چاہے آپ فوری آواز کی نقل چاہتے ہوں یا پہلے سے تربیت یافتہ، انتہائی اعلیٰ مخلص آوازیں، آپ ان میں سے انتخاب کر سکتے ہیں: چیٹر باکس اور سپرسونک.

خصوصیت چیٹر باکس سپرسونک
فن تعمیر ٹرانسفارمر پر مبنی زبان کا ماڈل بازی پر مبنی اویکت شور کو ہٹانا
ماڈل کی ساخت تقسیم کرنا (T3 GGUF + S3Gen ساتھی) سنگل فائل (GGUF)
آواز کا طریقہ زیرو شاٹ آواز کی نقل (WAV دیکھیں) پہلے سے تربیت یافتہ آواز کے انداز
نمونے لینے کی شرح 24,000 Hz 44,100 ہرٹج

1. چیٹر باکس انجن

چیٹر باکس اس پر بنایا گیا ہے: ٹرانسفارمر پر مبنی زبان کا ماڈل فن تعمیر یہ آڈیو جنریشن کو اسی طرح ہینڈل کرتا ہے جس طرح LLM کسی جملے میں اگلے لفظ کی پیشین گوئی کرتا ہے، لیکن اس کے بجائے انفرادی صوتی ٹوکن کی پیش گوئی کرتا ہے۔

یہ فن تعمیر چیٹر باکس کو اس میں ایکسل کرنے کی اجازت دیتا ہے: زیرو شاٹ آواز کی نقل. مکمل طور پر پہلے سے بھنی ہوئی آوازوں پر انحصار کرنے کے بجائے، آپ اپنے انتخاب کو پہنچا سکتے ہیں۔ referenceAudioSrc (کسی کی بات کرنے کی ایک مختصر WAV فائل) متن کے ساتھ۔ ٹرانسڈیوسر حوالہ آڈیو کی صوتی خصوصیات کا تجزیہ کرتا ہے اور ان خصوصیات کی بنیاد پر کلون آواز تیار کرتا ہے۔

2. سپرسونک انجن

Supertonic ایک بالکل مختلف نقطہ نظر لیتا ہے. بازی پر مبنی اویکت شور کو ہٹانا — وہی بنیادی فن تعمیر جو AI امیج جنریٹرز میں استعمال ہوتا ہے جیسے Stable Diffusion، لیکن اس کا اطلاق آڈیو پر ہوتا ہے۔

یہ خالص ڈیجیٹل شور سے شروع ہوتا ہے اور متن کے اشارے کی بنیاد پر اسے 44.1kHz پر ایک اعلی مخلص اسپیچ ویوفارم میں بار بار بہتر کرتا ہے۔ سپرٹونک اسپلٹ ماڈل کے بجائے واحد متحد GGUF فائل استعمال کرتا ہے۔ متحرک آواز کی نقل کے بجائے، ہم انتہائی بہتر، پہلے سے تربیت یافتہ آواز کے انداز استعمال کرتے ہیں، جیسے voice: "F1" یا voice: "M1") براہ راست ماڈل میں سینکا ہوا ہے۔ جب متحرک نقل کی ضرورت نہیں ہوتی ہے تو یہ کرسٹل صاف، اسٹوڈیو کے معیار کی تقریر پیدا کرنے کے لیے انتہائی موثر بناتا ہے۔

اس ٹیوٹوریل کے لیے ہم Supertonic استعمال کریں گے۔ یہ باکس سے باہر شاندار نتائج فراہم کرتا ہے اور متعدد ساتھی فائلوں کو لوڈ کرنے کی پیچیدگی سے بچتا ہے۔

انفرنس پائپ لائن

یہ دیکھنے کے لیے کہ آپ کا کوڈ بیس ان انجنوں کے ساتھ کیسے تعامل کر سکتا ہے، اپنے فون کی میموری میں ایک ورچوئل ریکارڈنگ اسٹوڈیو چلانے کے طور پر مقامی ٹیکسٹ ٹو اسپیچ کے بارے میں سوچیں۔

  1. اداکاروں کی خدمات حاصل کرنا (لوڈنگ ماڈل): کمپریسڈ GGUF فائلوں کو براہ راست اپنے آلے کی RAM یا GPU VRAM پر نقشہ بنائیں۔

  2. پاس اسکرپٹ (ٹیکسٹ ان پٹ): لوڈ شدہ انجن کو سادہ متن منتقل کرتا ہے۔

  3. کارکردگی (تخمینہ): انجن متن پڑھتا ہے اور ریاضی سے آواز کی لہروں کی پیش گوئی کرتا ہے۔ اہم طور پر، AI تیار شدہ آڈیو فائلوں کو برآمد نہیں کرتا ہے۔ اس کے بجائے، یہ خام ڈیجیٹل آواز کی لہروں کو نکالتا ہے جسے PCM نمونے کہتے ہیں۔

  4. آڈیو پیکیجنگ: چونکہ خام نمبر کی فہرست معیاری میڈیا پلیئرز کے ذریعے نہیں چلائی جا سکتی، اس لیے PCM ڈیٹا کو دستی طور پر معیاری WAV ہیڈر میں لپیٹنا چاہیے۔

  5. اسٹوڈیو کو بند کریں (ان لوڈ کریں): اسپیچ سنتھیسز میموری کی گہری ہوتی ہے اور ایک مستقل حالت کو برقرار رکھتی ہے، لہذا وسائل کو خالی کرنے اور سیاق و سباق کو فلش کرنے کے لیے ماڈلز کو RAM سے صاف کیا جاتا ہے۔

ماحول اور انحصار کو ترتیب دیں۔

اس سے پہلے کہ ہم کوڈبیس میں جائیں، اگر آپ کا پروجیکٹ pnpm پیکیج مینیجر کا استعمال کرتا ہے تو ذہن میں رکھنے کے لیے کچھ اہم انحصاری ترتیبات موجود ہیں۔

چونکہ QVAC پلگ ان عبوری بنیادی ہم مرتبہ انحصار پر انحصار کرتا ہے، pnpm جیسے سخت پیکیج مینیجر ان انحصاروں کو پوشیدہ اندر بند کر دیتے ہیں۔ .pnpm ذیلی فولڈرز۔

QVAC مقامی بنڈلر کو یقینی بنانے کے لیے (bare-pack) تعمیراتی وقت اور ورکر پلگ ان کی صحیح تشریح کر سکتا ہے۔ .npmrc پروجیکٹ کی جڑ میں فائلیں:

shamefully-hoist=true

اہم: اس فائل کو بنانے کے بعد، آپ کو انحصار کی ایک نئی انسٹال چلانے کی ضرورت ہوگی (pnpm install)۔ یہ راستے کی فلیٹ ترتیب کو یقینی بناتا ہے۔ node_modules اس بات کو یقینی بنائیں کہ مقامی آپریشنز کے دوران تمام QVAC سے متعلقہ مددگار پیکجز کو درست طریقے سے حل کیا گیا ہے۔ npx expo prebuild تالیف کا مرحلہ۔

آڈیو یوٹیلیٹی پیکیجنگ

چونکہ QVAC ایک خام PCM سرنی کو آؤٹ پٹ کرتا ہے، آپ کو پہلے میموری میں ایک درست WAV فائل بنانا چاہیے اور اسے مقامی آڈیو پلیئر میں چلانے سے پہلے ڈیوائس کے اسٹوریج میں لکھنا چاہیے۔

اس کو حاصل کرنے کے لیے، آئیے اندر ایک یوٹیلیٹی ماڈیول بنائیں: src/lib/utils.ts مطلوبہ WAV ہیڈر بنانے کے لیے، خام آڈیو نمونوں کو بائنری بفر میں تبدیل کریں اور اسے مقامی اسٹوریج میں لکھیں۔

import { Buffer } from "buffer";
import * as FileSystem from "expo-file-system/legacy";

/**
 * Creates a WAV header for 16-bit PCM audio
 */
export function createWavHeader(
  dataLength: number,
  sampleRate: number,
): Buffer {
  const buffer = Buffer.alloc(44);
  const channels = 1; // Mono
  const byteRate = sampleRate * channels * 2; // 16-bit audio
  const blockAlign = channels * 2;

  buffer.write("RIFF", 0);
  buffer.writeUInt32LE(36 + dataLength, 4);
  buffer.write("WAVE", 8);
  buffer.write("fmt ", 12);
  buffer.writeUInt32LE(16, 16); // Subchunk1Size
  buffer.writeUInt16LE(1, 20); // AudioFormat (PCM)
  buffer.writeUInt16LE(channels, 22);
  buffer.writeUInt32LE(sampleRate, 24);
  buffer.writeUInt32LE(byteRate, 28);
  buffer.writeUInt16LE(blockAlign, 32);
  buffer.writeUInt16LE(16, 34); // BitsPerSample
  buffer.write("data", 36);
  buffer.writeUInt32LE(dataLength, 40);

  return buffer;
}

/**
 * Converts the raw Int16Array samples from QVAC to a binary Buffer
 */
export function int16ArrayToBuffer(int16Array: Int16Array): Buffer {
  const buffer = Buffer.alloc(int16Array.length * 2);
  for (let i = 0; i < int16Array.length; i++) {
    buffer.writeInt16LE(int16Array[i] ?? 0, i * 2);
  }
  return buffer;
}

/**
 * Main function to package and save the file to local mobile storage
 */
export async function saveAudioToDevice(
  audioBuffer: Int16Array,
  sampleRate: number,
): Promise {
  try {
    const audioData = int16ArrayToBuffer(audioBuffer);
    const wavHeader = createWavHeader(audioData.length, sampleRate);
    const finalWavBuffer = Buffer.concat([wavHeader, audioData]);
    const base64Data = finalWavBuffer.toString("base64");

    const filename = `tts-speech-${Date.now()}.wav`;
    const fileUri = `\({FileSystem.documentDirectory}\){filename}`;

    await FileSystem.writeAsStringAsync(fileUri, base64Data, {
      encoding: FileSystem.EncodingType.Base64,
    });

    console.log(`✅ File saved locally at: ${fileUri}`);
    return fileUri;
  } catch (error) {
    console.error("❌ Failed to save audio file locally:", error);
    throw error;
  }
}

مکمل نفاذ

آئیے سب کو ساتھ لے کر آئیں۔ ہم صارف کا ان پٹ وصول کرنے، سپرٹونک انجن کے ڈاؤن لوڈ اور لوڈنگ کی صورتحال کو منظم کرنے، چلنے کے قابل مقامی فائلوں میں پیدا ہونے والی خام لہروں کو پیک کرنے، اور ایک انٹرایکٹو ویژول ویوفارم پلیئر پیش کرنے کے لیے ایک انٹرفیس نافذ کریں گے۔

حصہ لینے والی ایپ فائلوں کو تبدیل کرنا src/app/index.tsx مندرجہ ذیل نفاذ کے ساتھ:

import { useState, useEffect } from "react";
import {
  TextInput,
  KeyboardAvoidingView,
  Platform,
  ScrollView,
} from "react-native";
import {
  loadModel,
  unloadModel,
  textToSpeech,
  downloadAsset,
  TTS_EN_SUPERTONIC_Q8_0,
  getModelInfo,
  type ModelProgressUpdate,
} from "@qvac/sdk";
import { saveAudioToDevice } from "@/lib/utils";
import { TtsModelLoader } from "@/components/tts-model-loader";
import { AudioPlayer } from "@/components/audio-player";
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Text } from "@/components/ui/text";

const SUPERTONIC_SAMPLE_RATE = 44100;

// Global reference for our model ID
let globalModelId: string | null = null;

type TtsStatus =
  | { phase: "idle" }
  | { phase: "synthesizing" }
  | { phase: "done"; audioUri: string }
  | { phase: "error"; message: string };

export default function TextToVoiceScreen() {
  const [text, setText] = useState("");
  const [status, setStatus] = useState({ phase: "idle" });

  const [isModelLoaded, setIsModelLoaded] = useState(!!globalModelId);
  const [isDownloading, setIsDownloading] = useState(false);
  const [downloadProgress, setDownloadProgress] = useState(0);

  const isBusy = status.phase === "synthesizing";

  useEffect(() => {
    async function checkAndAutoLoad() {
      if (globalModelId) return;
      try {
        const info = await getModelInfo({ name: TTS_EN_SUPERTONIC_Q8_0.name });
        if (info.isCached) {
          setIsDownloading(true);
          setDownloadProgress(1);

          globalModelId = await loadModel({
            modelSrc: TTS_EN_SUPERTONIC_Q8_0,
            modelConfig: {
              ttsEngine: "supertonic",
              language: "en",
              voice: "F1",
              ttsSpeed: 1.05,
              ttsNumInferenceSteps: 5,
            },
          });

          setIsModelLoaded(true);
          setIsDownloading(false);
        }
      } catch (err: unknown) {
        console.warn("Failed to auto-load cached model on mount:", err);
        setIsDownloading(false);
      }
    }
    checkAndAutoLoad();
  }, []);

  const handleDownloadModel = async () => {
    if (isDownloading || isModelLoaded) return;

    try {
      setIsDownloading(true);
      setDownloadProgress(0);

      await downloadAsset({
        assetSrc: TTS_EN_SUPERTONIC_Q8_0,
        onProgress: (p: ModelProgressUpdate) => {
          setDownloadProgress(p.percentage / 100);
        },
      });

      setDownloadProgress(1);

      globalModelId = await loadModel({
        modelSrc: TTS_EN_SUPERTONIC_Q8_0,
        modelConfig: {
          ttsEngine: "supertonic",
          language: "en",
          voice: "F1",
          ttsSpeed: 1.05,
          ttsNumInferenceSteps: 5,
        },
      });

      setIsModelLoaded(true);
      setIsDownloading(false);
    } catch (err: unknown) {
      console.error("Failed to download or load model:", err);
      setIsDownloading(false);
      setStatus({
        phase: "error",
        message: err instanceof Error ? err.message : String(err),
      });
      setIsModelLoaded(false);
    }
  };

  const handleSubmit = async () => {
    if (!text.trim() || isBusy || !globalModelId) return;

    try {
      setStatus({ phase: "synthesizing" });

      // 1. Unload and reload the model to reset its state and clear the KV cache.
      if (globalModelId) {
        await unloadModel({ modelId: globalModelId });
      }
      globalModelId = await loadModel({
        modelSrc: TTS_EN_SUPERTONIC_Q8_0,
        modelConfig: {
          ttsEngine: "supertonic",
          language: "en",
          voice: "F1",
          ttsSpeed: 1.05,
          ttsNumInferenceSteps: 5,
        },
      });

      // 2. Synthesize text to raw PCM samples
      const result = textToSpeech({
        modelId: globalModelId,
        text: text.trim(),
        inputType: "text",
        stream: false,
      });

      const audioBuffer = await result.buffer;

      // 3. Package and save WAV file using our local util
      const samplesInt16 = new Int16Array(audioBuffer);
      const wavUri = await saveAudioToDevice(
        samplesInt16,
        SUPERTONIC_SAMPLE_RATE,
      );

      // 4. Show player
      setStatus({ phase: "done", audioUri: wavUri });
    } catch (err: unknown) {
      console.error("TTS error:", err);
      const msg = err instanceof Error ? err.message : String(err);
      setStatus({ phase: "error", message: msg });
    }
  };

  const buttonLabel =
    status.phase === "synthesizing" ? "Synthesizing…" : "Synthesize Speech";

  if (!isModelLoaded) {
    return (
      
    );
  }

  return (
    
      
        
          
            
              Text to Voice
            
            
              Type or paste your content to synthesize speech
            
          

          
            

            {status.phase === "error" && (
              
                {status.message}
              
            )}

            {status.phase === "done" && }

            
          
        
      
    
  );
}

کوڈ بیس تجزیہ

آئیے اس پر قریبی نظر ڈالتے ہیں کہ یہ مقامی ٹیکسٹ ٹو اسپیچ عمل درآمد کس طرح بنیادی ماڈل لائف سائیکل کو منظم کرتا ہے اور خام آڈیو صفوں کو ہینڈل کرتا ہے۔

1. مقامی لائف سائیکل مینجمنٹ

تقریر کی ترکیب کے لیے عصبی نیٹ ورک کے وزن کو لوڈ کرنا کمپیوٹیشنل طور پر مہنگا ہے۔ ایک ماڈل کو شروع کرتے وقت، QVAC رن ٹائم کو مقامی ڈسک سے پیرامیٹرز کو پڑھنا چاہیے اور فعال وزن کو ڈیوائس RAM میں کاپی کرنا چاہیے۔

اس کو مؤثر طریقے سے سنبھالنے کے لیے، میں نے جزو کے دائرہ کار سے باہر ایک حوالہ متغیر کا اعلان کیا۔

let globalModelId: string | null = null;

اگر globalModelId چونکہ یہ جزو حالت میں ٹریک کیا جاتا ہے، جب آپ ٹیکسٹ ٹو اسپیچ اسکرین کو چھوڑتے ہیں، تو ریاست صاف ہوجاتی ہے، جس کی وجہ سے آپ کی ایپ غیر ضروری طور پر حوالہ جات کو حذف کردیتی ہے۔ ID کو عالمی سطح پر ذخیرہ کرنے سے آپ ID کو لے آؤٹ سوئچز میں برقرار رکھ سکتے ہیں۔

2. KV کیش فلش: اتاریں اور دوبارہ لوڈ کریں۔

GGML انجن کا استعمال کرتے ہوئے آف لائن جنریشن کے سب سے اہم پہلوؤں میں سے ایک ریاستی انتظام ہے۔

// 1. Unload and reload the model to reset its state and clear the KV cache.
if (globalModelId) {
  await unloadModel({ modelId: globalModelId });
}

globalModelId = await loadModel({ ... });

انتباہ سمعی فریب: کسی ایک TTS ماڈل مثال سے جملے کو دوبارہ ترتیب دیئے بغیر مسلسل ترکیب کرنے سے ماڈل کی کلیدی قدر (KV) کیش بھر جاتا ہے۔ یہ نئے جملوں کو پچھلے جملے کے تسلسل کے طور پر ماننا شروع کر دیتا ہے، جس کے نتیجے میں شدید روبوٹک تحریف، بازگشت، اور تکرار تقریر ہوتی ہے۔

واضح طور پر اس کے ساتھ ماڈل کو حذف کریں: unloadModel فوری طور پر ایک نئی مثال بوٹ کریں۔ loadModelہم ایک صاف اور خالی سیاق و سباق کی ونڈو کو مجبور کرتے ہیں۔ چونکہ ماڈل پہلے ہی ڈاؤن لوڈ اور میموری میپ ہو چکا ہے، اس لیے ماڈل کو براہ راست مقامی فلیش اسٹوریج سے دوبارہ لوڈ کرنا بہت تیز ہے۔ عام طور پر جدید موبائل ہارڈویئر پر ایک سیکنڈ سے بھی کم وقت میں مکمل ہو جاتا ہے، یہ صارف کے ہموار تجربے کو یقینی بناتے ہوئے آرٹفیکٹ سے پاک آڈیو کو یقینی بناتا ہے۔

آپریٹنگ سسٹم اور بلٹ ان موبائل میڈیا ڈیکوڈر خام پلس کوڈ ماڈیولیشن (PCM) آواز کی لہروں کو براہ راست پارس نہیں کرسکتے ہیں۔ ایک خام PCM بفر صرف عددی نقاط کا ایک سلسلہ ہے جو آڈیو لہر کے طول و عرض کی نمائندگی کرتا ہے۔

ہم معیاری 44-بائٹ RIFF/WAVE ہیڈر کا استعمال کرتے ہوئے PCM بفر کو پہلے سے فارمیٹ کرکے اس مسئلے کو حل کرتے ہیں۔

یہ ہیڈر پاسپورٹ کی وضاحت کے طور پر کام کرتا ہے:

  • آڈیو فارمیٹ (1): سگنل غیر کمپریسڈ لکیری PCM۔

  • چینلز کی تعداد (1): مونو آڈیو۔

  • نمونہ کی شرح (44100): سپرٹونک پلے بیک کے لیے گھڑی کی فریکوئنسی درکار ہے۔

  • BitsPerSample(16): 16 بٹ لفظ کی لمبائی (2 بائٹس فی نمونہ)۔

فائل رائیٹس کو بیس 64 انکوڈنگ کے ذریعے بھی پروسیس کیا جاتا ہے، بائنری ڈیٹا کو صاف کیے بغیر React Native کے JavaScript-Native پل کو محفوظ طریقے سے عبور کیا جاتا ہے۔

const base64Data = finalWavBuffer.toString("base64");
await FileSystem.writeAsStringAsync(fileUri, base64Data, {
  encoding: FileSystem.EncodingType.Base64,
});

4. بصری ویوفارم پلیئر

پس منظر میں فوری طور پر چلنے والے پہلے سے طے شدہ ہیڈ لیس مقامی آڈیو پلیئر کو استعمال کرنے کے بجائے مقامی WAV فائل پاتھ کو حسب ضرورت بنائیں اجزاء کی طرف سے طاقت @simform_solutions/react-native-audio-waveform.

یہ ماڈیول نئی تخلیق شدہ WAV فائلوں کا تجزیہ کرتا ہے اور واٹس ایپ سے متاثر ہو کر چیکنا، انٹرایکٹو ویژول ویوفارمز تیار کرتا ہے، جس سے صارفین کو پلے بیک، متحرک رفتار ایڈجسٹمنٹ پر مکمل کنٹرول ملتا ہے۔1x، 1.5x، 2x)، اور تلاش کر رہے ہیں۔ یہ وسیع پیمانے پر UX اصلاحات ہیں جو حتمی نتیجہ کو پریمیم اور نفیس محسوس کرتی ہیں۔

نتیجہ

ٹیکسٹ ٹو اسپیچ کو کلاؤڈ سے آن ڈیوائس ہارڈویئر میں منتقل کرنا موبائل ایپلیکیشن ڈویلپرز کے لیے ایک عملی طریقہ پیش کرتا ہے۔ ماڈل کا اندازہ مقامی طور پر چلانے سے ریموٹ انٹرنیٹ کنیکشنز پر انحصار ختم ہوجاتا ہے، بار بار چلنے والے API کے استعمال کی لاگت ختم ہوجاتی ہے، اور اس بات کو یقینی بناتا ہے کہ صارف کا ٹیکسٹ ان پٹ کبھی بھی فزیکل ڈیوائس کو نہیں چھوڑتا ہے۔

مقامی تقریر کی ترکیب کو مربوط کرنا انٹرایکٹو، تعلیمی، یا بات چیت کی ایپس کے لیے بہت مفید ہو سکتا ہے۔ مثال کے طور پر، آواز کی رہنمائی کے نظام میں آن ڈیوائس TTS کا استعمال ایپلیکیشن کو نجی یا آف لائن ماحول میں کام کرنے کی اجازت دیتا ہے۔ جیسا کہ ایج پروسیسرز ہارڈ ویئر ایکسلریشن کور حاصل کرتے ہیں اور اوپن سورس ماڈلز کوانٹائزیشن ریسرچ کے ذریعے میموری کا سائز کم کرتے ہیں، مقامی-پہلے فن تعمیرات ایسے ڈویلپرز کے لیے ایک طاقتور متبادل پیش کرتے ہیں جو پرائیویسی، آف لائن لچک، اور متوقع لاگت کے ڈھانچے کو ترجیح دیتے ہیں۔

وسائل اور اضافی وسائل

مقامی ٹیکسٹ ٹو اسپیچ انفرنس کے بارے میں مزید جاننے کے لیے، سورس کوڈ کا معائنہ کریں، یا موبائل ایپلیکیشنز کی ایڈوانس کنفیگریشن دریافت کریں، درج ذیل وسائل کو دیکھیں:

اوپر تک سکرول کریں۔