Orux AI / Model notes
Qwen3 TTS: Split Lesson Scripts Without Losing Text
Prepare bilingual lesson scripts for Qwen3 TTS with an offline Python splitter. See 3 actual parts, exact-text checks, form limits and Credits rates.

On this page
Prepare a lesson in reviewable pieces before synthesizing it
A bilingual field lesson can move from an English instruction to a Chinese observation task and back again within a few sentences. Sending the entire document to a speech form is not always the right first step. A course author needs to preserve the wording, keep each submitted piece within the chosen input boundary and make the resulting sequence easy to review. Splitting every fixed number of characters can cut an instruction in half.
This guide delivers an offline Python splitter for preparing text for the current Orux qwen3-tts-flash form, with speech-2.8-hd as a second catalog option. The example produces three ordered text parts, preserves every original character and rejects an oversized segment rather than silently dropping or slicing it. It does not generate audio, assess pronunciation or promise that either model will handle a bilingual lesson naturally. Those are later listening decisions.
The useful boundary is explicit: the checked Orux forms declare a 600-character maximum on their speech-text fields. The tool uses a local cap measured in UTF-16 code units, with a smaller cap of 80 for the demonstration. That is a conservative text-preparation rule for the sample, not a definition of billed characters or a statement about the model family's native context limit. Keep text limits, text representation and billing quantities separate.

Check the two forms before designing the lesson handoff
The qwen3-tts-flash form lists the text field input and a voice selector containing Cherry. The speech-2.8-hd form lists text and a voice selector containing male-qn-qingse. Both text fields declare a maximum length of 600 in the public form configuration checked on November 1. The names are part of these specific form contracts; do not replace one field with the other merely because both tasks produce speech.
These observations do not establish that either form supports every voice advertised elsewhere, advanced pronunciation markup, per-sentence language selection or exact timing. In particular, a voice label is not evidence that a specific English word or Chinese place name will be spoken correctly. Use a short, approved text segment for a later listening check when generation is authorized. Do not present a proposed reading script as a recording that has already passed that check.
For this lesson, keep the text itself as the reviewable source. The output artifact should retain part numbers and exact wording, while the eventual audio manifest can separately associate each part with the approved voice and resulting file. If an instructor changes one sentence, a clear part boundary helps identify what needs to be regenerated without claiming that old and new recordings will join seamlessly.
Define what a sentence boundary means in this small tool
The supplied splitter recognizes Chinese full stop, question mark and exclamation mark, plus English period, question mark and exclamation mark when followed by whitespace or the end of the string. This avoids treating the period inside a simple decimal such as 3.5 as a boundary. It does not attempt to understand every abbreviation, quoted sentence or language-specific convention. A human author still reviews the segmentation.
It builds pieces without trimming spaces or normalizing punctuation, then combines adjacent pieces until the chosen cap would be exceeded. It never breaks an oversized piece just to make the count pass. Instead, it raises an error and asks the author to edit that segment manually. This makes the trade-off visible: exact preservation and simple boundaries take priority over automatically accepting every document.
A blank input is rejected, and the local cap must be an integer from 1 through 600. The demonstration uses 80 so that a short, readable fixture actually exercises the multi-part path. That choice is not a recommended audio duration and is not an assertion that 80 units produces a particular number of seconds. Speaking rate, pauses and actual delivery would need to be measured from recorded output.
Run the complete splitter locally
Save the following as chunk_lesson.py and run python3 chunk_lesson.py. The program uses Python's standard library, requires no API key and makes no network request. Its source is an invented educational script about observing leaves without picking plants. The script is an editorial fixture, not an actual model response, classroom trial or recording of a real instructor.
"""Local text preparation only. No TTS request or billed-character measurement."""
import json
import re
def units(text):
return len(text.encode("utf-16-le")) // 2
def chunks(text, limit=600):
if not isinstance(text, str) or not text.strip():
raise ValueError("A nonempty lesson is required")
if type(limit) is not int or not 1 <= limit <= 600:
raise ValueError("Choose a local cap from 1 to 600 UTF-16 units")
pieces = re.split(r"(?<=[。!?])|(?<=[.!?])(?=\s|$)", text)
result, current = [], ""
for piece in pieces:
if not piece:
continue
if units(piece) > limit:
raise ValueError("One segment exceeds cap; edit it manually")
if current and units(current + piece) > limit:
result.append(current)
current = ""
current += piece
if current:
result.append(current)
assert "".join(result) == text
assert all(0 < units(x) <= limit for x in result)
return result
if __name__ == "__main__":
lesson = ("Welcome to the field lesson. Observe the leaf before naming it. "
"先观察叶子的边缘,再记录它的形状。不要采摘植物。 "
"Write one observation in your notebook. Then compare it with your partner.")
output = chunks(lesson, limit=80)
assert len(chunks("Ready 🌿. Next step.", 12)) == 2
assert units("🌿") == 2 and len("🌿") == 1
for text, limit in [("x" * 81, 80), ("", 80), (lesson, 601)]:
try:
chunks(text, limit)
except ValueError:
pass
else:
raise AssertionError("Invalid lesson accepted")
print(json.dumps([{"part": i+1, "utf16_units": units(t), "text": t}
for i, t in enumerate(output)], ensure_ascii=False, indent=2))The actual local run returned:
[
{
"part": 1,
"utf16_units": 63,
"text": "Welcome to the field lesson. Observe the leaf before naming it."
},
{
"part": 2,
"utf16_units": 65,
"text": " 先观察叶子的边缘,再记录它的形状。不要采摘植物。 Write one observation in your notebook."
},
{
"part": 3,
"utf16_units": 35,
"text": " Then compare it with your partner."
}
]The three text parts contain 63, 65 and 35 UTF-16 units. Their leading spaces are intentional: joining their text fields in order recreates the source exactly. The code verifies both that identity and each part's cap. Do not strip these parts and then claim the same exact-preservation property. If a separate speech application needs whitespace cleanup, treat that as a deliberate transformation with its own checks and approved wording.
The emoji check is also deliberate. The leaf symbol is one Python Unicode code point but two UTF-16 code units. Neither count measures spoken content, an audio file's length or a platform invoice. A visible symbol may not be a suitable speech instruction at all. Keep such representation checks in the preparation tool, and decide separately whether symbols should be expanded into approved words in a real lesson.
Inspect meaningful failures before using a real document
The example rejects an 81-unit segment under an 80-unit cap, an empty lesson and a requested cap above 600. It also verifies that a short emoji-containing example produces two parts under a cap of 12. These tests cover the local contract and a representation edge case. They do not demonstrate the behavior of a remote TTS model, an entire language or every possible document format.
If a sentence exceeds the cap, edit it into two clear instructions at an intentional point. Do not silently cut a safety instruction or discard its final clause. Re-run the tool, compare the joined text with the newly approved source and have the instructor review the resulting order. A count that passes is useful only if the lesson still says what the instructor intended.
The punctuation rule can place an abbreviation at an inconvenient boundary. It can also retain a trailing quote with the next segment, depending on the text. These are reasons to inspect the parts, not to claim the tool understands sentence meaning. For a document with complex quotations or specialist notation, revise the boundary rules or curate the segments manually before treating them as generation-ready.
Compare the billing unit without inventing a script invoice
| Orux alias | Checked unit | Credits per 1,000 billed characters | 3,000 billed characters, hypothetical |
|---|---|---|---|
| qwen3-tts-flash | per_kchar | 2.1669 | 6.5007 |
| speech-2.8-hd | per_kchar | 10.2084 | 30.6252 |
Swipe horizontally to view the full table.
These are ordinary public Orux price rows checked on November 1, 2026. One Credit equals $0.005. Both selected rows specify exact quantity rounding and a zero additional minimum in this snapshot. The last column multiplies a stated hypothetical billed quantity by the rate; it is not a measurement or quote for the three-part fixture. No audio request was made to obtain an actual billed quantity.
Do not multiply a Python string length, UTF-16 length or UTF-8 byte count by the table and call the result a settled charge. The tool's cap exists to prepare text, while final speech usage is a separate metered quantity. Splitting may also change the number of requests and the operational review work. Recheck live pricing and the exact request estimate before authorizing synthesis, especially if the service's terms have changed since this snapshot.
The rate difference alone does not establish voice preference, pronunciation accuracy, audio duration or a quality winner. This article does not independently compare official-provider prices, so it makes no savings claim against them. Choose a candidate according to the task, then listen to an appropriate authorized sample. An inexpensive unusable recording would not be a successful lesson deliverable.
Keep the later listening review separate from text validation
For a real course, keep a manifest with part number, exact source text, requested alias and approved voice. After an actual generation, add the file URL and review status without replacing the source text. Listen for missing words, incorrect names, distracting pauses and changes in delivery between parts. Confirm file duration and order from the actual media rather than estimating them from a character count.
The structured extraction guide addresses preserving missing values instead of inventing data, while the support digest input guide controls which fields leave a local system. Here the contract is different: preserve the lesson's entire approved wording while making its input pieces bounded and inspectable. That distinction is why silently deleting troublesome content is not an acceptable fix for an oversized lesson segment.
Prices checked on 2026-11-01. Sources and current model pages: Public catalog, Public catalog, Python reference, Python reference. qwen3-tts-flash, speech-2.8-hd.
FAQ
Does the 600-character form setting describe the model's native limit?
No. It is the declared maximum of the text field in the two checked Orux forms, not a claim about every API, deployment or native model context. The local tool uses a stated UTF-16-unit cap for preparation. Verify the exact current form and request contract before sending a longer document through another path.
Will the splitter lose spaces or change punctuation?
Not in its defined transformation. It keeps pieces without trimming and asserts that joining their text exactly reproduces the source. The displayed leading spaces are therefore intentional. If you normalize text later, review that as a separate change instead of carrying forward an exact-preservation claim that no longer applies.
Does this prove the two voices read bilingual text correctly?
No. No audio was generated or evaluated. The article checks form fields, local text boundaries and dated price rows. A bilingual recording still needs an authorized listening test for pronunciation, missing words and transitions. A voice name or a successful text-length check cannot establish those audio qualities.
Are 63, 65 and 35 the billed character quantities?
No. They are the UTF-16-unit lengths printed by the local fixture. They are not measured speech usage or an invoice. The price table uses explicitly hypothetical billed quantities so that the arithmetic is clear without claiming a character-count rule that this offline program has not established for a live request.
What should I do when one segment is too long?
Edit it manually at a meaningful point, approve the new source and rerun the checks. The tool rejects that case instead of cutting through an instruction or dropping text. Review abbreviations and quotations too, because its simple punctuation rule is not a linguistic sentence parser and does not understand the lesson's meaning.
Build with these models through one API
One OpenAI-compatible API for chat, image, video and audio models, billed in Credits.
Related articles
Browse all articles →
Qwen3 RAG Cost Calculator: Embeddings and Reranking Candidate Budgets
Calculate Qwen3 embedding and reranking costs for 20, 50 or 100 candidates. Includes current Orux Credits, USD estimates and a tested offline Python calculator.

gpt-6-luna: Build a Support Digest Without Raw Messages
Build a support digest for gpt-6-luna using 4 synthetic tickets. Includes an offline allowlist, observed JSON, rejection tests and dated Orux Credits.

Nano Banana 2.1 API: Price per Image and Python Quickstart
Nano Banana 2.1 API costs 3.6 Credits ($0.018) per 1K image on Orux AI vs $0.0336 direct. See 2K/4K rates, aspect ratios, edit mode and Python code.